Spring Recipes, 3rd Edition


Deinum Rubio Long Mak US $49.99 Shelve in Programming Languages/Java User level: Intermediate www.apress.com SOURCE CODE ONLINE BOOKS FOR PROFESSIONALS BY PROFESSIONALS® Spring Recipes Spring Recipes: A Problem-Solution Approach, Third Edition builds upon the best-selling success of the previous editions and focuses on the latest Spring Framework features for building enterprise Java applications. This book provides code recipes for the following, found in the latest Spring: • Spring fundamentals: Spring IoC container, Spring AOP/ AspectJ, and more • Spring enterprise: Spring Java EE integration, Spring Integration, Spring Batch, Spring Remoting, messaging, transactions, and working with big data and the cloud using Hadoop and MongoDB • Spring web: Spring MVC, other dynamic scripting, integration with the popular Grails Framework (and Groovy), REST/web services, and more This book guides you step-by-step through topics using complete and real-world code examples. When you start a new project, you can consider copying the code and configuration files from this book, and then modifying them for your needs. This can save you a great deal of work over creating a project from scratch! You’ll learn: • How to develop with the core Spring Framework, aspect oriented programming, dependency injection, and inversion of control • How to create Spring web services using Spring REST, SOAP and remoting • How to build a web client using Spring MVC and how to work with other web frameworks • How to use Grails and Groovy • How to integrate Spring with social media and mobile apps • How to work with Spring Data, Batch, NoSQL and big data; then integrating with Hadoop, MongoDB, Redis and more • How to create and manage Spring transactions • How to handle Spring messaging, integration, testing, and caching • How to secure your Spring applications THIRD EDITION RELATED 9781430 259084 54999 ISBN 978-1-4302-5908-4 Covers Spring Framework 4.0 www.it-ebooks.info For your convenience Apress has placed some of the front matter material after the index. Please use the Bookmarks and Contents at a Glance links to access them. www.it-ebooks.info v Contents at a Glance About the Authors ���������������������������������������������������������������������������������������������������������� xxxiii About the Technical Reviewer ���������������������������������������������������������������������������������������� xxxv Acknowledgments �������������������������������������������������������������������������������������������������������� xxxvii Introduction ������������������������������������������������������������������������������������������������������������������� xxxix Chapter 1: Spring Development Tools■■ ��������������������������������������������������������������������������������1 Chapter 2: Spring Core Tasks■■ ������������������������������������������������������������������������������������������47 Chapter 3: Spring Annotation Driven Core Tasks■■ �����������������������������������������������������������135 Chapter 4: Spring @MVC■■ �����������������������������������������������������������������������������������������������217 Chapter 5: Spring REST■■ �������������������������������������������������������������������������������������������������275 Chapter 6: Spring Social■■ ������������������������������������������������������������������������������������������������303 Chapter 7: Spring Security■■ ��������������������������������������������������������������������������������������������331 Chapter 8: Spring Mobile■■ �����������������������������������������������������������������������������������������������385 Chapter 9: Spring with Other Web Frameworks■■ ������������������������������������������������������������401 Chapter 10: Data Access■■ �����������������������������������������������������������������������������������������������419 Chapter 11: Spring Transaction Management■■ ���������������������������������������������������������������475 Chapter 12: Spring Batch■■ ����������������������������������������������������������������������������������������������511 Chapter 13: NoSQL and BigData■■ ������������������������������������������������������������������������������������549 Chapter 14: Spring Java Enterprise Services and Remoting Technologies■■ �������������������591 Chapter 15: Spring Messaging■■ ��������������������������������������������������������������������������������������659 www.it-ebooks.info ■ Contents at a Glance vi Chapter 16: Spring Integration■■ ��������������������������������������������������������������������������������������691 Chapter 17: Spring Testing■■ ��������������������������������������������������������������������������������������������723 Chapter 18: Grails■■ ����������������������������������������������������������������������������������������������������������757 Index ���������������������������������������������������������������������������������������������������������������������������������799 www.it-ebooks.info xxxix Introduction The Spring framework is growing. It has always been about choice. Java EE focused on a few technologies, largely to the detriment of alternative, better solutions. When the Spring framework debuted, few would have agreed that Java EE represented the best-in-breed architectures of the day. Spring debuted to great fanfare, because it sought to simplify Java EE. Each release since marks the introduction of new features designed to both simplify and enable solutions. With version 2.0 and later, the Spring framework started targeting multiple platforms. The framework provided services on top of existing platforms, as always, but was decoupled from the underlying platform wherever possible. Java EE is a still a major reference point, but it’s not the only target. Additionally, the Spring framework runs on different Cloud environments. With the introduction of Java based configuration and more XML schemas, the Spring framework created powerful configuration options. Frameworks built on top of the Spring framework have emerged supporting application integration, batch processing, messaging, and much more. This is the 3rd edition of the superb Spring Recipes and it contains mostly updated frameworks, describing the new features and explaining the different configuration (Java and/or XML) options. Additionally, new projects have been added to the Spring ecosystem like the Spring Data family of products. It was impossible to describe each and every project in the Spring ecosystem, so we had to decide what to keep, what to add, and what to update. This was a hard decision but we think we have included the most important and used content. Who This Book Is For This book is for Java developers who want to simplify their architecture and solve problems outside the scope of the Java EE platform. If you are already using Spring in your projects, the more advanced chapters present discussions of newer technologies that you might not know about already. If you are new to the framework, this book will get you started in no time. This book assumes that you have some familiarity with Java and an IDE of some sort. While it is possible, and indeed useful, to use Java exclusively with client applications, Java’s largest community lives in the enterprise space and that, too, is where you’ll see most of these technologies deliver the most benefit. Thus, some familiarity with basic enterprise programming concepts like the Servlet API is assumed. How This Book Is Structured Chapter 1, “Spring Development Tools”, gives an overview of tools supporting the Spring framework and how to use them. Chapter 2, “Spring core tasks,” gives a general overview of the Spring framework: how to set it up, what it is, and how it’s used. Chapter 3, “Spring Annotation Driven Core Task,” reviews, in addition to Chapter 2 more annotation driven concepts that are still key to fully exploiting the container. Chapter 4, “Spring @MVC,” covers web-based application development using the Spring Web MVC framework. Chapter 5, “Spring REST,” provides an introduction to Spring’s support for RESTful web services. Chapter 6, “Spring Social,” provides an introduction of Spring Social, which lets you integrate easily with social networks. Chapter 7, “Spring Security,” provides an overview of the Spring Security project, to help you better secure your application. Chapter 8, “Spring Mobile,” provides an introduction of Spring Mobile, which lets you integrate Mobile device detection and usage in your application. www.it-ebooks.info ■ Introduction xl Chapter 9, “Integrating Spring with Other Web Frameworks,” introduces the core web-tier support that Spring provides. This provides a base for all technologies that Spring provides in the web tier. Chapter 10, “Data Access,” discusses using Spring to talk to data stores using APIs like JDBC, Hibernate, and JPA. Chapter 11, “Transaction Management in Spring,” introduces the concepts behind Spring’s robust transaction management facilities. Chapter 12, “Spring Batch,” introduces the Spring Batch framework, which provides a way to model solutions traditionally considered the domain of mainframes. Chapter 13, “NoSQL and BigData,” an introduction to multiple Spring Data portfolio projects, covering different NoSQL technologies and BigData with Hadoop. Chapter 14, “Spring Java Enterprise Services and Remoting Technologies,” introduces you to the JMX support, scheduling, e-mail support, and various facilities for RPC, including the Spring Web Services project. Chapter 15, “Spring Messaging,” discusses using Spring with message-oriented middleware through JMS and RabbitMQ and the simplifying Spring abstractions. Chapter 16, “Spring Integration,” discusses using the Spring Integration framework to integrate disparate services and data. Chapter 17, “Spring Testing,” discusses unit testing with the Spring framework. Chapter 18, “Grails,” discusses the Grails framework, with which you can increase your productivity by using best- of-breed pieces and gluing them together with Groovy code. Appendix A, “Deployment to the Cloud,” shows how to deploy a Java (Web) application to the cloud using the Pivotals CloudFoundry solution. Appendix B, “Spring Caching,” introduces the Spring Caching abstraction, including how to configure it and how to transparently add caching to your application. Conventions Sometimes, when we want you to pay particular attention to a part within a code example, we will make the font bold. Please note that the bold doesn’t necessarily reflect a code change from the previous version. In cases when a code line is too long to fit the page’s width, we will break it with a code continuation character. Please note that when you try to type the code, you have to concatenate the line by yourself without any spaces. Prerequisites Because the Java programming language is platform independent, you are free to choose any supported operating system. However, some of the examples in this book use platform-specific paths. Translate them as necessary to your operating system’s format before typing the examples. To make the most of this book, install JDK version 1.7 or higher. You should have a Java IDE installed to make development easier. For this book, the sample code is Gradle based. If you’re running Eclipse and Install the Gradle plug-in, you can open the same code in Eclipse and the CLASSPATH and dependencies will be filled in the by the Gradle metadata. If you’re using Eclipse, you might prefer SpringSource’s SpringSource Tool Suite (STS), as it comes preloaded with the plug-ins you’ll need to be productive with the Spring framework in Eclipse. If you use IntelliJ IDEA, you need to enable the Gradle (and Groovy) plugins. Downloading the code The source code for this book is available from the Apress web site (www.apress.com) in the Source Code / Download section. The source code is organized by chapters, each of which includes one or more independent examples. Contacting the Authors We always welcome your questions and feedback regarding the contents of this book. You can contact Marten Deinum at marten@deinum.biz www.it-ebooks.info 1 Chapter 1 Spring Development Tools In this chapter you’ll learn how to setup and work with the most popular development tools to create Spring applications. Like many other software frameworks, Spring has a wide array of development tools to choose from, from bare-bones command line tools to sophisticated graphical tools the software industry calls IDEs (Integrated Development Environments). Whether you already use certain Java development tools or are a first-time developer, the following recipes will guide you on how to set up different toolboxes to do the exercises in the upcoming chapters, as well as develop any Spring application. Table 1-1 describes a list of toolboxes and the corresponding recipes you need to follow to get the right tools to start a Spring application. Table 1-1.  Toolboxes to develop Spring applications Toolbox A Toolbox B Toolbox C Toolbox D Spring Tool Suite Recipe 1-1 Eclipse IDE Recipe 1-2 IntelliJ IDE Recipe 1-3 w/Maven CLI Recipe 1-4 Text editor w/Maven CLI Recipe 1-4 w/Gradle CLI Recipe 1-5 w/Gradle CLI Recipe 1-5 Bear in mind you don’t need to install every toolbox described in Table 1-1 to work with Spring. It can be helpful to try them all out, so you can use the toolbox you feel most comfortable with. 1-1. Build a Spring application with the Spring Tool Suite Problem You want to use the Spring Tool Suite (STS) to build a Spring application. Solution Install STS on your workstation. Open STS and click the ‘Open Dashboard’ link. To create a new Spring application, click on the ‘Spring project’ link on the dashboard window inside the ‘Create’ table. To open a Spring application that uses Maven, from the top level ‘File’ menu select the ‘Import...’ option, click on the ‘Maven’ icon and select the ‘Existing Maven projects’. Next, select the Spring application based on Maven from your workstation. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 2 To install Gradle on STS, click on the ‘Extensions’ tab at the bottom of the dashboard window. Click on the ‘Gradle Support’ checkbox. Proceed with the Gradle extension installation and restart STS once the installation is complete. To open a Spring application that uses Gradle, from the top level ‘File’ menu select the ‘Import...’ option, click on the ‘Gradle’ icon and select the ‘Gradle project’. Next, select the Spring application based on Gradle from your workstation. Click on the ‘Build Model’ button and last click ‘Finish’ to start working on the project. How It Works STS is the IDE developed by SpringSource -- a division of Pivotal -- creators of the Spring framework. STS is specifically designed to develop Spring applications, making it one of the most complete tools for this purpose. STS is an Eclipse-powered tool, so it has the same ‘look and feel’ as the Eclipse open source IDE. STS can be downloaded for free from http://spring.io/tools/sts. STS is available for all six major operating system (OS) versions: Windows, Windows (64bit), Mac OS X (Cocoa), Mac OS X (Cocoa, 64bit), Linux (GTK), and Linux (GTK, 64bit). STS is also distributed in two versions of Eclipse, the Eclipse 3.x branch and the Eclipse 4.x branch. In addition, STS is itself versioned, so you have the option to download the latest stable release or a milestone/development version. Download the version suited to your OS and I recommend you chose the Eclipse 4.x branch because it’s newer. At the time of this writing the latest stable release of the Spring Tool Suite is the 3.5 version. Therefore the download link you chose should have the title ‘SPRING TOOL SUITE 3.5.1.RELEASE - BASED ON ECLIPSE 4.3.2’ or you can choose a newer release if you prefer. Once you download STS, ensure you have a Java SDK installed on your system since this is an STS installation requirement. Proceed to install STS. Follow the installation wizard and you should have STS setup in 5 to 10 minutes. Upon termination, a folder with the name STS_ is created under the home folder of the user making the installation or where the user chooses to place the installation-based folder. If you inspect this folder, you’ll see the STS executable which is used to start STS. Start STS. At startup, STS asks you to define a workspace location. A workspace is where STS places all project information. You can keep the default directory which is under the main STS installation directory or define a different directory to your liking. After startup is complete you’ll see a screen like the one in Figure 1-1. Figure 1-1.  STS startup screen www.it-ebooks.info Chapter 1 ■ Spring Development Tools 3 Click on the ‘Open Dashboard’ link. Then you’ll see the STS Dashboard illustrated in Figure 1-2. Figure 1-2.  STS Dashboard On the STS Dashboard, in the center column inside the ‘Create’ box there’s a link called ‘Spring project’. You can click on this link to create a new Spring application. You can go ahead and create an empty application if you like. You’ll be asked for a name and to define a series of parameters which you can leave with default values. A more common case than creating a Spring application from scratch is to continue development on a pre-existing Spring application. Under such circumstances, the owner of an application generally distributes the application’s source code with a build script to facilitate its ongoing development. The build script of choice for most Java application is a pom.xml file designed around the build tool called Maven and more recently a build.gradle file designed around the build tool called Gradle. The book’s source code and its applications are provided with Gradle build files, in addition to a single application with a Maven build file. JAVA BUILD TOOLS, A MEANS TO AN END: ANT, MAVEN, IVY, GRADLE In a Java application there can be dozens or hundreds of menial tasks required to put together an application (e.g., Copying JARs or configuration files, setting up Java’s classpath to perform compilation, downloading JAR dependencies, etc.). Java build tools emerged to perform such tasks in Java applications. Java build tools continue to have their place because applications distributed with build files ensure that all menial tasks intended by the creator of an application are replicated exactly by anyone else using the application. If an application is distributed with an Ant build.xml file, a Maven pom.xml file, an Ivy ivy.xml file or a Gradle build. gradle file, each of these build files guarantees build consistency across users and different systems. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 4 Some of the newer Java build tools are more powerful and enhance the way their earlier counterparts work and each build file uses its own syntax to define actions, dependencies, and practically any other task required to build an application. However, you should never lose sight of the fact that a Java build tool is just a means to an end. It’s a choice made by the creator of an application to streamline the build process. Don’t panic if you see an application distributed with a build file from the oldest Ant version or the newest Gradle version, from an end user perspective all you need to do is download and install the build tool to create the application as its creator intended. Since many Spring applications continue to use Maven and some of the newer Spring applications use Gradle, I’ll describe the import process into STS for both types of projects. Once you download the book’s source and unpack it to a local directory, click on the STS top level ‘File’ menu and select the ‘Import...’ option. A pop-up window appears. In the pop-up window, click on the ‘Maven’ icon and select the ‘Existing Maven Projects’ option as illustrated in Figure 1-3. Figure 1-3.  STS maven import www.it-ebooks.info Chapter 1 ■ Spring Development Tools 5 Notice in Figure 1-4 the ‘Projects:’ window is updated to include the line pom.xml com.apress. springrecipes... which reflects the Maven project to import. Select the project checkbox and click on the ‘Finish’ button to import the project. All projects in STS are accessible on the left-hand side in the ‘Package Explorer’ window. In this case, the project appears with the name springintro_mvn. If you click on the project icon in the ‘Package Explorer’ window, you’ll be able to see the project structure (i.e., java classes, dependencies, configuration files, etc.). If you double click on any of the project files inside the ‘Package Explorer’, the file is opened in a separate tab in the center window -- alongside the dashboard. Once a file is opened, you can inspect, edit, or delete its contents. Select the project icon in the ‘Package Explorer’ window and click on the right button of your mouse. A contextual menu appears with various project commands. Select the ‘Run as’ option followed by the ‘Maven build’ option. A pop-up window appears to edit and configure the project build. Just click on the ‘Run’ button in the bottom right. In the bottom center of STS you’ll see the ‘Console’ window appear. In this case, the ‘Console’ window displays a series of build messages produced by maven, as well as any possible errors in case the build process fails. Next, click on the ‘Next’ button. In the following screen, on the ‘Select root directory’ line click on the ‘Browse’ button and select the directory of the book’s source code in Ch1 called springintro_mvn as illustrated in the Figure 1-4. Figure 1-4.  STS select maven project www.it-ebooks.info Chapter 1 ■ Spring Development Tools 6 You’ve just built the application, congratulations! Now let’s run it. Select the project icon from the ‘Package Explorer’ window once again and press the F5 key to refresh the project directory. Expand the project tree. Toward the bottom you’ll see a new directory called target which contains the built application. Expand the target directory by clicking on its icon. Next, select the file springintro_mvn-1.0-SNAPSHOT.jar as illustrated in Figure 1-5. Figure 1-5.  Select executable in STS www.it-ebooks.info Chapter 1 ■ Spring Development Tools 7 Click on the ‘Run’ button in the bottom right. In the bottom center of STS you’ll see the ‘Console’ window. In this case, the ‘Console’ window displays the application logging messages, as well as a greeting message defined by the application. Even though you’ve built and run a Spring application with STS, we’re still not done. The process you just completed with STS was mostly done behind the scenes by the build tool called Maven. Next, it’s time to import a Spring application that uses one of the newer build tools call Gradle. While gradle is still a relatively new tool, there are signs that gradle will supplant Maven in the future. For example, many large Java projects -- such as the Spring framework itself -- now use Gradle instead of maven due to its greater simplicity and power. Given this tendency, it’s a worth describing how to use Gradle with STS. Figure 1-6.  Define main executable class in STS With the file selected, click on the right button of your mouse. A contextual menu appears with various project commands. Select the ‘Run as’ option followed by the ‘Run configurations...’ option. A pop-up window to edit and configure the run appears. Ensure the ‘Java application’ option is selected on the left-hand side. In the ‘Main class:’ box introduce: com.apress.springrecipes.hello.Main. This is the main class for this project, as illustrated in Figure 1-6. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 8 Tip■■ if you have a Maven project (i.e., pom.xml file) you can use the bootstrap plugin or maven2gradle tool to create a Gradle project (i.e., build.gradle file). The bootstrap plugin is included with Gradle (See documentation at http://gradle.org/docs/current/userguide/bootstrap_plugin.html) and the maven2gradle tool is available at https://github.com/jbaruch/maven2gradle.git. To install Gradle in STS go to the dashboard window. At the bottom of the dashboard window click on the ‘Extensions’ tab. A list of STS extensions is loaded in the window. Click on the ‘Gradle Support’ checkbox from the list as illustrated in Figure 1-7. If you don’t see the ‘Gradle Support’ extension in the list, type the word ‘gradle’ in the ‘Find:’ textbox. If you still don’t see the ‘Gradle Support’ extension after this step, click on the ‘Find Updates’ button. Figure 1-7.  Gradle STS installation www.it-ebooks.info Chapter 1 ■ Spring Development Tools 9 Click on the ‘Next’ button in the bottom right to proceed with the Gradle extension installation. A confirmation pop-up window appears as illustrated in Figure 1-8. Figure 1-8.  Gradle STS installation confirmation Click on the pop-up window’s ‘Next’ button. Once you read the license and accept the terms click on the pop-up window’s ‘Finish’ button. The Gradle extension installation process starts. Once the installation process finishes, you’ll be prompted to restart STS for the changes to take effect. Confirm the STS restart to finish the Gradle installation. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 10 The book’s source contains numerous Spring applications designed to be built with Gradle, so I’ll describe how to import these Spring applications into STS. Once you download the book’s source and unpack it to a local directory, in STS click on the top level ‘File’ menu and select the ‘Import...’ option. A pop-up window appears. In the pop-up window, click on the ‘Gradle’ icon and select the ‘Gradle Project’ option as illustrated in Figure 1-9. Figure 1-9.  STS gradle import Next, click on the ‘Next’ button. In the following screen, in the ‘Select root directory’ line click on the ‘Browse’ button and select the book’s source code top level directory. Next, click on the ‘Build model’ button beside the ‘Browse’ button. The build model process retrieves the various Gradle subprojects contained in the book’s source code. A pop-up window appears indicating the progress of the build model process. Once the build model process finishes, you’ll see a list of Gradle projects, click on the project checkbox called ‘springintro’ inside ‘Ch1’ as illustrated in Figure 1-10. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 11 Click on the ‘Finish’ button to import the project. If you look at the left-hand side of STS in the ‘Package Explorer’ window you’ll see the project is loaded with the name springintro. If you click on the project icon, you’ll be able to see the project structure (i.e., java classes, dependencies, configuration files, etc.). Select the project icon and click on the right button of your mouse. A contextual menu appears with various project commands. Select the ‘Run as’ option followed by the ‘Gradle build’ option. A pop-up window appears to edit and configure the build. Click on the Project/task option ‘build’ and then click on the ‘Run’ button in the bottom right. In the bottom center of STS you’ll see the ‘Console’ window appear. In this case, the ‘Console’ window displays a series of build messages produced by gradle, as well as any possible errors in case the build process fails. You’ve just built the application, now let’s run it. Select the project icon once again and press the F5 key to refresh the project directory. Expand the project tree. Toward the middle you’ll see a new directory called libs which contains the built application. Expand the libs directory by clicking on the icon. Next, select the file springintro-1.0-SNAPSHOT.jar. Figure 1-10.  STS select gradle subproject www.it-ebooks.info Chapter 1 ■ Spring Development Tools 12 With the file selected, from the top level menu ‘Run’ select the ‘Run configurations...’ option. A pop-up window appears to edit and configure the run. Ensure the ‘Java application’ option is selected in the left-hand side. In the ‘Main class:’ box introduce com.apress.springrecipes.hello.Main. This is the main class for this project. Click on the ‘Run’ button in the bottom right. In the bottom center of STS you’ll see the ‘Console’ window. In this case, the ‘Console’ window displays the application logging messages, as well as a greeting message defined by the application. 1-2. Build a Spring application with the Eclipse IDE Problem You want to use the Eclipse IDE to build Spring applications. Solution From Eclipse’s top level ‘Help’ menu select the ‘Eclipse Marketplace...’ . Install STS for Eclipse. To create a new Spring application, click on the top level ‘File’ menu select t ‘New’ and then the ‘Project’ . Next, click on the ‘Spring project’ option. To open a Spring application that uses Maven, you first need to install Maven Integration for Eclipse (a.k.a. m2eclipse). From Eclipse’s top level ‘Help’ menu select the ‘Eclipse Marketplace...’. Install Maven Integration for Eclipse. Click on the Eclipse top level ‘File’ menu and select the ‘Import...’ option. A pop-up window appears. In the pop-up window, click on the ‘Maven’ icon and select the ‘Existing Maven Projects’ option. Next, seledct the Spring application based on Maven from your workstation. To open a Spring application that uses Gradle, you first need to install Gradle Integration for Eclipse. From Eclipse’s top level ‘Help’ menu select the ‘Eclipse Marketplace...’. Install Maven Integration for Eclipse. Click on the Eclipse top level ‘File’ menu and select the ‘Import...’ option. A pop-up window appears. In the pop-up window, click on the ‘Gradle’ icon and select the ‘Gradle Project’ option. Next, select the Spring application based on Gradle from your workstation. How It Works Eclipse is one of the most popular IDEs to develop Java applications, therefore it can be a natural choice to develop Spring applications. For this recipe I’ll assume you’ve already installed Eclipse and are familiar with its environment. Tip■■ if you haven’t installed Eclipse, I recommend you try out STS which is described in Recipe 1-1. STS is an Eclipse-powered tool -- meaning it has the same ‘look and feel’ as Eclipse -- but it’s a product made by the creators of the Spring framework -- SpringSource -- so it’s specifically designed to fulfill the needs of Spring application development. Eclipse supports a wide variety of tools that make it easy to work with certain technologies directly in the Eclipse IDE. Some of these tools include support for version control technologies like svn and git, support for platform technologies like Python and Android and as you probably already guessed support for the Spring framework. Eclipse makes all these tools available in what it calls the Eclipse marketplace. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 13 Warning■■ if you don’t see the ‘Eclipse Marketplace...’ option illustrated in Figure 1-11 it means your Eclipse version doesn’t support the Eclipse Marketplace. The Eclipse Marketplace is included in all Eclipse packages available from the Eclipse download page, except the Eclipse classic package. Once you select the ‘Eclipse Marketplace...’ option a pop-up window appears where you can access and search the Eclipse marketplace. Figure 1-11.  Eclipse Marketplace option You can access the Eclipse marketplace at its website (http://marketplace.eclipse.org/) or directly in the Eclipse IDE from the top level ‘Help’ menu selecting the ‘Eclipse Marketplace...’ option as illustrated in Figure 1-11. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 14 Figure 1-12.  STS for Eclipse installation Click on the ‘Install’ button alongside the STS for Eclipse tool to begin the process. Follow the installation wizard. Once you read the license and accept the terms click on the pop-up window’s ‘Finish’ button. The STS for Eclipse tool installation process initiates. Once the installation process finishes, you’ll be prompted to restart Eclipse for the changes to take effect. Confirm the Eclipse restart to finish the STS for Eclipse installation. The Eclipse tool for the Spring framework is called ‘STS for Eclipse’. In the pop-up window you can scroll through the main list or type ‘sts’ in the ‘Find:’ box to limit the main list as illustrated in Figure 1-12. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 15 With the STS for Eclipse tool you gain access to several Spring specific features not available in a regular Eclipse installation. For example, to create a new Spring project from the top level ‘File’ menu select the ‘New’ and then the ‘Project’ option as illustrated in Figure 1-13. Figure 1-13.  Create new Eclipse project www.it-ebooks.info Chapter 1 ■ Spring Development Tools 16 A pop-up window appears to create a new project. If you scroll toward the bottom you’ll see the option to create a Spring project as illustrated in Figure 1-14. You can go ahead and create an empty application if you like, you’ll be asked for a name and to define a series of parameters which you can leave with default values. Figure 1-14.  Create new Spring project in Eclipse A more common case than creating a Spring application from scratch is to continue development of a pre-existing Spring application. Under such circumstances, the owner of an application generally distributes the application’s source code with a build script to facilitate its ongoing development. The build script of choice for most Java application is a pom.xml file designed around the build tool called Maven and more recently a build.gradle file designed around the build tool called Gradle. The book’s source code and its applications are provided with Gradle build files, in addition to a single application with a Maven build file. To access Spring applications distributed with Maven or Gradle build files, it’s necessary to install additional Eclipse tools from the Eclipse Marketplace. Once again you’ll need to use the Eclipse marketplace. From the top level ‘Help’ menu select the ‘Eclipse Marketplace...’ option as illustrated in Figure 1-11. A pop-up window appears where you can access the Eclipse marketplace. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 17 Figure 1-15.  Maven Integration for Eclipse installation In the ‘Find:’ box type ‘maven integration’ and you’ll see the ‘Maven Integration for Eclipse’ in the main list as illustrated in Figure 1-15. Click on the ‘Install’ button and follow the installation wizard. Postpone the Eclipse restart request so you can also install Gradle. Go back to the Eclipse marketplace. In the ‘Find:’ box type ‘gradle’ and you’ll see the ‘Gradle Integration for Eclipse’ in the main list as illustrated in Figure 1-16. Click on the ‘Install’ button and follow the installation wizard. You’ll be prompted to restart Eclipse for the changes to take effect. Confirm the Eclipse restart to finish the Maven Integration for Eclipse and Gradle Integration for Eclipse installations. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 18 Figure 1-16.  Gradle Integration for Eclipse installation www.it-ebooks.info Chapter 1 ■ Spring Development Tools 19 With access to both maven and gradle from Eclipse, let’s access some of the book’s applications. Once you download the book’s source and unpack it to a local directory, click on the Eclipse top level ‘File’ menu and select the ‘Import...’ option. A pop-up window appears. In the pop-up window, click on the ‘Maven’ icon and select the ‘Existing Maven Projects’ option as illustrated in Figure 1-17. Figure 1-17.  Eclipse maven import www.it-ebooks.info Chapter 1 ■ Spring Development Tools 20 Notice in Figure 1-18 the ‘Projects:’ window is updated to include the line pom.xml com.apress. springrecipes... which reflects the Maven project to import. Select the project checkbox and click on the ‘Finish’ button to import the project. All projects in Eclipse are accessible on the left-hand side in the ‘Project Explorer’ window. In this case, the project appears with the name sprintro_mvn. If you click on the project icon in the ‘Project Explorer’ window, you’ll be able to see the project structure (i.e., java classes, dependencies, configuration files, etc.).If you double click on any of the project files inside the ‘Project Explorer’, the file is opened in a separate tab in the center window -- alongside the dashboard. Once a file is opened, you can inspect, edit, or delete its contents. Select the project icon in the ‘Project Explorer’ window and click on the right button of your mouse. A contextual menu appears with various project commands. Select the ‘Run as’ option followed by the ‘Maven build’ option. A pop-up window appears to edit and configure the project build. Just click on the ‘Run’ button in the bottom right. In the bottom center of Eclipse you’ll see the ‘Console’ window. In this case, the ‘Console’ window displays a series of build messages produced by Maven, as well as any possible errors in case the build process fails. Figure 1-18.  Eclipse select maven project Next, click on the ‘Next’ button. In the following screen, on the ‘Select root directory’ line click on the ‘Browse’ button and select the directory Ch1 of the book’s source code and select sprintro_mvn as illustrated in the Figure 1-18. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 21 You’ve just built the application, congratulations! Now let’s run it. Select the project icon from the ‘Project Explorer’ window once again and press the F5 key to refresh the project directory. Expand the project tree. Toward the bottom you’ll see a new directory called target which contains the built application. Expand the target directory by clicking on its icon. Next, select the file sprintro_mvn-1.0-SNAPSHOT.jar as illustrated in Figure 1-19. Figure 1-19.  Select executable in Eclipse www.it-ebooks.info Chapter 1 ■ Spring Development Tools 22 Click on the ‘Run’ button in the bottom right. In the bottom center of Eclipse you’ll see the ‘Console’ window. In this case, the ‘Console’ window displays the application logging messages, as well as a greeting message defined by the application. Figure 1-20.  Define main executable class in Eclipse With the file selected, click on the right button of your mouse. A contextual menu appears with various project commands. Select the ‘Run as’ option followed by the ‘Run configurations...’ option. A pop-up window to edit and configure the run appears. Ensure the ‘Java application’ option is selected on the left-hand side. In the ‘Main class:’ box introduce: com.apress.springrecipes.hello.Main. This is the main class for this project, as illustrated in Figure 1-20. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 23 Next, let’s build a Gradle application with Eclipse. Go to Eclipse’s top level ‘File’ menu and select the ‘Import...’ option. A pop-up window appears. In the pop-up window, click on the ‘Gradle’ icon and select the ‘Gradle Project’ option as illustrated in Figure 1-21. Figure 1-21.  Eclipse Gradle import www.it-ebooks.info Chapter 1 ■ Spring Development Tools 24 Click on the ‘Finish’ button to import the project. If you look at the left hand side of Eclipse in the ‘Project Explorer’ window you’ll see the project is loaded with the name springintro. If you click on the project icon, you’ll be able to see the project structure (i.e., java classes, dependencies, configuration files, etc.). Figure 1-22.  Eclipse select Gradle subproject Next, click on the ‘Next’ button. In the following screen, in the ‘Select root directory’ line click on the ‘Browse’ button and select the top level directory of the book’s source code. Next, click on the ‘Build model’ button beside the ‘Browse’ button. The build model process retrieves the various Gradle subprojects contained in the book’s source code. A pop-up window appears indicating the progress of the build model process. Once the build model process finishes, you’ll see a list of Gradle projects, click on the project checkbox called ‘springintro’ inside ‘Ch1’ as illustrated in Figure 1-22. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 25 Next, click on the ‘Run’ button in the bottom right. In the bottom center of Eclipse you’ll see the ‘Console’ window appear. In this case, the ‘Console’ window displays a series of build messages produced by gradle, as well as any possible errors in case the build process fails. You’ve just built the application, now let’s run it. Select the project icon once again and press the F5 key to refresh the project directory. Expand the project tree. Toward the middle inside the build directory you’ll see a new directory called libs which contains the built application. Expand the libs directory by clicking on the icon. Next, select the file springintro-1.0-SNAPSHOT.jar. With the file selected, from Eclipse’s top level ‘Run’ menu select the ‘Run configurations...’ option. A pop-up window appears to edit and configure the run. Ensure the ‘Java application’ option is selected in the left-hand side. In the ‘Main class:’ box introduce com.apress.springrecipes.hello.Main. This is the main class for this project. Figure 1-23.  Eclipse select Gradle ‘build’ option Select the project icon and click on the right button of your mouse. A contextual menu appears with various project commands. Select the ‘Run as’ option followed by the ‘Gradle build’ option. A pop-up window appears to edit and configure the build. Click on the Project/task option ‘build’ as illustrated in Figure 1-23. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 26 Click on the ‘Run’ button in the bottom right. In the bottom center of Eclipse you’ll see the ‘Console’ window. In this case, the ‘Console’ window displays the application logging messages, as well as a greeting message defined by the application. 1-3. Build a Spring application with the IntelliJ IDE Problem You want to use the IntelliJ IDE to build Spring applications. Solution To start a new Spring application in the IntelliJ ‘Quick Start’ window click on the ‘Create New Project’ link. In the next window, assign a name to the project, a run-time JDK and select the ‘Java Module’ option. In the next window, click on the various Spring checkboxes so IntelliJ download’s the necessary Spring dependencies for the project. To open a Spring application that uses Maven, you first need to install Maven to work from a command line interface (See Recipes 1-4). From the IntelliJ top level ‘File’ menu select the ‘Import Project’ option. Next, select the Spring application based on Maven from your workstation. In the next screen select the ‘Import project from external model’ option and select a ‘Maven’ type. To open a Spring application that uses Gradle, you first need to install Gradle to work from a command line interface (See Recipe 1-5). From the IntelliJ top level ‘File’ menu select the ‘Import Project’ option. Next, select the Spring application based on Gradle from your workstation. In the next screen select the ‘Import project from external model’ option and select a ‘Gradle’ type. How It Works IntelliJ is one of the most popular commercial IDEs in the market. Unlike other IDEs which are produced by a foundation -- such as Eclipse -- or are made to support the flagship software of a company -- such as STS for the Spring framework -- IntelliJ is produced by a company called JetBrains whose sole business is to commercialize development tools. It’s this focus which makes IntelliJ particularly popular for professional developers in corporate environments. For this recipe I’ll assume you’ve already installed IntelliJ ultimate edition and just want to get up and running with Spring applications. Warning■■ intelliJ is available in a free community edition and an ultimate edition with a 30-day free trial. Although the free community edition provides good value for application development, the community edition does not include support for Spring applications. The instructions that follow are based on the assumption that you’re using the IntelliJ ultimate edition. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 27 To start a Spring application, in the IntelliJ ‘Quick Start’ window click on the ‘Create New Project’ link. In the next window, assign a name to the project, a run-time JDK and select the ‘Java Module’ option as illustrated in Figure 1-24. Figure 1-24.  IntelliJ create Spring project www.it-ebooks.info Chapter 1 ■ Spring Development Tools 28 A more common case than creating a Spring application from scratch is to continue development of a pre-existing Spring application. Under such circumstances, the owner of an application generally distributes the application’s source code with a build script to facilitate its ongoing development. The build script of choice for most Java application is a pom.xml file designed around the build tool called Maven and more recently a build.gradle file designed around the build tool called Gradle. The book’s source code and its applications are provided with Gradle build files, in addition to a single application with a Maven build file. Click on the ‘Next’ button. In the next window, click on the various Spring checkboxes -- as illustrated in Figure 1-25 -- for IntelliJ to download the necessary Spring dependencies for the project. Figure 1-25.  IntelliJ define project dependencies www.it-ebooks.info Chapter 1 ■ Spring Development Tools 29 Once you download the book’s source and unpack it to a local directory, click on the IntelliJ top level ‘File’ menu and select the ‘Import Project’ option. A pop-up window appears as illustrated in Figure 1-26. Figure 1-26.  IntelliJ select file or directory to import www.it-ebooks.info Chapter 1 ■ Spring Development Tools 30 Ensure the project checkbox is selected and click on the ‘Next’ button to import the project. Next, choose the SDK version for the project. Confirm the project name, location and click on the ‘Finish’ button. All projects in IntelliJ are loaded on the left-hand side in the ‘Project’ window. In this case, the project appears with the name springintro_mvn. If you click on the project icon, you’ll be able to see the project structure (i.e., java classes, dependencies, configuration files, etc.). If you double click on any of the project files inside the ‘Project’ window, the file is opened in a separate tab in the center window. You can inspect the contents of the file, as well as edit or delete its contents. Next, we need to setup Maven to work with IntelliJ. Follow the instructions in Recipe 1-4 to install Maven to work from the command line. Once you do this, you can setup IntelliJ to work with Maven. In the next window you’ll see the line 'com.apress.springrecipes:...' as illustrated in Figure 1-28. Figure 1-27.  IntelliJ import project Figure 1-28.  IntelliJ import Maven project Click on the directory tree presented in the pop-up window until you select the directory of the book’s source code inside Ch1 and then select springintro_mvn. Click on the ‘Next’ button. In the next screen select the ‘Import project from external model’ option and select a ‘Maven’ type as illustrated in Figure 1-27. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 31 Figure 1-29.  IntelliJ Maven configuration Click on the IntelliJ top level ‘File’ menu and select the ‘Settings’ option. A pop-up window appears to configure IntelliJ settings. Click on the ‘Maven’ option and in the ‘Maven home directory’ introduce the Maven installation directory based on your system. This is illustrated in Figure 1-29. Click on the ‘Apply’ button, followed by the ‘OK’ button. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 32 Select the project the ‘Introduction to Spring’ line in the Maven projects window and click on the right button of your mouse. A contextual menu appears with various commands for the project. Select the ‘Run Maven Build’ option. In the bottom center of IntelliJ you’ll see the ‘Run’ window appear. In this case, the ‘Run’ window displays a series of build messages produced by Maven, as well as any possible errors in case the build process fails. Warning■■ if you see the error message ‘No valid Maven installation found. Either set the home directory in the configuration dialog or set the M2_HOME environment variable on your system’ it means Maven is not being found by IntelliJ. Verify the Maven installation and configuration process. Figure 1-30.  IntelliJ Maven projects window Next, on the right-hand side of IntelliJ click on the vertical tab ‘Maven projects’ to show the Maven project window as illustrated in Figure 1-30. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 33 You’ve just built the application, congratulations! Now let’s run it. By default, IntelliJ hides the target directory created by Maven to place the built application. You’ll need to deactivate this behavior in order to run the application directly from IntelliJ. Click on the IntelliJ top level ‘File’ menu and select the ‘Settings’ option. A pop-up window appears to configure IntelliJ settings. Double click on the ‘Maven’ option and click on the ‘Importing’ sub-option. Uncheck the box ‘Exclude build directory’ as illustrated in Figure 1-31. Click on the ‘Apply’ button and then click on the ‘OK’ button. Figure 1-31.  IntelliJ Maven show build directory www.it-ebooks.info Chapter 1 ■ Spring Development Tools 34 Figure 1-32.  IntelliJ select application to run With this last change you’ll see the target directory in the ‘Project’ window. If you don’t see the target directory click the Ctrl+Alt+Y key combination to synchronize the project. Expand the target directory by clicking on its icon. Next, select the file springintro_mvn-1.0-SNAPSHOT.jar as illustrated in Figure 1-32. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 35 Click on the IntelliJ top level ‘Run’ menu and select the ‘Run’ option. A small box appears to configure IntelliJ run configurations. Click on the small box and a pop-up window appears to configure the various types of applications supported by IntelliJ. Click on the ‘Application’ option. Add the com.apress.springrecipes.hello.Main value to the ‘Main class:’ box and ensure the remaining options are similar to Figure 1-33. Figure 1-33.  Configure IntelliJ application run www.it-ebooks.info Chapter 1 ■ Spring Development Tools 36 Click on the ‘Run’ button. In the bottom center of IntelliJ in the ‘Run’ window you’ll see the application logging messages, as well as a greeting message defined by the application. Now let’s build a Gradle application with IntelliJ. First you need to install Gradle. Follow the instructions in Recipe 1-5 to install Gradle to work from the command line. Once you do this, you can setup IntelliJ to work with Gradle. Click on the IntelliJ top level ‘File’ menu and select the ‘Import Project’ option. A pop-up window appears as illustrated in Figure 1-26. Click on the directory tree presented in the pop-up window until you select the file build.gradle in the top level directory of the book’s source code. Figure 1-34.  Define IntelliJ application run Click on the ‘Apply’ button. The ‘Run’ button is disabled because you’re working with the default run configurations. Click on the plus sign in the top-left of the pop-up window. The left column changes to ‘Add new configuration’, click on the ‘Application’ option. A new application configuration is added. Add the application name sprigintro_mvn to the ‘Name’ box as illustrated in Figure 1-34 and click on the ‘Apply’ button. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 37 Click on the ‘Next’ button. In the next screen select the ‘Import project from external model’ option and select a ‘Gradle’. In the next screen, introduce the Gradle home directory in the ‘Gradle home’ box, based on the Gradle installation of your system. This is illustrated in Figure 1-35. Figure 1-35.  Define Gradle home for IntelliJ Figure 1-36.  IntelliJ JetGradle project window Click on the ‘Next’ button to confirm the import process and the click on the ‘Finish’ button to complete the import process. Next, on the right hand side of IntelliJ click on the vertical ‘Jet Gradle’ tab to show Gradle. Select the project the ‘springintro’ inside ‘Ch1’ in the projects window as illustrated in Figure 1-36. Click on the IntelliJ top level ‘Run’ menu and select the ‘Run’ option. A small box appears to configure IntelliJ run configurations. Click on the small box and a pop-up window appears to configure the various types of applications supported by IntelliJ. Click on the ‘Groovy’ option -- this is due to Gradle being a Groovy based tool. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 38 In the ‘Script path’ box assign the build.gradle file from the Ch1 directory of the book’s source code. In the ‘Module’ box select the ‘Ch1’ option. In the ‘Script parameters:’ box introduce the text build, which is the parameter for the Gradle script. And in the ‘Working directory’ ensure you have the Ch1/springintro directory of the book’s source code. Click on the ‘Apply’ button. Click on the plus sign in the top-left of the pop-up window. The left column changes to ‘Add new configuration’, click on the ‘Groovy’ option. A new application configuration is added. Add the application name to the ‘springintro’ box as illustrated in Figure 1-37 and click on the ‘Apply’ button once again. Figure 1-37.  IntelliJ Groovy application configuration Next, click on the IntelliJ top level ‘Run’ menu and select the ‘Run springintro’ option. In the bottom center of IntelliJ you’ll see the ‘Run’ window appear. In this case, the ‘Run’ window displays a series of build messages produced by Gradle, as well as any possible errors in case the build process fails. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 39 Figure 1-38.  IntelliJ project structure show build folder You’ve just built the application. Now let’s run it. By default, IntelliJ hides the build directory created by Gradle to place the built application. You’ll need to deactivate this behavior in order to run the application directly from IntelliJ. Click on the IntelliJ top level ‘File’ menu and select the ‘Project Structure...’ option. A pop-up window appears to configure IntelliJ project structure. Ensure the ‘Modules’ option is selected on the left-hand side. In the ‘Excluded folders’ list, click on the cross next to the ‘build’ folder. This is illustrated in Figure 1-38. Click on the ‘Apply’ button and then click on the ‘OK’ button. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 40 Figure 1-39.  IntelliJ select application to run With this last change you’ll see the build directory in the ‘Project’ window. If you don’t see the target directory click the key combination Ctrl+Alt+Y to synchronize the project. Expand the target directory by clicking on its icon. Next, select the file springintro-1.0-SNAPSHOT.jar inside the libs directory of the build directory as illustrated in Figure 1-39. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 41 Click on the IntelliJ top level ‘Run’ menu and select the ‘Run...’ option. A small box appears to configure IntelliJ run configurations. Click on the ‘Edit configurations...’ of the small box, a pop-up window appears to configure the various types of applications supported by IntelliJ. Click on the ‘Application’ option inside the ‘Defaults’ list. Add com.apress.springrecipes.hello.Main to the ‘Main class:’ box and ensure the remaining options are similar to Figure 1-40. Click on the ‘Apply’ button. The ‘Run’ button is disabled because you’re working with the default run configurations. Click the plus sign in the top-left of the pop-up window. The left column changes to ‘Add new configuration’, click on the ‘Application’ option. A new application configuration is added. Add the application name ‘springintrojar’ to the ‘Name’ box and click on the ‘Apply’ button. Select the file springintro-1.0-SNAPSHOT.jar inside the libs directory of the build directory once more and click on the right button of your mouse. From the contextual menu select the ‘Add as Library...’ option near the bottom. This is necessary due to Gradle with IntelliJ classpath bug. Finally, from the top level ‘Run’ menu select the ‘Run springintrojar...’ button. In the bottom center of IntelliJ in the ‘Run’ window you’ll see the application logging messages, as well as a greeting message defined by the application. Figure 1-40.  Configure IntelliJ application run www.it-ebooks.info Chapter 1 ■ Spring Development Tools 42 1-4. Build a Spring application with the Maven command line interface Problem You want to build a Spring application with Maven from the command line. Solution Download Maven from http://maven.apache.org/download.cgi. Ensure the JAVA_HOME environment variable is set to Java’s SDK main directory. Modify the PATH environment variable to include Maven’s bin directory. How It Works Maven is available as a standalone command line interface tool. This allows Maven to be leveraged from a wide variety of development environments. For example, if you prefer to use a text editor like emacs or vi to edit an application’s code, it becomes essential to be able to access a build tool like Maven to automate the grunt work (e.g., copying files, one step compiling) typically associated with the build process for Java applications. Maven can be downloaded for free from http://maven.apache.org/download.cgi. Maven is available in both source code and binary versions. Since Java tools are cross-platform, I recommend you download the binary version to avoid the additional compilation step. At the time of this writing the latest stable release of Maven is the 3.0.5 version. Once you download Maven, ensure you have a Java SDK installed on your system as Maven requires it at run-time. Proceed to install Maven by unpacking it and defining the JAVA_HOME and PATH environment variables.   www@ubuntu:~$ tar -xzvf apache-maven-3.0.5-bin.tar.gz apache-maven-3.0.5/boot/plexus-classworlds-2.4.jar apache-maven-3.0.5/lib/maven-embedder-3.0.5.jar apache-maven-3.0.5/lib/maven-settings-3.0.5.jar .... .... # Add JAVA_HOME variable www@ubuntu:~$ export JAVA_HOME=/usr/lib/jvm/java-6-openjdk/ # Add Maven executable to PATH variable www@ubuntu:~$ export PATH=$PATH:/home/www/apache-maven-3.0.5/bin/   Notice the maven binary installation is straightforward. After you unpack the maven binary, you need to define the JAVA_HOME environment so maven has access to Java libraries at run-time. In addition you should also add the maven bin directory to the PATH environment variable so maven is accessible from any location on your system. Tip■■ if you declare the variables JAVA_HOME and PATH as illustrated previously, you’ll need to do this process every time you open a new shell session to use Maven. On Unix/Linux systems you can open the .bashrc file inside a user’s home directory and add the same export lines to avoid the need to declare the environment variables each session. On Windows systems you can set environment variables permanently by selecting the ‘My Computer’ icon, clicking on the right mouse button and then selecting the ‘Properties’ option. Then in the pop-up window select the ‘Advanced’ tab and click on the ‘Environment variables’ button. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 43 The Maven executable is available through the mvn command. If you set the environment variables correctly as described previously, typing mvn from any directory on your system invokes Maven. Describing any more details about Maven execution would go beyond the scope of this recipe. However, next I’ll describe how to use Maven to build a Spring application from the book’s source code. Once you download the book’s source code and unpack it to a local directory, go to the directory called Ch1/springintro_mvn. Type mvn to invoke maven and build the application under springintro_mvn. The output should look like the following   www@ubuntu:/www/springrecipes/Ch1/springintro_mvn$ mvn [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building Introduction to Spring 1.0-SNAPSHOT [INFO] ------------------------------------------------------------------------ [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ springintro --- ......... ......... ......... [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 8.004s   You’ve just built the application, congratulations! Now let’s run it. Descend into the directory called target created by maven under the Ch1/springintro_mvn directory. You’ll see the file springintro_mvn-1-0.SNAPSHOT.jar which is the built application. Execute the command java -jar springintro_mvn-1-0.SNAPSHOT.jar to run the application. You’ll see application logging messages, as well as a greeting message defined by the application. 1-5. Build a Spring application with the Gradle command line interface Problem You want to build a Spring application with Gradle from the command line. Solution Download Gradle from http://www.gradle.org/downloads. Ensure the JAVA_HOME environment variable is set to Java’s SDK main directory. Modify the PATH environment variable to include Gradle’s bin directory. How It Works Gradle is available as a standalone command line tool. This allows Gradle to be leveraged from a wide variety of development environments. For example, if you prefer to use a text editor like emacs or vi to edit an application’s code, it becomes essential to be able to access a build tool like Gradle to automate the grunt work (e.g., copying files, one step compiling) typically associated with the build process for Java applications. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 44 Gradle can be downloaded for free from http://www.gradle.org/downloads. Gradle is available in both source code and binary versions. Since Java tools are cross-platform, I recommend you download the binary version to avoid the additional compilation step. At the time of this writing the latest stable release of Gradle is the 1.11 version. Once you download Gradle, ensure you have a Java SDK installed on your system as Gradle requires it at run-time. Proceed to install Gradle by unpacking it and defining the JAVA_HOME and PATH environment variables.   www@ubuntu:~$ unzip gradle-1.5-bin.zip Archive: gradle-1.5-bin.zip creating: gradle-1.5/ inflating: gradle-1.5/getting-started.html inflating: gradle-1.5/LICENSE inflating: gradle-1.5/NOTICE .... .... # Add JAVA_HOME variable www@ubuntu:~$ export JAVA_HOME=/usr/lib/jvm/java-6-openjdk/ # Add Maven executable to PATH variable www@ubuntu:~$ export PATH=$PATH:/home/www/gradle-1.5/bin/   Notice the Gradle binary installation is straightforward. After you unpack the Gradle binary, you need to define the JAVA_HOME environment so Gradle has access to Java libraries at run-time. In addition you should also add the Gradle bin directory to the PATH environment variable so Gradle is accessible from any location on your system. Tip■■ if you declare the variables JAVA_HOME and PATH as illustrated previously, you’ll need to do this process every time you open a new shell session to use Gradle. On Unix/Linux systems you can open the .bashrc file inside a user’s home directory and add the same export lines to avoid the need to declare the environment variables each session. On Windows systems you can set environment variables permanently by selecting the ‘My Computer’ icon, clicking on the right mouse button and then selecting the ‘Properties’ option. Then in the pop-up window select the ‘Advanced’ tab and click on the ‘Environment variables’ button. The Gradle executable is available through the gradle command. If you set the environment variables correctly as described previously, typing gradle from any directory on your system invokes Gradle. Describing any more details about Gradle execution would go beyond the scope of this recipe. However, since the book’s source code has numerous Spring applications that use Gradle, I’ll describe how to use Gradle to build one of these Spring applications. Once you download the book’s source and unpack it to a local directory, go to the directory called Ch1/springintro. Type gradle to invoke Gradle and build the application under springintro. The output should look like the following   www@ubuntu:/www/springrecipes/Ch1/springintro$ gradle :springintro:clean UP-TO-DATE :springintro:compileJava :springintro:processResources :springintro:classes :springintro:copyDependenciesToLibDir :springintro:jar :springintro:assemble :springintro:compileTestJava UP-TO-DATE www.it-ebooks.info Chapter 1 ■ Spring Development Tools 45 :springintro:processTestResources UP-TO-DATE :springintro:testClasses UP-TO-DATE :springintro:test :springintro:check :springintro:build   BUILD SUCCESSFUL   Total time: 8.975 secs   You’ve just built the application, congratulations! Now let’s run it. Descend into the directory called libs created by gradle under the Ch1/springintro directory. You’ll see the file springintro-1-0.SNAPSHOT.jar which is the built application. Execute the command java -jar springintro-1-0.SNAPSHOT.jar to run the application. You’ll see application logging messages, as well as a greeting message defined by the application. 1.6 Build a Spring application with the Gradle Wrapper Problem You want to build a Spring application utilizing the Gradle Wrapper from the command line. Solution Run the gradlew script from the command line. How It Works Although Gradle (see Recipe 1.5) is available as a standalone command line tool, a lot of (Open Source) projects use the Gradle Wrapper to give you access to Gradle. The advantage of this approach is that the application is completely self-providing. You as a developer don’t need to have Gradle installed, as the Gradle Wrapper will download a specific version of Gradle to build the project. Once you have a project that utilizes the Gradle Wrapper you can simply type ./gradlew build on the command line to have Gradle automatically download and run the build. The only prerequisite is to have a Java SDK installed as Gradle requires it at run-time and the Gradle Wrapper needs it to run. Once you download the book’s source code and unpack it to a local directory, go to the directory called Ch1/Recipe_1_6. Type ./gradlew to invoke the Gradle Wrapper and automatically build the application under Recipe_1_6. The output will something look like Figure 1-41. www.it-ebooks.info Chapter 1 ■ Spring Development Tools 46 TIP■■ the source code from the book can be built with either plain Gradle or by using the Gradle Wrapper. The latter is preferable as the code will be built using the same Gradle version while developing the samples. Summary In this chapter you learned how to set up the most popular development tools to create Spring applications. You explored how to build and run the Spring application with five toolboxes. Three toolboxes consisted of using IDEs. The Spring Tool Suite distributed by the creators of the Spring Framework. The Eclipse IDE distributed by the Eclipse Software foundation. And the IntelliJ IDE distributed by IntelliJ. Two toolboxes consisted of using command line tools. The Maven build tool and the newer Gradle build tool which is gaining popularity against the Maven build tool. Figure 1-41.  Gradle build output www.it-ebooks.info 47 Chapter 2 Spring Core Tasks In this chapter, you’ll learn about the core tasks associated with Spring. At the heart of the Spring framework is the Spring Inversion of Control (IoC) container. The IoC container is used to manage and configure POJOs or Plain Old Java Objects.1 Because one of the primary appeals of the Spring framework is to build Java applications with POJOs, many of Spring’s core tasks involve managing and configuring POJOs in the IoC container. So whether you plan to use the Spring framework for web applications, enterprise integration, or some other type of project, working with POJOs and the IoC container is one of the first steps you need to take to work with the Spring framework. The majority of the recipes in this chapter cover tasks that you’ll use throughout the book and on a daily basis to develop Spring applications. Note■■   The term ‘bean’ is used interchangeably with a POJO instance both in the book and the Spring documentation. Both refer to an object instance created from a Java class. The term ‘component’ is used interchangeably with a POJO class both in the book and the Spring documentation. Both refer to the actual Java class from which object instances are created. The source code download is organized to use gradle to build the different Recipe applications. Gradle takes care of loading all the necessary Java classes, dependencies and creating an executable. Chapter 1 describes how to setup the Gradle tool. Furthermore, if a Recipe illustrates more than one approach, the source code is classified with various examples with roman letters (e.g., Recipe_2_1_i, Recipe_2_1_ii, Recipe_2_1_iii, etc.). Note ■■   To build each application, go inside the Recipe directory (e.g., Ch2/Recipe_2_1_i/) and execute the gradle command to compile the source code. Once the source code is compiled, a build/libs sub-directory is created with the application executable. You can then run the application JAR from the command line (e.g., java -jar Recipe_2_1_i- 1.0.SNAPSHOT.jar) 1The term POJO means an ordinary Java object without any specific requirements, such as to implement an interface or to extend a base class. This term is used to distinguish lightweight Java components from heavyweight components in other complex component models (e.g., EJB components prior to version 3.1 of the EJB specification). www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 48 2-1. Manage and Configure POJOs with the Spring IoC Container Problem You want to manage POJOs with Spring’s IoC container. Solution Design a POJO class. Next, configure POJO instance values for the Spring IoC container in an XML file. Next, instantiate the Spring IoC container to gain access to the POJO instance values defined in an XML file. The POJO instances or bean instances become accessible to put together as part of an application. How It Works Suppose you are going to develop an application to generate sequence numbers. And that you are also going to need many series of sequence numbers for different purposes. Each sequence will have its own prefix, suffix, and initial value. So you have to create and maintain multiple generator instances for the application. Create the POJO Class Let’s create a SequenceGenerator class that has three properties— prefix, suffix, and initial. The class will have a private field counter to store the numeric value of each generator. Each time you call the getSequence() method on a generator instance, you get the last sequence number with the prefix and suffix joined. You declare this last method as synchronized to make it thread-safe.   package com.apress.springrecipes.sequence;   public class SequenceGenerator { private String prefix; private String suffix; private int initial; private int counter; public SequenceGenerator() {} public SequenceGenerator(String prefix, String suffix, int initial) { this.prefix = prefix; this.suffix = suffix; this.initial = initial; } public void setPrefix(String prefix) { this.prefix = prefix; }   public void setSuffix(String suffix) { this.suffix = suffix; }   public void setInitial(int initial) { this.initial = initial; } www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 49 public synchronized String getSequence() { StringBuffer buffer = new StringBuffer(); buffer.append(prefix); buffer.append(initial + counter++); buffer.append(suffix); return buffer.toString(); } }   Note this last SequenceGenerator class can be instantiated by setter methods or by a standard Java constructor. When you use the Spring IoC container to initialize POJOs, if you use the standard Java constructor the mechanism is called constructor injection, where as if you use setter methods the mechanism is called setter injection. Create a XML Configuration for your POJO To define instances of a POJO class in the Spring IoC container, you have to create an XML configuration with instantiation values. Spring XML configuration files can have any name, but by convention we’ll call the configuration file beans.xml. You can put this file in the root of the classpath for easier testing within an IDE. Tip■■   Spring allows you to configure POJO instances or beans in one or more XML configuration files. For a simple application, you can just centralize your beans in a single configuration file (e.g., beans.xml). But for a large application with a lot of beans, you should separate them in multiple configuration files according to their functionalities (e.g., DAOs and controllers). One useful division is by the architectural layer of context services. Recipe 2-3, section ‘Resolve POJO references from multiple locations’ describes this process. The Spring XML configuration allows you to use custom tags from different schemas (tx, jndi, jee, and so on) to make the bean configuration simpler and clearer. Here’s an example of the simplest XML configuration possible.   ...   Each POJO instance or bean should have a unique name or id and a fully qualified POJO class name so the Spring IoC container to instantiate it. For each bean property of a simple type (e.g., String and other primitive types), you can specify a element for it. Spring will attempt to convert your value into the declaring type of this property. To configure a property via setter injection, you use the element and specify the property name in its name attribute. A requires that the underlying POJO class contain a corresponding setter method.   30 www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 50 A 100000   You can also configure bean properties via constructor injection by declaring them in the elements. Note there’s no name attribute in , because constructor arguments are position based.   30 A 100000   In the Spring IoC container, each bean’s name should be unique for each context. Duplicate names are allowed for overriding bean declaration if more than one context is loaded. The concept of contexts is explained in the next section ‘Instantiate the Spring IoC container’. Although a bean’s name can be defined by the name attribute of the element, the preferred way of identifying a bean is through the standard XML id attribute. In this way, if your text editor is XML-aware, it can help to validate each bean’s uniqueness at design time.   ...   However, be aware XML has restrictions on the characters that can appear in the XML id attribute. For this reason alone, the name attribute can be helpful if you use such special characters in a bean name. In addition, Spring allows you to specify multiple bean names for the same bean separated by commas in the name attribute, something you can’t do this with the XML id attribute because commas are not allowed. With respect to bean names it’s also worth mentioning that neither the name attribute nor the id attribute is required. A bean that has no name defined is called an anonymous bean. You will usually create these types of beans for administrative purposes, mainly because application beans are referenced by name or id to be used by other application beans. Spring also supports a shortcut for specifying the value of a simple type property. You can present a value attribute in the element instead of enclosing a element inside.     www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 51 This shortcut also works for constructor arguments.     Spring also has another convenient shortcut to define properties via setter injection. It consists of using the p schema to define bean properties as attributes of the element. This can shorten the lines of XML configuration.     And Spring also has another convenient shortcut to define properties via constructor injection. It consists of using the c schema to define constructor arguments as attributes of the element. This can shorten the lines of XML configuration.   Instantiate the Spring IoC Container To create bean instances, you have to instantiate the Spring IoC container by reading the XML configuration files with bean instantiation values. Then, you can get the bean instances from the IoC container itself. Spring provides two types of IoC container implementations. The basic one is called bean factory. The more advanced one is called application context, which is compatible with the bean factory. Note the configuration files for these two types of IoC containers are identical. The application context provides more advanced features than the bean factory while keeping the basic features compatible. Therefore, we strongly recommend using the application context for every application unless the resources of an application are restricted (e.g., such as when running Spring for an applet or a mobile device). The interfaces for the bean factory and the application context are BeanFactory and ApplicationContext, respectively. The ApplicationContext interface is a subinterface of BeanFactory for maintaining compatibility. www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 52 Since ApplicationContext is an interface, you have to instantiate an implementation of it. Spring has several application context implementations, we recommend you use GenericXmlApplicationContext which is the newest and most flexible implementation. With this class you can load the XML configuration file from the classpath. ApplicationContext context = new GenericXmlApplicationContext("beans.xml"); Once the application context is instantiated, the object reference—in this case context—provides an entry point to access the POJO instances or beans. Get POJO Instances or Beans from the IoC Container To get a declared bean from a bean factory or an application context, you just make a call to the getBean() method and pass in the unique bean name. The return type of the getBean() method is java.lang.Object, so you have to cast it to its actual type before using it.   SequenceGenerator generator = (SequenceGenerator) context.getBean("sequenceGenerator");   The getBean() method also supports another variation where you can provide the bean class name to avoid making the cast.   SequenceGenerator generator = context.getBean("sequenceGenerator",SequenceGenerator.class);   Once you reach this step you can use the POJO or bean just like any object created using a constructor outside of Spring. A Main class for running the sequence generator application would be the following:   package com.apress.springrecipes.sequence;   import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext;   public class Main {   public static void main(String[] args) { ApplicationContext context = new GenericXmlApplicationContext("beans.xml");   SequenceGenerator generator = (SequenceGenerator) context.getBean("sequenceGenerator");   System.out.println(generator.getSequence()); System.out.println(generator.getSequence()); } }   If everything is available in the Java classpath (the beans.xml file, SequenceGenerator POJO class and the Spring JAR dependencies), you should see the following output, along with some logging messages:   30100000A 30100001A www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 53 2-2. Create POJOs by Invoking a Constructor Problem You would like to create a POJO instance or bean in the Spring IoC container by invoking its constructor, which is the most common and direct way of creating beans. It is equivalent to using the new operator to create objects in Java. Solution Define a POJO class with a constructor or constructors. Use Spring’s element to define constructor arguments, then for each element Spring injects the value through the setter method. If no is specified, the default constructor with no arguments is invoked. You can specify the type and index attributes or the name attribute for the element to avoid constructor ambiguity. How It Works Suppose you’re going to develop a shop application to sell products online. First of all, you create the Product POJO class, which has several properties, such as the product name and price. As there are many types of products in your shop, you make the Product class abstract to extend it for different product subclasses.   package com.apress.springrecipes.shop;   public abstract class Product {   private String name; private double price;   public Product() {}   public Product(String name, double price) { this.name = name; this.price = price; }   // Getters and Setters ...   public String toString() { return name + " " + price; } } www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 54 Create the POJO Classes with Constructors Then you create two product subclasses, Battery and Disc. Each of them has its own properties.   package com.apress.springrecipes.shop;   public class Battery extends Product {   private boolean rechargeable;   public Battery() { super(); }   public Battery(String name, double price) { super(name, price); }   // Getters and Setters ... }   package com.apress.springrecipes.shop;   public class Disc extends Product {   private int capacity;   public Disc() { super(); }   public Disc(String name, double price) { super(name, price); }   // Getters and Setters ... } Create XML Configuration to Create POJOs via Constructor To define some products in the Spring IoC container, you create the following bean configuration file:     www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 55   If there’s no element specified, the default constructor with no argument is invoked. Then for each element, Spring injects the value through the setter method. The preceding bean configuration is equivalent to the following code snippet:   Product aaa = new Battery(); aaa.setName("AAA"); aaa.setPrice(2.5); aaa.setRechargeable(true);   Product cdrw = new Disc(); cdrw.setName("CD-RW"); cdrw.setPrice(1.5); cdrw.setCapacity(700);   Otherwise, if one or more elements is specified, Spring invokes the most appropriate constructor that matches your arguments.     As there is no constructor ambiguity for the Product class and subclasses, the preceding bean configuration is equivalent to the following code snippet:   Product aaa = new Battery("AAA", 2.5); aaa.setRechargeable(true);   Product cdrw = new Disc("CD-RW", 1.5); cdrw.setCapacity(700);   www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 56 You can write the following Main class to test your products by retrieving them from the Spring IoC container:   package com.apress.springrecipes.shop;   import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext;   public class Main {   public static void main(String[] args) throws Exception { ApplicationContext context = new GenericXmlApplicationContext("beans.xml");   Product aaa = (Product) context.getBean("aaa"); Product cdrw = (Product) context.getBean("cdrw"); System.out.println(aaa); System.out.println(cdrw); } } Resolve Constructor Ambiguity When you specify one or more constructor arguments for a POJO instance or bean, Spring attempts to find an appropriate constructor in the POJO class and pass in your arguments for bean instantiation. However, if the arguments can be applied to more than one constructor, it may cause ambiguity in constructor matching. In this case, Spring may not be able to invoke your expected constructor. Now let’s use another example of the SequenceGenerator class—introduced in Recipe 2-1. Let’s add a new constructor to the SequenceGenerator class with the prefix and suffix as arguments.   package com.apress.springrecipes.sequence;   public class SequenceGenerator { ... public SequenceGenerator(String prefix, String suffix) { this.prefix = prefix; this.suffix = suffix; } }   In the bean declaration (i.e., XML file), you can specify one or more constructor arguments through the elements. Spring attempts to find an appropriate constructor for that class and pass in your arguments for bean instantiation.     www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 57 It’s easy for Spring to find a constructor for these two arguments, as there is only one constructor that requires two arguments. But suppose you have to add another constructor to SequenceGenerator with prefix and initial as arguments.   package com.apress.springrecipes.sequence;   public class SequenceGenerator { ... public SequenceGenerator(String prefix, String suffix) { this.prefix = prefix; this.suffix = suffix; }   public SequenceGenerator(String prefix, int initial) { this.prefix = prefix; this.initial = initial; } }   To invoke this constructor, you make the following bean declaration to pass a prefix and an initial value. The remaining suffix is injected through the setter method.     However, if you run the application now, you will get the following result:   300A 301A   The cause of this unexpected result is that the first constructor, with prefix and suffix as arguments, has been invoked, but not the second. This is because Spring resolves both of your arguments as String type by default and considers the first constructor was most suitable, as no type conversion was required. To specify the expected type of your arguments, you have to set it in the type attribute in .     www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 58 Now add one more constructor to SequenceGenerator with initial and suffix as arguments.   package com.apress.springrecipes.sequence;   public class SequenceGenerator { ... public SequenceGenerator(String prefix, String suffix) { this.prefix = prefix; this.suffix = suffix; }   public SequenceGenerator(String prefix, int initial) { this.prefix = prefix; this.initial = initial; }   public SequenceGenerator(int initial, String suffix) { this.initial = initial; this.suffix = suffix; } }   Next, modify the bean declaration accordingly to match this new constructor.     If you run the application again, you may get the right result or the following unexpected result:   30100000null 30100001null   The reason for this uncertainty is that Spring internally scores each constructor for compatibility with your arguments. But during the scoring process, the order in which your arguments appear in the XML is not considered. This means that from the view of Spring, the second and the third constructors get the same score. Which one Spring picks depends on which one is matched first. According to the Java Reflection API, or more accurately the Class.getDeclaredConstructors() method, the constructors returned will be in an arbitrary order that may differ from the declaration order. All these factors, acting together, cause ambiguity in constructor matching. To avoid this problem, you have to indicate the indexes of your arguments explicitly through the index attribute of . With both the and attributes set, Spring is able to find the expected constructor for a bean accurately.     www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 59 And yet another approach to avoid ambiguity in constructors is to specify the constructor parameter names with the name attribute.     However, to make the constructor name attribute work out of the box, the code must be compiled with the debug flag enabled. This allows Spring to look up the parameter name from the constructor. If you can’t compile your code with debug flag (or don’t want to) you can use @ConstructorProperties JDK annotation to explicitly name your constructor arguments. In this case the POJO class with the @ConstructorProperties JDK annotation would look like:   public class SequenceGenerator { ... @ConstructorProperties({"prefix", "suffix"}) public SequenceGenerator(String prefix, String suffix) { this.prefix = prefix; this.suffix = suffix; } @ConstructorProperties({"prefix", "initial"}) public SequenceGenerator(String prefix, int initial) { this.prefix = prefix; this.initial = initial; } @ConstructorProperties({"initial", "suffix"}) public SequenceGenerator(int initial, String suffix) { this.initial = initial; this.suffix = suffix; } } 2-3. Use POJO References, Auto-Wiring, and Imports to Interact with Other POJOs Problem The POJO instances or beans that make up an application often need to collaborate with each other to complete the application’s functions. For beans to access each other, you have to specify bean references in the bean configuration file. The Spring container can auto-wire your beans to save you the trouble of configuring the wirings manually. Solution In the bean configuration file, you can specify a bean reference for a bean property or a constructor argument by the element. It’s as easy as specifying a simple value by the element. You can also enclose a bean declaration in a property or a constructor argument directly as an inner bean. To specify auto-wiring mode in the Spring IoC container you only have to specify the autowire attribute of tag. www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 60 How It Works It can be common to have POJO instances reference one another. For example, the prefix value of the SequenceGenerator class — initially described in Recipe 2-1 — uses a string. However, it’s possible to use another POJO instead of using a simple string. This can make it easier to adapt to future requirements, because instead of hardcoded values, values are based on programming logic contained in another POJOs. Create Multiple POJO Classes that Reference One Another You can create the PrefixGenerator interface to define the prefix generation operation.   package com.apress.springrecipes.sequence;   public interface PrefixGenerator {   public String getPrefix(); }   A prefix generation strategy could be to use the current system date. Let’s create the DatePrefixGenerator class that implements the PrefixGenerator interface.   package com.apress.springrecipes.sequence; import java.text.SimpleDateFormat; import java.text.DateFormat; import java.util.Date;   public class DatePrefixGenerator implements PrefixGenerator {   private DateFormat formatter;   public void setPattern(String pattern) { this.formatter = new SimpleDateFormat(pattern); }   public String getPrefix() { return formatter.format(new Date()); } }   The pattern of this generator will be injected through the setter method setPattern() and then used to create a java.text.DateFormat object to format the date. Note that since the pattern string won’t be used once the DateFormat object is created, it’s not necessary to store it in a private field. Next, you can declare a DatePrefixGenerator bean with string pattern for date formatting.   www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 61 Specify POJO References for Setter Methods To apply this prefix generator approach, the SequenceGenerator class should accept a PrefixGenerator object instead of a simple string. You can choose setter injection to accept this prefix generator. You have to delete the prefix property, its setter methods and constructors to avoid compiler errors.   package com.apress.springrecipes.sequence; public class SequenceGenerator { ... private PrefixGenerator prefixGenerator;   public void setPrefixGenerator(PrefixGenerator prefixGenerator) { this.prefixGenerator = prefixGenerator; }   public synchronized String getSequence() { StringBuffer buffer = new StringBuffer(); buffer.append(prefixGenerator.getPrefix()); buffer.append(initial + counter++); buffer.append(suffix); return buffer.toString(); } }   With this change a SequenceGenerator bean can refer to the dataPrefixGenerator bean as its prefixGenerator property by enclosing it in the element.     The bean attribute in the element can be a reference to any bean in the Spring IoC container, even if it’s not defined in the same XML configuration file. If you are referring to a bean in the same XML file, you should use the local attribute, as it is an XML ID reference. Your XML editor can help to validate whether a bean with that ID exists in the same XML file (i.e., the reference integrity).   ...   www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 62 There is also a convenient shortcut to specify a bean reference in the ref attribute of the element.   ...   Another convenient shortcut to specify bean references is by using the p schema to specify bean references as attributes of the element. This can shorten the lines of XML configuration.     To distinguish a bean reference from a simple property value, you have to add the -ref suffix to the property name. Specify POJO References for Constructor Arguments Bean references can also be applied to constructor injection. For example, you can add a constructor that accepts a PrefixGenerator object as an argument.   package com.apress.springrecipes.sequence;   public class SequenceGenerator { ... private PrefixGenerator prefixGenerator;   public SequenceGenerator(PrefixGenerator prefixGenerator) { this.prefixGenerator = prefixGenerator; } }   www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 63 In the element, you can enclose a bean reference by just like in the element.     The shortcut for specifying a bean reference also works for .   ... Declare Inner or Anonymous POJO References Whenever a bean instance is used for only one particular property, it can be declared as an inner bean. An inner bean declaration is enclosed in or directly, without any id or name attribute. In this way, the bean is anonymous and therefore can’t be used anywhere else. In fact, even if you define an id or a name attribute for an inner bean, it’s ignored.     An inner bean can also be declared in a constructor argument.   www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 64 Auto-Wire POJOs To specify the auto-wiring mode in the Spring IoC container you only have to specify the autowire attribute of element. Table 2-1 lists the auto-wiring modes supported by Spring. Table 2-1.  Auto-Wiring Modes Supported by Spring Mode Description no* No auto-wiring will be performed. You must wire the dependencies explicitly. byName For each bean property, wire a bean with the same name as the property. byType For each bean property, wire a bean whose type is compatible with that of the property. If more than one bean is found, an UnsatisfiedDependencyException will be thrown. constructor For each argument of each constructor, first find a bean whose type is compatible with the argument. Then, pick the constructor with the most matching arguments. In case of any ambiguity, an UnsatisfiedDependencyException will be thrown. * The default mode is no, but this can be changed by setting the default-autowire attribute of the root element. This default mode can be overridden by a bean’s own mode if specified. Tip■■   Although the auto-wiring feature is very powerful, the cost is that it reduces the readability of bean configurations. Because auto-wiring is performed by Spring at runtime, you cannot derive how beans are wired from the bean configuration file. In practice, we recommend applying auto-wiring only in applications whose component dependencies are not complicated. For example, to auto wire by type you can set the autowire attribute of the sequenceGenerator bean to byType and leave the prefixGenerator property unset. Then, Spring will attempt to wire a bean whose type is compatible with PrefixGenerator. In this case, the dataPrefixGenerator bean is wired automatically.   www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 65 Resolve Auto-Wire Ambiguity with the Primary Attribute The main problem of auto-wiring by type is that sometimes there is more than one bean in the IoC container compatible with the target type. In this case, Spring is not be able to decide which bean is most suitable for the property, and hence cannot perform auto-wiring. For example, if you have multiple beans that are based on the same interface and try to auto-wire by interface, auto-wiring by type won’t work. Spring will throw an UnsatisfiedDependencyException if more than one bean is found for auto-wiring. To help Spring decide which bean to use in case there is more than one bean compatible with the target type to auto-wire, you can use the primary attribute on a bean definition. The primary attribute gives preference to a bean when multiple candidates are qualified to autowire a single-valued dependency.   Resolve Auto-Wire Ambiguity with the ByName Attribute Another mode of auto-wiring by name using the byName attribute, which can sometimes resolve the problems of auto-wiring by type. It works very similarly to byType, but in this case, Spring attempts to wire a bean whose class name is the same as the property name, rather than with the compatible type. As the bean name is unique within a container, auto-wiring by name does not cause ambiguity.     However, auto-wiring by name may not work in all cases. Sometimes, it’s not possible to make the name of the target bean the same as the property. So in practice, you often need to specify ambiguous dependencies explicitly while keeping others beans auto-wired. That means you employ a mixture of explicit wiring and auto-wiring. www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 66 Another alternative to auto-wiring is to do it by constructor. This process works like auto-wiring by type, but it’s more complicated. For a bean with a single constructor, Spring attempts to wire a bean with a compatible type for each constructor argument. But for a beans with multiple constructors this process gets more complicated. Spring first attempts to find a bean with a compatible type for each argument of each constructor. Then, it picks the constructor with the most matching arguments. Suppose that SequenceGenerator has one default constructor and one constructor with an argument PrefixGenerator.   package com.apress.springenterpriserecipes.sequence;   public class SequenceGenerator {   public SequenceGenerator() {}   public SequenceGenerator(PrefixGenerator prefixGenerator) { this.prefixGenerator = prefixGenerator; } ... }   In this case, the second constructor is matched and picked because Spring can find a bean whose type is compatible with PrefixGenerator.     However, multiple constructors in a class can cause ambiguity for constructor argument matching. The situation may be further complicated if you ask Spring to determine a constructor for you. As you have seen, if Spring finds more than one candidate bean for auto-wiring, it will throw an UnsatisfiedDependencyException. In addition, if the auto-wiring mode is set to byName or byType, and Spring cannot find a matching bean to wire, it leaves the property unset, which may cause a NullPointerException or a value that has not been initialized. So, if you use this auto-wiring mode, take great care to avoid ambiguity and try to use the following guidelines to avoid it: Exclusively use constructor injection (instead of setter injection) to ensure the right • properties are set. Create setters with a dedicated init method.• Create setters with • @Required annotation when the property is required. Use the • @Autowired annotation which implies a required property by default. www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 67 Note■■   The @Required and @Autowired annotations are described in the next chapter. And to avoid auto-wiring issues altogether, it’s also possible to exclude beans from auto-wiring by adding the autowire-candidate attribute of the element to false. This technique is useful for beans you never want to be injected into other beans by autowiring. It does not mean that an excluded bean cannot itself be configured using autowiring. Rather, the bean itself is not a candidate for autowiring other beans. Resolve POJO References from Multiple Locations As an application grows it can become difficult to manage every POJO in a single configuration file (e.g., beans.xml). A common practice is to separate POJOs into multiple configuration files according to their functionalities. When you create multiple configuration files, obtaining references and auto-wiring POJOs that are defined in different files isn’t as straightforward as when everything is in a single configuration file. One approach is to initialize the application context with the location of each configuration file. In this manner, the POJOs for each configuration file are loaded into the context and references and autowiring between POJOs is possible.   ApplicationContext context = new GenericXmlApplicationContext(new String[] {"beans.xml","generators.xml"});   Instead of initializing the application context with a String configuration file, you use a String array to specify multiple configuration files. Another alternative is to use the tag so Spring makes the POJOs from one configuration file available in another.     The sequenceGenerator bean requires the datePrefixGenerator bean due to the depends-on attribute. But notice the datePrefixGenerator bean is not defined in the configuration file. The datePrefixGenerator bean is defined in a separate configuration file generators.xml. With the statement, Spring brings all the POJOs in generators.xml into the scope of the present configuration file where they can be referenced or auto-wired. www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 68 2-4. Configure POJOs with Java Collection Attributes Problem You want to configure POJOs with Java collection attributes. Solution Use the Spring tags , , and . How It Works Most POJOs use simple type properties (e.g., String and other primitive types), but it can also be common to have POJOs that have more elaborate properties like collections. The Spring IoC container can also instantiate POJOs with Java collection attributes. List, Set, and Map are the core interfaces representing the three main types of collections in the Java SDK, part of a framework called the Java Collections framework. For each collection type, Java provides several implementations with different functions and characteristics from which you can choose. In Spring, these types of collections are configured with a group of built-in XML tags: , , and . Based on the application presented in Recipe 2-1, suppose you’re going to allow more than one suffix for a sequence generator, with the suffixes appended to the sequence numbers with hyphens as the separators. This allows the sequence generator to accept suffixes of arbitrary data types and convert them to strings when appending to the sequence numbers. Lists, Arrays, and Sets First, let’s use a java.util.List collection to contain the suffixes. A list is an ordered and indexed collection whose elements can be accessed either by index or with a for-each loop.   package com.apress.springrecipes.sequence; ... public class SequenceGenerator { ... private List suffixes;   public void setSuffixes(List suffixes) { this.suffixes = suffixes; }   public synchronized String getSequence() { StringBuffer buffer = new StringBuffer(); ... for (Object suffix : suffixes) { buffer.append("-"); buffer.append(suffix); } return buffer.toString(); } }   www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 69 To define a property of the interface java.util.List in the bean configuration, you specify a tag that contains the elements. The elements allowed inside the tag can be a simple constant value specified by , a bean reference specified by , an inner bean definition specified by , an ID reference definition specified by , or a null element specified by . You can even embed other collections in a collection.   A   Conceptually, an array is very similar to a list in that it’s also an ordered and indexed collection that can be accessed by index. The main difference is that the length of an array is fixed and cannot be extended dynamically. In the Java Collections framework, an array and a list can be converted to each other through the Arrays.asList() and List.toArray() methods. For the sequence generator, you can use an Object[] array to contain the suffixes and access them either by index or with a for-each loop.   package com.apress.springrecipes.sequence; ... public class SequenceGenerator { ... private Object[] suffixes;   public void setSuffixes(Object[] suffixes) { this.suffixes = suffixes; } ... }   The definition of an array in the bean configuration file is identical to a list denoted by the tag. Another common collection type is a set. Both the java.util.List interface and the java.util.Set interface extend the same interface: java.util.Collection. A set differs from a list in that it is neither ordered nor indexed, and it can store unique objects only. That means no duplicate element can be contained in a set. When the same element is added to a set for the second time, it will replace the old one. The equality of elements is determined by the equals() method.   www.it-ebooks.info Chapter 2 ■ Spring Core Tasks 70 package com.apress.springrecipes.sequence; ... public class SequenceGenerator { ... private Set suffixes;   public void setSuffixes(Set suffixes) { this.suffixes = suffixes; } ... }   To define a property of java.util.Set type, use the tag to define the elements in the same way as a list.   ... A       www.it-ebooks.info Chapter 4 ■ Spring @MVC 230   The tag registers, among other things, the RequestMappingHandlerMapping and RequestMappingHandlerAdapter. As of Spring 3.1 it is also possible to use a Java based configuration approach which we can leverage to eliminate the XML (we will only be left with the for now). The Java based configuration looks like this   package com.apress.springrecipes.court.web.config;   // ... Imports omitted   @Configuration @EnableWebMvc public class WebConfiguration {   @Bean public ViewResolver internalResourceViewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/jsp/"); viewResolver.setSuffix(".jsp"); return viewResolver; } }   The important thing to notice here is the @EnableWebMvc annotation, which takes care of registering the RequestMappingHandlerMapping and RequestMappingHandlerAdapter as does the tag. We also added the @Configuration annotation so that Spring knows that this class needs to be used to configure out application. Our XML is now reduced to only a which will pickup our @Configuration annotated class. We could do the same for our service configuration and move that to Java configuration.   package com.apress.springrecipes.court.service.config;   import com.apress.springrecipes.court.service.ReservationService; import com.apress.springrecipes.court.service.ReservationServiceImpl; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;   @Configuration public class ServiceConfiguration {   @Bean public ReservationService reservationService() { return new ReservationServiceImpl(); } }   www.it-ebooks.info Chapter 4 ■ Spring @MVC 231 Now we would need to modify our court-service.xml to do component scanning of the service sub package to detect this configuration class.     If we rebuild and redeploy our application it should still work. As of the release of the Servlet 3.0 specification it isn’t necessary to have a web.xml file anymore, it became optional. As hinted earlier we can use an implementation of a ServletContainerInitializer (or multiple) to configure our application. Instead of implementing our own we are going to leverage the convenient Spring implementation the SpringServletContainerInitializer. This class is an implementation of the ServletContainerInitializer interface and scans the classpath for implementations of a WebApplicationInitializer interface. Luckily Spring provides some convenience implementations of this interface which we can leverage for our application we are going to use the AbstractAnnotationConfigDispatcherServletInitializer to reduce our Spring XML to zero. Create a class which extends AbstractAnnotationConfigDispatcherServletInitializer and implements the necessary methods.    package com.apress.springrecipes.court.web;   import com.apress.springrecipes.court.service.config.ServiceConfiguration; import com.apress.springrecipes.court.web.config.WebConfiguration; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;   public class CourtApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class[] getRootConfigClasses() { return new Class[] {ServiceConfiguration.class}; }   @Override protected Class[] getServletConfigClasses() { return new Class[] {WebConfiguration.class}; }   @Override protected String[] getServletMappings() { return new String[] {"/", "/welcome"}; } }   www.it-ebooks.info Chapter 4 ■ Spring @MVC 232 We need to configure the root-context and the servlet-context, respectively the ContextLoaderListener and DispatcherServlet. We do this by implementing the getRootConfigClasses and getServletConfigClasses methods. The first is for the ContextLoaderListener and this we pass the ServiceConfiguration class we created earlier. The second is for the DispatcherServlet and that we pass the WebConfiguration class (which requires a minor addition). Finally we need to map the servlet to 1 or more URLs for this we implement the getServletMappings method.   @Configuration @EnableWebMvc @ComponentScan("com.apress.springrecipes.court.web") public class WebConfiguration { ... }   As we want to eliminate XML as much as possible we also need to move the component-scanning to Java configuration. For this we use the @ComponentScan tag, which replaces the tag. With the configuration above we can remove the court-service.xml and court-servlet.xml however we, sadly, still need the web.xml. This is due to some omissions in the Servlet 3.0 spec, not everything that can be configured in the web.xml has a Java based alternative yet. For instance the welcome pages can only be specified in XML not in Java, the same goes for the display-name and error-pages (which we will see later). However the following is what is left of the web.xml and all the XML we have left in our application.   Court Reservation System welcome   Note■■   From this point on the recipes will use Java-based configuration, however if you prefer an XML-based approach we also included the XML equivalent of the configuration. See the court-servlet.xml file in the project. 4-2. Mapping requests with @RequestMapping Problem When DispatcherServlet receives a web request, it attempts to dispatch requests to the various controllers classes that have been declared with the @Controller annotation. The dispatching process depends on the various @RequestMapping annotations declared in a controller class and its handler methods. You want to define a strategy for mapping requests using the @RequestMapping annotation. www.it-ebooks.info Chapter 4 ■ Spring @MVC 233 Solution In a Spring MVC application, web requests are mapped to handlers by one or more @RequestMapping annotations declared in controller classes. Handler mappings match URLs according to their paths relative to the context path (i.e., the web application context’s deployed path) and the servlet path (i.e., the path mapped to DispatcherServlet). So for example, in the URL http://localhost:8080/court/welcome the path to match is /welcome, as the context path is /court and there’s no servlet path—recall the servlet path declared as / in web.xml. How It Works Mapping requests by method The simplest strategy for using @RequestMapping annotations is to decorate the handler methods directly. For this strategy to work, you have to declare each handler method with the @RequestMapping annotation containing a URL pattern. If a handler’s @RequestMapping annotation matches a request’s URL, DispatcherServlet it dispatches the request to this handler for it to handle the request.   @Controller public class MemberController {    private MemberService memberService;   @Autowired public MemberController(MemberService memberService) { this.memberService = memberService; }   @RequestMapping("/member/add") public String addMember(Model model) { model.addAttribute("member", new Member()); model.addAttribute("guests", memberService.list()); return "memberList"; }   @RequestMapping(value={"/member/remove","/member/delete"}, method=RequestMethod.GET) public String removeMember( @RequestParam("memberName") String memberName) { memberService.remove(memberName); return "redirect:"; } }   This last listing illustrates how each handler method is mapped to a particular URL using the @RequestMapping annotation. The second handler method illustrates the assignment of multiple URLs, so both /member/remove and /member/delete trigger the execution of the handler method. By default, it’s assumed all incoming requests to URLs are of the HTTP GET kind. www.it-ebooks.info Chapter 4 ■ Spring @MVC 234 Mapping requests by class The @RequestMapping annotation can also be used to decorate a controller class. This allows handler methods to either forgo the use of @RequestMapping annotations, as illustrated in the ReservationQueryController controller in recipe 4-1, or use finer grained URLs with their own @RequestMapping annotation. For broader URL matching, the @ RequestMapping annotation also supports the use wildcards (i.e., *) . The following listing illustrates the use of URL wildcards in a @RequestMapping annotation, as well as finer grained URL matching on @RequestMapping annotations for handler methods.   @Controller @RequestMapping("/member/*") public class MemberController {    private MemberService memberService;   @Autowired public MemberController(MemberService memberService) { this.memberService = memberService; }   @RequestMapping("add") public String addMember(Model model) { model.addAttribute("member", new Member()); model.addAttribute("guests", memberService.list()); return "memberList"; }   @RequestMapping(value={"remove","delete"}, method=RequestMethod.GET) public String removeMember( @RequestParam("memberName") String memberName) { memberService.remove(memberName); return "redirect:"; }   @RequestMapping("display/{user}") public String removeMember( @RequestParam("memberName") String memberName, @PathVariable("user") String user) { ..... }   @RequestMapping public void memberList() { ..... }   public void memberLogic(String memberName) { ..... } }   www.it-ebooks.info Chapter 4 ■ Spring @MVC 235 Note the class level @RequestMapping annotation uses a URL wildcard: /member/* . This in turn delegates all requests under the /member/ URL to the controller’s handler methods. The first two handler methods make use of the @RequestMapping annotation. The addMember() method is invoked when an HTTP GET request is made on the /member/add URL. Whereas the removeMember() method is invoked when an HTTP GET request is made on either the /member/remove or /member/delete URL. The third handler method uses the special notation {path_variable} to specify its @RequestMapping value. By doing so, a value present in the URL can be passed as input to the handler method. Notice the handler method declares @PathVariable("user") String user. In this manner, if a request is received in the form member/display/jdoe , the handler method has access to the user variable with a jdoe value. This is mainly a facility that allows you to avoid tinkering with a handler’s request object and an approach that is especially helpful when you design RESTful web services. The fourth handler method also uses the @RequestMapping annotation, but in this case lacks a URL value. Since the class level uses the /member/* URL wildcard, this handler method is executed as a catch-all. So any URL request (e.g., /member/abcdefg or /member/randomroute) triggers this method. Note the void return value, that in turn makes the handler method default to a view by its name (i.e., memberList). The last method—memberLogic—lacks any @RequestMapping annotations, this means the method is a utility for the class and has no influence on Spring MVC. Mapping requests by HTTP request type By default, @RequestMapping annotations handle all types of incoming requests. It is however, in most cases, not wanted that the same method is executed for both a GET and POST request. To differentiate on HTTP request, it’s necessary to specify the type explicitly in the @RequestMapping annotation as follows:   @RequestMapping(value= "processUser", method = RequestMethod.POST) public String submitForm(@ModelAttribute("member") Member member, BindingResult result, Model model) { ..... }   The extent to which you require specifying a handler method’s HTTP type depends on how and what is interacting with a controller. For the most part, web browsers perform the bulk of their operations using HTTP GET and HTTP POST requests. However, other devices or applications (e.g., RESTful web services) may require support for other HTTP request types. In all, there are eight different HTTP request types: HEAD, GET, POST, PUT, DELETE, TRACE, OPTIONS and CONNECT. However, support for handling all these request types goes beyond the scope of an MVC controller, since a web server, as well as the requesting party need to support such HTTP request types. Considering the majority of HTTP requests are of the GET or POST kind, you will rarely if ever require implementing support for these additional HTTP request types. WHERE ARE THE URL EXTENSIONS LIKE .HTML AND .JSP ? You might have noticed that in all the URLs specified in @RequestMapping annotations, there was no trace of a file extension like .html or .jsp. This is good practice in accordance with MVC design, even though it’s not widely adopted. A controller should not be tied to any type of extension that is indicative of a view technology, like HTML or JSP. This is why controllers return logical views and also why matching URLs should be declared without extensions. www.it-ebooks.info Chapter 4 ■ Spring @MVC 236 In an age where it’s common to have applications serve the same content in different formats, such as XML, JSON, PDF, or XLS (Excel). It should be left to a view resolver to inspect the extension provided in a request—if any—and determine which view technology to use. In this short introduction, you’ve seen how a resolver is configured in an MVC’s configuration file (*-servlet.xml ) to map logical views to JSP files, all without every using a URL file extension like .jsp. In later recipes, you will learn how Spring MVC uses this same non-extension URL approach to serve content using different view technologies. 4-3. Intercepting Requests with Handler Interceptors Problem Servlet filters defined by the Servlet API can pre-handle and post-handle every web request before and after it’s handled by a servlet. You want to configure something with similar functions as filters in Spring’s web application context to take advantage of the container features. Moreover, sometimes you may want to pre-handle and post-handle web requests that are handled by Spring MVC handlers, and manipulate the model attributes returned by these handlers before they are passed to the views. Solution Spring MVC allows you to intercept web requests for pre-handling and post-handling through handler interceptors. Handler interceptors are configured in Spring’s web application context, so they can make use of any container features and refer to any beans declared in the container. A handler interceptor can be registered for particular URL mappings, so it only intercepts requests mapped to certain URLs. Each handler interceptor must implement the HandlerInterceptor interface, which contains three callback methods for you to implement: preHandle(), postHandle(), and afterCompletion(). The first and second methods are called before and after a request is handled by a handler. The second method also allows you to get access to the returned ModelAndView object, so you can manipulate the model attributes in it. The last method is called after the completion of all request processing (i.e., after the view has been rendered). How It Works Suppose you are going to measure each web request’s handling time by each request handler and allow the views to show this time to the user. You can create a custom handler interceptor for this purpose:   package com.apress.springrecipes.court.web; ... import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView;   public class MeasurementInterceptor implements HandlerInterceptor {   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { long startTime = System.currentTimeMillis(); request.setAttribute("startTime", startTime); return true; }   www.it-ebooks.info Chapter 4 ■ Spring @MVC 237 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { long startTime = (Long) request.getAttribute("startTime"); request.removeAttribute("startTime");   long endTime = System.currentTimeMillis(); modelAndView.addObject("handlingTime", endTime - startTime); }   public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }   In the preHandle() method of this interceptor, you record the start time and save it to a request attribute. This method should return true, allowing DispatcherServlet to proceed with request handling. Otherwise, DispatcherServlet assumes that this method has already handled the request, so DispatcherServlet returns the response to the user directly. Then, in the postHandle() method, you load the start time from the request attribute and compare it with the current time. You can calculate the total duration and then add this time to the model for passing to the view. Finally, as there is nothing for the afterCompletion() method to do, you can leave its body empty. When implementing an interface, you must implement all the methods even though you may not have a need for all of them. A better way is to extend the interceptor adapter class instead. This class implements all the interceptor methods by default. You can override only the methods that you need.   package com.apress.springrecipes.court.web; ... import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;   public class MeasurementInterceptor extends HandlerInterceptorAdapter {   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ... }   public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { ... } }   www.it-ebooks.info Chapter 4 ■ Spring @MVC 238 To register an interceptor you need to modify the WebConfiguration which was created in the first recipe. You need to have it extend WebMvcConfigurerAdapter and override the addInterceptors method. The method gives you access to the InterceptorRegistry which you can use the add interceptors. The modified class looks like the following.   @Configuration @EnableWebMvc @ComponentScan("com.apress.springrecipes.court.web") public class WebConfiguration extends WebMvcConfigurerAdapter {   @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(measurementInterceptor()); }   @Bean public MeasurementInterceptor measurementInterceptor() { return new MeasurementInterceptor(); }   ... }   Now you can show this time in welcome.jsp to verify this interceptor’s functionality. As WelcomeController doesn’t have much to do, you may likely see that the handling time is 0 milliseconds. If this is the case, you may add a sleep statement to this class to see a longer handling time.   <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>   Welcome   ...
Handling time : ${handlingTime} ms   By default HandlerInterceptors apply to all @Controllers however sometimes you want to discriminate on which controllers interceptors are applied. The namespace as well as the Java-based configuration allow for interceptors to be mapped to particular URLs. It is only a matter of configuration. Below is the Java configuration of this.   package com.apress.springrecipes.court.web.config; // Other imports omitted for brevity import com.apress.springrecipes.court.web.ExtensionInterceptor; import org.springframework.web.servlet.config.annotation.InterceptorRegistry;   @Configuration @EnableWebMvc @ComponentScan("com.apress.springrecipes.court.web") www.it-ebooks.info Chapter 4 ■ Spring @MVC 239 public class WebConfiguration extends WebMvcConfigurerAdapter {   @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(measurementInterceptor()); registry.addInterceptor(summaryReportInterceptor()).addPathPatterns("/reservationSummary*"); }   @Bean public MeasurementInterceptor measurementInterceptor() { return new MeasurementInterceptor(); }   @Bean public ExtensionInterceptor summaryReportInterceptor() { return new ExtensionInterceptor();}   @Bean public ViewResolver internalResourceViewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/jsp/"); viewResolver.setSuffix(".jsp"); return viewResolver; } }   First there is the addition of the interceptor bean summaryReportInterceptor. The structure of the backing class for this bean is identical to that of the measurementInterceptor. (i.e., it implements the HandlerInterceptor interface). However, this interceptor performs logic that should be restricted to a particular controller which is mapped to the /reservationSummary URI. When registering an interceptor we can specify which URLs it maps to, by default this takes a ANT-style expression. We pass this pattern into the addPathPatterns method, there is also an excludePathPatterns method which you can use to exclude the interceptor for certain URLs. 4-4. Resolving User Locales Problem In order for your web application to support internationalization, you have to identify each user’s preferred locale and display contents according to this locale. Solution In a Spring MVC application, a user’s locale is identified by a locale resolver, which has to implement the LocaleResolver interface. Spring MVC comes with several LocaleResolver implementations for you to resolve locales by different criteria. Alternatively, you may create your own custom locale resolver by implementing this interface. You can define a locale resolver by registering a bean of type LocaleResolver in the web application context. You must set the bean name of the locale resolver to localeResolver for DispatcherServlet to auto-detect. Note that you can register only one locale resolver per DispatcherServlet. www.it-ebooks.info Chapter 4 ■ Spring @MVC 240 How It Works Resolving Locales by an HTTP Request Header The default locale resolver used by Spring is AcceptHeaderLocaleResolver. It resolves locales by inspecting the accept-language header of an HTTP request. This header is set by a user’s web browser according to the locale setting of the underlying operating system. Note that this locale resolver cannot change a user’s locale because it is unable to modify the locale setting of the user’s operating system. Resolving Locales by a Session Attribute Another option of resolving locales is by SessionLocaleResolver. It resolves locales by inspecting a predefined attribute in a user’s session. If the session attribute doesn’t exist, this locale resolver determines the default locale from the accept-language HTTP header.   @Bean public LocaleResolver localeResolver () { SessionLocaleResolver localeResolver = new SessionLocaleResolver(); localeResolver.setDefaultLocale(new Locale("en")); return localeResolver; }   You can set the defaultLocale property for this resolver in case the session attribute doesn’t exist. Note that this locale resolver is able to change a user’s locale by altering the session attribute that stores the locale. Resolving Locales by a Cookie You can also use CookieLocaleResolver to resolve locales by inspecting a cookie in a user’s browser. If the cookie doesn’t exist, this locale resolver determines the default locale from the accept-language HTTP header.   @Bean public LocaleResolver localeResolver() { return new CookieLocaleResolver(); }   The cookie used by this locale resolver can be customized by setting the cookieName and cookieMaxAge properties. The cookieMaxAge property indicates how many seconds this cookie should be persisted. The value -1 indicates that this cookie will be invalid after the browser is closed.   @Bean public LocaleResolver localeResolver() { CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver(); cookieLocaleResolver.setCookieName("language"); cookieLocaleResolver.setCookieMaxAge(3600); cookieLocaleResolver.setDefaultLocale(new Locale("en")); return cookieLocaleResolver; }    You can also set the defaultLocale property for this resolver in case the cookie doesn’t exist in a user’s browser. This locale resolver is able to change a user’s locale by altering the cookie that stores the locale. www.it-ebooks.info Chapter 4 ■ Spring @MVC 241 Changing a User’s Locale In addition to changing a user’s locale by calling LocaleResolver.setLocale() explicitly, you can also apply LocaleChangeInterceptor to your handler mappings. This interceptor detects if a special parameter is present in the current HTTP request. The parameter name can be customized with the paramName property of this interceptor. If such a parameter is present in the current request, this interceptor changes the user’s locale according to the parameter value.   package com.apress.springrecipes.court.web.config;   import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.i18n.CookieLocaleResolver; import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; import org.springframework.web.servlet.view.InternalResourceViewResolver;   import java.util.Locale;   // Other imports omitted   @Configuration @EnableWebMvc @ComponentScan("com.apress.springrecipes.court.web") public class WebConfiguration extends WebMvcConfigurerAdapter {   @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(measurementInterceptor()); registry.addInterceptor(localeChangeInterceptor()); registry.addInterceptor(summaryReportInterceptor()).addPathPatterns("/reservationSummary*"); }   @Bean public LocaleChangeInterceptor localeChangeInterceptor() { LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor(); localeChangeInterceptor.setParamName("language"); return localeChangeInterceptor; }   @Bean public LocaleResolver localeResolver() { CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver(); cookieLocaleResolver.setCookieName("language"); cookieLocaleResolver.setCookieMaxAge(3600); cookieLocaleResolver.setDefaultLocale(new Locale("en")); return cookieLocaleResolver; } ... }   www.it-ebooks.info Chapter 4 ■ Spring @MVC 242 Now a user’s locale can be changed by any URLs with the language parameter. For example, the following two URLs change the user’s locale to English for the United States, and to German, respectively:   http://localhost:8080/court/welcome?language=en_US http://localhost:8080/court/welcome?language=de   Then you can show the HTTP response object’s locale in welcome.jsp to verify the locale interceptor’s configuration:   <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>   Welcome   ...
Locale : ${pageContext.response.locale} 4-5. Externalizing Locale-Sensitive Text Messages Problem When developing an internationalized web application, you have to display your web pages in a user’s preferred locale. You don’t want to create different versions of the same page for different locales. Solution To avoid creating different versions of a page for different locales, you should make your web page independent of the locale by externalizing locale-sensitive text messages. Spring is able to resolve text messages for you by using a message source, which has to implement the MessageSource interface. Then your JSP files can use the tag, defined in Spring’s tag library, to resolve a message given the code. How It Works You can define a message source by registering a bean of type MessageSource in the web application context. You must set the bean name of the message source to messageSource for DispatcherServlet to auto-detect. Note that you can register only one message source per DispatcherServlet. The ResourceBundleMessageSource implementation resolves messages from different resource bundles for different locales. For example, you can register it in WebConfiguration to load resource bundles whose base name is messages:   @Bean public MessageSource messageSource() { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); messageSource.setBasename("messages"); return messageSource; }   www.it-ebooks.info Chapter 4 ■ Spring @MVC 243 Then you create two resource bundles, messages.properties and messages_de.properties, to store messages for the default and German locales. These resource bundles should be put in the root of the classpath.   welcome.title=Welcome welcome.message=Welcome to Court Reservation System   welcome.title=Willkommen welcome.message=Willkommen zum Spielplatz-Reservierungssystem   Now, in a JSP file such as welcome.jsp, you can use the tag to resolve a message given the code. This tag automatically resolves the message according to a user’s current locale. Note that this tag is defined in Spring’s tag library, so you have to declare it at the top of your JSP file.   <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>   <spring:message code="welcome.title" text="Welcome" />

...   In , you can specify the default text to output when a message for the given code cannot be resolved. 4-6. Resolving Views by Names Problem After a handler has finished handling a request, it returns a logical view name. In which case the DispatcherServlet has to delegate control to a view template so the information is rendered. You want to define a strategy for DispatcherServlet to resolve views by their logical names. Solution In a Spring MVC application, views are resolved by one or more view resolver beans declared in the web application context. These beans have to implement the ViewResolver interface for DispatcherServlet to auto-detect them. Spring MVC comes with several ViewResolver implementations for you to resolve views using different strategies. www.it-ebooks.info Chapter 4 ■ Spring @MVC 244 How It Works Resolving Views Based on a template’s name and location The basic strategy of resolving views is to map them to a template’s name and location directly. The view resolver InternalResourceViewResolver maps each view name to an application’s directory by means of a prefix and a suffix declaration. To register InternalResourceViewResolver, you can declare a bean of this type in the web application context.   @Bean public ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/jsp/"); viewResolver.setSuffix(".jsp"); return viewResolver; }   For example, InternalResourceViewResolver resolves the view names welcome and reservationQuery in the following way:   welcome ' /WEB-INF/jsp/welcome.jsp reservationQuery ' /WEB-INF/jsp/reservationQuery.jsp   The type of the resolved views can be specified by the viewClass property. By default, InternalResourceViewResolver resolves view names into view objects of type JstlView if the JSTL library (i.e., jstl.jar) is present in the classpath. So, you can omit the viewClass property if your views are JSP templates with JSTL tags. InternalResourceViewResolver is simple, but it can only resolve internal resource views that can be forwarded by the Servlet API’s RequestDispatcher (e.g., an internal JSP file or a servlet). As for other view types supported by Spring MVC, you have to resolve them using other strategies. Resolving Views from an XML Configuration File Another strategy for resolving views is to declare them as Spring beans and resolve them by their bean names. You can declare the view beans in the same configuration file as the web application context, but it’s better to isolate them in a separate configuration file. By default, XmlViewResolver loads view beans from /WEB-INF/views.xml, but this location can be overridden through the location property.   Configuration @EnableWebMvc @ComponentScan("com.apress.springrecipes.court.web") public class WebConfiguration extends WebMvcConfigurerAdapter implements ResourceLoaderAware {    private ResourceLoader resourceLoader;   @Bean public ViewResolver viewResolver() { XmlViewResolver viewResolver = new XmlViewResolver(); viewResolver.setLocation(resourceLoader.getResource("/WEB-INF/court-views.nl")); return viewResolver; }   www.it-ebooks.info Chapter 4 ■ Spring @MVC 245 @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader=resourceLoader; }   Note the implementation of the ResourceLoaderAware interface, we need to load resources as the location property takes an argument of the type Resource. In a Spring XML file the conversion from String to Resource is handled for us, however when using Java-based configuration we have to do some additional work. In the court-views.xml configuration file, you can declare each view as a normal Spring bean by setting the class name and properties. In this way, you can declare any types of views (e.g., RedirectView and even custom view types).   Resolving Views from a Resource Bundle In addition to an XML configuration file, you can declare view beans in a resource bundle. ResourceBundleViewResolver loads view beans from a resource bundle in the classpath root. Note that ResourceBundleViewResolver can also take advantage of the resource bundle capability to load view beans from different resource bundles for different locales.   @Bean public ViewResolver viewResolver() { ResourceBundleViewResolver viewResolver = new ResourceBundleViewResolver(); viewResolver.setBasename("court-views"); return viewResolver; }   www.it-ebooks.info Chapter 4 ■ Spring @MVC 246 As you specify court-views as the base name of ResourceBundleViewResolver, the resource bundle is court-views.properties. In this resource bundle, you can declare view beans in the format of properties. This type of declaration is equivalent to the XML bean declaration.   welcome.(class)=org.springframework.web.servlet.view.JstlView welcome.url=/WEB-INF/jsp/welcome.jsp   reservationQuery.(class)=org.springframework.web.servlet.view.JstlView reservationQuery.url=/WEB-INF/jsp/reservationQuery.jsp   welcomeRedirect.(class)=org.springframework.web.servlet.view.RedirectView welcomeRedirect.url=welcome Resolving Views with Multiple Resolvers If you have a lot of views in your web application, it is often insufficient to choose only one view-resolving strategy. Typically, InternalResourceViewResolver can resolve most of the internal JSP views, but there are usually other types of views that have to be resolved by ResourceBundleViewResolver. In this case, you have to combine both strategies for view resolution.   @Bean public ViewResolver viewResolver() { ResourceBundleViewResolver viewResolver = new ResourceBundleViewResolver(); viewResolver.setOrder(0); viewResolver.setBasename("court-views"); return viewResolver; }   @Bean public ViewResolver internalResourceViewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setOrder(1); viewResolver.setPrefix("/WEB-INF/jsp/"); viewResolver.setSuffix(".jsp"); return viewResolver; }   When choosing more than one strategy at the same time, it’s important to specify the resolving priority. You can set the order properties of the view resolver beans for this purpose. The lower order value represents the higher priority. Note that you should assign the lowest priority to InternalResourceViewResolver because it always resolves a view no matter whether it exists or not. So, other resolvers will have no chance to resolve a view if they have lower priorities. Now the resource bundle court-views.properties should only contain the views that can’t be resolved by InternalResourceViewResolver (e.g., the redirect views):   welcomeRedirect.(class)=org.springframework.web.servlet.view.RedirectView welcomeRedirect.url=welcome www.it-ebooks.info Chapter 4 ■ Spring @MVC 247 The Redirect Prefix If you have InternalResourceViewResolver configured in your web application context, it can resolve redirect views by using the redirect prefix in the view name. Then the rest of the view name is treated as the redirect URL. For example, the view name redirect:welcome triggers a redirect to the relative URL welcome. You may also specify an absolute URL in the view name. 4-7. Views and Content Negotiation Problem You are relying on extension-less URLs in your controllers—welcome and not welcome.html or welcome.pdf. You want to devise a strategy so the correct content and type is returned for all requests. Solution When a request is received for a web application, it contains a series of properties that allow the processing framework, in this case Spring MVC, to determine the correct content and type to return to the requesting party. The main two properties include: The URL extension provided in a request• The HTTP • Accept header For example, if a request is made to a URL in the form /reservationSummary.xml, a controller is capable of inspecting the extension and delegating it to a logical view representing an XML view. However, the possibility can arise for a request to be made to a URL in the form /reservationSummary. Should this request be delegated to an XML view or an HTML view? Or perhaps some other type of view? It’s impossible to tell through the URL. But instead of deciding on a default view for such requests, a request can be inspected for its HTTP Accept header to decide what type of view is more appropriate. Inspecting HTTP Accept headers in a controller can be a messy process. So Spring MVC supports the inspection of headers through the ContentNegotiatingViewResolver allowing view delegation to be made based on either a URL file extension or HTTP Accept header value. How It Works The first thing you need to realize about Spring MVC content negotiation is that it’s configured as a resolver, just like those illustrated in the previous recipe “Resolving Views by Names.” The Spring MVC content negotiating resolver is based on the ContentNegotiatingViewResolver class. But before we describe how it works, we will illustrate how to configure and integrate it with other resolvers.   @Configuration @EnableWebMvc @ComponentScan("com.apress.springrecipes.court.web") public class WebConfiguration extends WebMvcConfigurerAdapter {   @Autowired private ContentNegotiationManager contentNegotiationManager;     www.it-ebooks.info Chapter 4 ■ Spring @MVC 248 @Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { Map mediatypes = new HashMap<>(); mediatypes.put("html", MediaType.TEXT_HTML); mediatypes.put("pdf", new MediaType("application/pdf")); mediatypes.put("xls", new MediaType("application/vnd.ms-excel")); mediatypes.put("xml", MediaType.APPLICATION_XML); mediatypes.put("json", MediaType.APPLICATION_JSON); configurer.mediaTypes(mediatypes); }   @Bean public ContentNegotiatingViewResolver contentNegotiatingViewResolver() { ContentNegotiatingViewResolver viewResolver = new ContentNegotiatingViewResolver(); viewResolver.setContentNegotiationManager(contentNegotiationManager); return viewResolver; }   First of all we need to configure content negotiation, the default configuration adds a ContentNegotiationManager, which can be configured by implementing the configureContentNegotiation method. To get access to the configured ContentNegotiationManager we can simply autowire it in our configuration class. Turning our attention back to the ContentNegotiatingViewResolver resolver. This configuration sets up the resolver to have the highest priority among all resolvers, which is necessary to make the content negotiating resolver work. The reason for this resolver having the highest priority is that it does not resolve views themselves, but rather delegates them to other view resolvers (which it automatically detects). Since a resolver that does not resolve views can be confusing, we will elaborate with an example. Let’s assume a controller receives a request for /reservationSummary.xml. Once the handler method finishes, it sends control to a logical view named reservation. At this point Spring MVC resolvers come into play, the first of which is the ContentNegotiatingViewResolver resolver, since it has the highest priority. The ContentNegotiatingViewResolver resolver first determines the media type for a request based on the following criteria: It checks a request path extension (e.g., .html, .xml, or .pdf) against the default media types (e.g., text/html) specified by the mediaTypes map in the configuration of the ContentNegotiatingManager bean. If a request path has an extension but no match can be found in the default mediaTypes section attempt is made to determine an extension’s media type using FileTypeMap belonging to Java Activation Framework. If no extension is present in a request path, the HTTP Accept header of the request is used. For the case of a request made on /reservationSummary.xml, the media type is determined in step 1 to be application/xml. However, for a request made on a URL like /reservationSummary, the media type is not determined until step 3. The HTTP Accept header contains values like Accept: text/html or Accept:application/pdf , these values help the resolver determine the media type a requester is expecting, given that no extension is present in the requesting URL. At this juncture, the ContentNegotiatingViewResolver resolver has a media type and logical view named reservation. Based on this information, an iteration is performed over the remaining resolvers—based on their order—to determine what view best matches the logical name based on the detected media type. This process allows you to have multiple logical views with the same name, each supporting a different media type (e.g., HTML, PDF, or XLS), with ContentNegotiatingViewResolver resolving which is the best match. In such cases a controller’s design is further simplified, since it won’t be necessary to hard-code the logical view necessary to create a certain media type (e.g., pdfReservation, xlsReservation, or htmlReservation), but instead a single view (e.g., reservation), letting the ContentNegotiatingViewResolver resolver determine the best match. www.it-ebooks.info Chapter 4 ■ Spring @MVC 249 A series of outcomes for this process can be the following: The media type is determined to be • application/pdf. If the resolver with the highest priority (lower order) contains a mapping to a logical view named reservation, but such a view does not support the application/pdf type, no match occurs—the lookup process continues onto the remaining resolvers. The media type is determined to be • application/pdf. The resolver with the highest priority (lower order) containing a mapping to a logical view named reservation and having support for application/pdf is matched. The media type is determined to be • text/html. There are four resolvers with a logical view named reservation, but the views mapped to the two resolvers with highest priority do not support text/html. It’s the remaining resolver containing a mapping for a view named reservation that supports text/html that is matched. This search process for views automatically take place on all the resolvers configured in an application. It’s also possible to configure—within the ContentNegotiatingViewResolver bean—default views and resolvers, in case you don’t want to fall-back on configurations made outside the ContentNegotiatingViewResolver resolver. Recipe 4-13, “Creating Excel and PDF Views,” will illustrate a controller that relies on the ContentNegotiatingViewResolver resolver to determine an application’s views. 4-8. Mapping Exceptions to Views Problem When an unknown exception occurs, your application server usually displays the evil exception stack trace to the user. Your users have nothing to do with this stack trace and complain that your application is not user friendly. Moreover, it’s also a potential security risk, as you may expose the internal method call hierarchy to users. Though a web application’s web.xml can be configured to display friendly JSP pages in case an HTTP error or class exception occur. Spring MVC supports a more robust approach to managing views for class exceptions. Solution In a Spring MVC application, you can register one or more exception resolver beans in the web application context to resolve uncaught exceptions. These beans have to implement the HandlerExceptionResolver interface for DispatcherServlet to auto-detect them. Spring MVC comes with a simple exception resolver for you to map each category of exceptions to a view. How It Works Suppose your reservation service throws the following exception due to a reservation not being available:   package com.apress.springrecipes.court.service; ... public class ReservationNotAvailableException extends RuntimeException {   private String courtName; private Date date; private int hour;   // Constructors and Getters ... }   www.it-ebooks.info Chapter 4 ■ Spring @MVC 250 To resolve uncaught exceptions, you can write your custom exception resolver by implementing the HandlerExceptionResolver interface. Usually, you’ll want to map different categories of exceptions into different error pages. Spring MVC comes with the exception resolver SimpleMappingExceptionResolver for you to configure the exception mappings in the web application context. For example, you can register the following exception resolver in WebConfiguration:   @Override public void configureHandlerExceptionResolvers(List exceptionResolvers) { exceptionResolvers.add(handlerExceptionResolver()); }   @Bean public HandlerExceptionResolver handlerExceptionResolver() { Properties exceptionMapping = new Properties(); exceptionMapping.setProperty( ReservationNotAvailableException.class.getName(), "reservationNotAvailable");   SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver(); exceptionResolver.setExceptionMappings(exceptionMapping); exceptionResolver.setDefaultErrorView("error"); return exceptionResolver; }   In this exception resolver, you define the logical view name reservationNotAvailable for ReservationNotAvailableException. You can add any number of exception classes using the exceptionMappings property, all the way down to the more general exception class java.lang.Exception. In this manner, depending on the type of class exception, a user is served a view in accordance with the exception. The property defaultErrorView, is used to define a default view named error, used in case an exception class not mapped in the exceptionMapping element is raised. Addressing the corresponding views, if the InternalResourceViewResolver is configured in your web application context, the following reservationNotAvailable.jsp page is shown in case of a reservation not being available:   <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>   Reservation Not Available   Your reservation for ${exception.courtName} is not available on at ${exception.hour}:00.   In an error page, the exception instance can be accessed by the variable ${exception}, so you can show the user more details on this exception. It’s a good practice to define a default error page for any unknown exceptions. You can use to define a default view or map a page to the key java.lang.Exception www.it-ebooks.info Chapter 4 ■ Spring @MVC 251 as the last entry of the mapping, so it will be shown if no other entry has been matched before. Then you can create this view’s JSP— error.jsp—as follows:   Error   An error has occurred. Please contact our administrator for details.   Mappings exceptions using @ExceptionHandler Instead of configuring a HandlerExceptionResolver we can also annotate a method with @ExceptionHandler. It works in a similar way as the @RequestMapping annotation.   @Controller @RequestMapping("/reservationForm") @SessionAttributes("reservation") // Command name class was used in earlier Spring versions public class ReservationFormController { @ExceptionHandler(ReservationNotAvailableException.class) public String handle(ReservationNotAvailableException ex) { return "reservationNotAvailable"; }   @ExceptionHandler public String handleDefault(Exception e) { return "error"; } ... }   We have here two methods annotated @ExceptionHandler. The first is for handling the specific ReservationNotAvailableException, the second is the general (catch-all) exception handling method. You also don’t have to specify a HandlerExceptionResolver in the WebConfiguration anymore. Methods annotated with @ ExceptionHandler can have a variety of return types (like the @RequestMapping methods), here we just return the name of the view which needs to be rendered, but we could also have returned a ModelAndView, a View etc. Although using @ExceptionHandler annotated methods is very powerful and flexible there is a drawback when you put them in controllers. Those methods will only work in the controller they are defined in, so if we have an exception occurring in another controller (for instance the WelcomeController) these methods won’t be called. Generic exception handling methods have to be moved to a separate class and that class has to be annotated with @ControllerAdvice   @ControllerAdvice public class ExceptionHandlingAdvice {   @ExceptionHandler(ReservationNotAvailableException.class) public String handle(ReservationNotAvailableException ex) { return "reservationNotAvailable"; }   www.it-ebooks.info Chapter 4 ■ Spring @MVC 252 @ExceptionHandler public String handleDefault(Exception e) { return "error"; } }   This class will apply to all controllers in the application context, hence the name @ControllerAdvice. 4-9. Handling Forms with Controllers Problem In a web application, you often have to deal with forms. A form controller has to show a form to a user and also handle the form submission. Form handling can be a complex and variable task. Solution When a user interacts with a form, it requires support for two operations from a controller. First when a form is initially requested, it asks the controller to show a form by an HTTP GET request, that renders the form view to the user. Then when the form is submitted, an HTTP POST request is made to handle things like validation and business processing for the data present in the form. If the form is handled successfully, it renders the success view to the user. Otherwise, it renders the form view again with errors. How It Works Suppose you want to allow a user to make a court reservation by filling out a form. To give you a better idea of the data handled by a controller, we will introduce the controller’s view (i.e., the form) first. Creating a form’s views Let’s create the form view reservationForm.jsp. The form relies on Spring’s form tag library, as this simplifies a form’s data binding, display of error messages and the re-display of original values entered by the user in case of errors.   <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>   Reservation Form   www.it-ebooks.info Chapter 4 ■ Spring @MVC 253
Court Name
Date
Hour
  The Spring declares two attributes. The method="post" attribute used to indicate a form performs an HTTP POST request upon submission. And the modelAttribute="reservation" attribute used to indicate the form data is bound to a model named reservation. The first attribute should be familiar to you since it’s used on most HTML forms. The second attribute will become clearer once we describe the controller that handles the form. Bear in mind the tag is rendered into a standard HTML before it’s sent to a user, so it’s not that the modelAttribute="reservation" is of use to a browser, the attribute is used as facility to generate the actual HTML form. Next, you can find the tag, used to define a location in which to place errors in case a form does not meet the rules set forth by a controller. The attribute path="*" is used to indicate the display of all errors—given the wildcard *—where as the attribute cssClass="error" is used to indicate a CSS formatting class to display the errors. Next, you can find the form’s various tags accompanied by another set of corresponding tags. These tags make use of the attribute path to indicate the form’s fields, which in this case are courtName, date and hour. The tags are bound to properties corresponding to the modelAttribute by using the path attribute. They show the user the original value of the field, which will either be the bound property value or the value rejected due to a binding error. They must be used inside the tag, which defines a form that binds to the modelAttribute by its name. Finally, you can find the standard HTML tag that generates a ‘Submit’ button and trigger the sending of data to the server, followed by the tag that closes out the form. In case the form and its data are processed correctly, you need to create a success view to notify the user of a successful reservation. The reservationSuccess.jsp illustrated next serves this purpose.   Reservation Success   www.it-ebooks.info Chapter 4 ■ Spring @MVC 254 Your reservation has been made successfully.   It’s also possible for errors to occur due to invalid values being submitted in a form. For example, if the date is not in a valid format, or an alphabetic character is presented for the hour, the controller is designed to reject such field values. The controller will then generate a list of selective error codes for each error to be returned to the form view, values that are placed inside the tag. For example, for an invalid value input in the date field, the following error codes are generated by a controller:   typeMismatch.command.date typeMismatch.date typeMismatch.java.util.Date typeMismatch   If you have a ResourceBundleMessageSource defined, you can include the following error messages in your resource bundle for the appropriate locale (e.g., messages.properties for the default locale):   typeMismatch.date=Invalid date format typeMismatch.hour=Invalid hour format   The corresponding error codes and their values are what is returned to a user if a failure occurs processing form data. Now that you know the structure of the views involved with a form, as well as the data handled by it, let’s take a look at the logic that handles the submitted data (i.e., the reservation) in a form. Creating a form’s service processing This is not the controller, but rather the service used by the controller to process the form’s data reservation. First define a make() method in the ReservationService interface:   package com.apress.springrecipes.court.service; ... public interface ReservationService { ... public void make(Reservation reservation) throws ReservationNotAvailableException; }   Then you implement this make() method by adding a Reservation item to the list that stores the reservations. You throw a ReservationNotAvailableException in case of a duplicate reservation.   package com.apress.springrecipes.court.service; ... public class ReservationServiceImpl implements ReservationService { ... public void make(Reservation reservation) throws ReservationNotAvailableException { for (Reservation made : reservations) { if (made.getCourtName().equals(reservation.getCourtName()) www.it-ebooks.info Chapter 4 ■ Spring @MVC 255 && made.getDate().equals(reservation.getDate()) && made.getHour() == reservation.getHour()) { throw new ReservationNotAvailableException( reservation.getCourtName(), reservation.getDate(), reservation.getHour()); } } reservations.add(reservation); } }   Now that you have a better understanding of the two elements that interact with a controller—a form’s views and the reservation service class—let’s create a controller to handle the court reservation form. Creating a form’s controller A controller used to handle forms makes use of practically the same annotations you’ve already used in the previous recipes. So let’s get right to the code.    package com.apress.springrecipes.court.web; ...   @Controller @RequestMapping("/reservationForm") @SessionAttributes("reservation") public class ReservationFormController {   private ReservationService reservationService;   @Autowired public ReservationFormController(ReservationService reservationService) { this.reservationService = reservationService; }   @RequestMapping(method = RequestMethod.GET) public String setupForm(Model model) { Reservation reservation = new Reservation(); model.addAttribute("reservation", reservation); return "reservationForm"; }   @RequestMapping(method = RequestMethod.POST) public String submitForm( @ModelAttribute("reservation") Reservation reservation, BindingResult result, SessionStatus status) { reservationService.make(reservation); return "redirect:reservationSuccess"; }   www.it-ebooks.info Chapter 4 ■ Spring @MVC 256 The controller starts by using the standard @Controller annotation, as well as the @RequestMapping annotation that allows access to the controller through the following URL:   http://localhost:8080/court/reservationForm   When you enter this URL in your browser, it will send an HTTP GET request to your web application. This in turn triggers the execution of the setupForm method, which is designated to attend this type of request based on its @RequestMapping annotation. The setupForm method defines a Model object as an input parameter, which serves to send model data to the view (i.e., the form). Inside the handler method, an empty Reservation object is created that is added as an attribute to the controller’s Model object. Then the controller returns the execution flow to the reservationForm view, which in this case is resolved to reservationForm.jsp (i.e., the form). The most important aspect of this last method is the addition of empty Reservation object. If you analyze the form reservationForm.jsp ,you will notice the tag declares an attribute modelAttribute="reservation" . This means that upon rendering the view, the form expects an object named reservation to be available, which is achieved by placing it inside the handler method’s Model. In fact further inspection reveals that the path values for each tag correspond to the field names belonging to the Reservation object. Since the form is being loaded for the first time, it should be evident that an empty Reservation object is expected. Another aspect that is vital to describe prior to analyzing the other controller handler method is the @Session Attributes("reservation") annotation—declared at the top of the controller class. Since it’s possible for a form to contain errors, it can be an inconvenience to lose whatever valid data was already provided by a user on every subsequent submission. To solve this problem, the @SessionAttributes is used to save a reservation field to a user’s session, so that any future reference to the reservation field is in fact made on the same reference, whether a form is submitted twice or more times. This is also the reason why only a single Reservation object is created and assigned to the reservation field in the entire controller. Once the empty Reservation object is created—inside the HTTP GET handler method—all actions are made on the same object, since it’s assigned to a user’s session. Now let’s turn our attention to submitting the form for the first time. After you have filled in the form fields, submitting the form triggers an HTTP POST request, that in turn invokes the submitForm method—on account of this method’s @RequestMapping value. The input fields declared for the submitForm method are three. The @ModelAttribute("reservation") Reservation reservation used to reference the reservation object. The BindingResult object that contains newly submitted data by the user. And the SessionStatus object used in case it’s necessary to access a user’s session. At this juncture, the handler method doesn’t incorporate validation or perform access to a user’s session, which is the purpose of the BindingResult object and SessionStatus object—I will describe and incorporate them shortly. The only operation performed by the handler method is reservationService.make(reservation);. This operation invokes the reservation service using the current state of the reservation object. Generally, controller objects are first validated prior to performing this type of operation on them. Finally, note the handler method returns a view named redirect:reservationSuccess. The actual name of the view in this case is reservationSuccess, which is resolved to the reservationSuccess.jsp page you created earlier. The redirect: prefix in the view name is used to avoid a problem known as duplicate form submission. When you refresh the web page in the form success view, the form you just submitted is resubmitted again. To avoid this problem, you can apply the post/redirect/get design pattern, which recommends redirecting to another URL after a form submission is handled successfully, instead of returning an HTML page directly. This is the purpose of prefixing a view name with redirect:. Initializing a model attribute object and pre-populating a form with values The form is designed to let users make reservations. However, if you analyze the Reservation domain class, you will note the form is still missing two fields in order to create a complete reservation object. One of these fields is the player field, which corresponds to a Player object. Per the Player class definition, a Player object has both a name and phone fields. www.it-ebooks.info Chapter 4 ■ Spring @MVC 257 So can the player field be incorporated into a form view and controller? Let’s analyze the form view first:   Reservation Form
...
Player Name
Player Phone
  Using a straightforward approach, you add two additional tags used to represent the Player object’s fields. Though these forms declaration are simple, you also need to perform modifications to the controller. Recall that by using tags, a view expects to have access to model objects passed by the controller, that match the path value for tags. Though the controller’s HTTP GET handler method returns an empty reservation Reservation to this last view, the player property is null, so it causes an exception when rendering the form. To solve this problem, you have to initialize an empty Player object and assign it to the Reservation object returned to the view.   @RequestMapping(method = RequestMethod.GET) public String setupForm( @RequestParam(required = false, value = "username") String username, Model model) { Reservation reservation = new Reservation(); reservation.setPlayer(new Player(username, null)); model.addAttribute("reservation", reservation); return "reservationForm"; }   In this case, after creating the empty Reservation object, the setPlayer method is used to assign it an empty Player object. Further note that the creation of the Person object relies on the username value. This particular value is obtained from the @RequestParam input value which was also added to the handler method. By doing so, the Player object can be created with a specific username value passed in as a request parameter, resulting in the username form field being pre-populated with this value. www.it-ebooks.info Chapter 4 ■ Spring @MVC 258 So for example, if a request to the form is made in the following manner:   http://localhost:8080/court/reservationForm?username=Roger   This allows the handler method to extract the username parameter to create the Player object, in turn pre-populating the form’s username form field with a Roger value. It’s worth noting that the @RequestParam annotation for the username parameter uses the property required=false, this allows a form request to be processed even if such a request parameter is not present. Providing form Reference Data When a form controller is requested to render the form view, it may have some types of reference data to provide to the form (e.g., the items to display in an HTML selection). Now suppose you want to allow a user to select the sport type when reserving a court—which is the final unaccounted field for the Reservation class.   Reservation Form
...
Sport Type
  The tag provides a way to generate a drop-down list of values passed to the view by the controller. Thus the form represents the sportType field as a set of HTML —that require a user to introduce text values. Next, let’s take a look at how the controller assigns the sportType field as a model attribute, the process is a little different than the previous fields. www.it-ebooks.info Chapter 4 ■ Spring @MVC 259 First let’s define the getAllSportTypes() method in the ReservationService interface for retrieving all available sport types:   package com.apress.springrecipes.court.service; ... public interface ReservationService { ... public List getAllSportTypes(); }   Then you can implement this method by returning a hard-coded list:   package com.apress.springrecipes.court.service; ... public class ReservationServiceImpl implements ReservationService { ... public static final SportType TENNIS = new SportType(1, "Tennis"); public static final SportType SOCCER = new SportType(2, "Soccer");   public List getAllSportTypes() { return Arrays.asList(new SportType[] { TENNIS, SOCCER }); } }   Now that you have an implementation that returns a hard-coded list of SportType objects, let’s take a look at how the controller associates this list for it to be returned to the form view.   package com.apress.springrecipes.court.service; ..... @ModelAttribute("sportTypes") public List populateSportTypes() { return reservationService.getAllSportTypes(); }   @RequestMapping(method = RequestMethod.GET) public String setupForm( @RequestParam(required = false, value = "username") String username, Model model) { Reservation reservation = new Reservation(); reservation.setPlayer(new Player(username, null)); model.addAttribute("reservation", reservation); return "reservationForm"; }   Notice that the setupForm handler method charged with returning the empty Reservation object to the form view remains unchanged. The new addition and what is responsible for passing a SportType list as a model attribute to the form view is the method decorated with the @ModelAttribute("sportTypes") annotation. The @ModelAttribute annotation is used to define global model attributes, available to any returning view used in handler methods. In the same way a handler method declares a Model object as an input parameter and assigns attributes that can be accessed in the returning view. www.it-ebooks.info Chapter 4 ■ Spring @MVC 260 Since the method decorated with the @ModelAttribute("sportTypes") annotation has a return type of List and makes a call to reservationService.getAllSportTypes(), the hard-coded TENNIS and SOCCER SportType objects are assigned to the model attribute named sportTypes. With this last model attribute used in the form view to populate a drop down list (i.e., tag). Binding Properties of Custom Types When a form is submitted, a controller binds the form field values to model object’s properties of the same name, in this case a Reservation object. However, for properties of custom types, a controller is not able to convert them unless you specify the corresponding property editors for them. For example, the sport type selection field only submits the selected sport type ID—as this is the way HTML
  Notice the hidden input which contains a CSFR token (Cross Site Forgery Request token). This is to prevent malicious websites or javascript code to post to our URL. When using Spring Security with Java Config this is enabled by default. It can be disabled with http.csfr().disable() in the SecurityConfig class. There are two final configuration pieces left. First this configuration needs to be loaded and second a filter needs to be registered to apply the security to our request. For this modify the SocialWebApplicationInitializer class.   package com.apress.springrecipes.social;   import com.apress.springrecipes.social.config.SecurityConfig; import com.apress.springrecipes.social.config.SocialConfig; import com.apress.springrecipes.social.config.WebConfig; import org.springframework.web.filter.DelegatingFilterProxy; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;   import javax.servlet.Filter;   public class SocialWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class[] getRootConfigClasses() { return new Class[]{SecurityConfig.class, SocialConfig.class}; }   www.it-ebooks.info Chapter 6 ■ Spring Social 325 @Override protected Filter[] getServletFilters() { DelegatingFilterProxy springSecurityFilterChain = new DelegatingFilterProxy(); springSecurityFilterChain.setTargetBeanName("springSecurityFilterChain"); return new Filter[]{springSecurityFilterChain}; } ... }   First notice that the SecurityConfig class is added to the getRootConfigClasses method, this will take care of the configuration class being loaded. Next the getServletFilters method is added. This method is used to register filters to requests that are going to be handled by the DispatcherServlet. Spring Security, by default, registers a Filter in the application context named springSecurityFilterChain. To have this executed you need to add a DelegatingFilterProxy. The DelegatingFilterProxy will look up a bean of the type Filter for the specified targetBeanName. Using Spring Security to Obtain the Username In the previous recipes you used a UserIdSource implementation that returned a static username. If you have an application that is already using Spring Security you could use the AuthenticationNameUserIdSource which uses the SecurityContext (from Spring Security) to obtain the username of the authenticated current user. That username in turn is used to store and lookup the users connections with the different service providers.   @Configuration @EnableSocial @PropertySource("classpath:/application.properties") public class SocialConfig extends SocialConfigurerAdapter {   @Override public UserIdSource getUserIdSource() { return new AuthenticationNameUserIdSource(); }   ... }   Notice the construction of the AuthenticationNameUserIdSource. This is all that is needed to be able to retrieve the username from Spring Security. It will do a lookup of the Authentication object from the SecurityContext and return the name property of the Authentication. When restarting the application you will be prompted with a login form. Now login as user1 with password user1. Using Spring Social for Sign In Letting the current user connect his social networks is nice. It would be better if a user could use his social network account(s) to sign in to the application. Spring Social provides tight integration with Spring Security to enable this. There are a couple of additional parts that need to be setup for this. www.it-ebooks.info Chapter 6 ■ Spring Social 326 First Spring Social needs to be integrated with Spring Security. For this the SpringSocialConfigurer can be used and applied to the Spring Security configuration.   @Configuration @EnableWebMvcSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter {   @Override protected void configure(HttpSecurity http) throws Exception { ... http.apply(new SpringSocialConfigurer()); } ... }   The SpringSocialConfigurer needs a SocialUserDetailsService. This is used to lookup a user based on the user’s id. For this recipe an implementation that delegates to a normal UserDetailsService will do. The SpringSocialConfigurer registers the SocialAuthenticationFilter which starts the authentication flow with the selected service provider. The filter listens, by default, to the /auth/{provider} URL, where {provider} points to the service provider being used (i.e., Twitter, Facebook, etc.).   package com.apress.springrecipes.social.security;   import org.springframework.dao.DataAccessException; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.social.security.SocialUser; import org.springframework.social.security.SocialUserDetails; import org.springframework.social.security.SocialUserDetailsService; import org.springframework.util.Assert;   public class SimpleSocialUserDetailsService implements SocialUserDetailsService {   private final UserDetailsService userDetailsService;   public SimpleSocialUserDetailsService(UserDetailsService userDetailsService) { Assert.notNull(userDetailsService, "UserDetailsService cannot be null."); this.userDetailsService = userDetailsService; }   @Override public SocialUserDetails loadUserByUserId(String userId) throws UsernameNotFoundException, DataAccessException { UserDetails user = userDetailsService.loadUserByUsername(userId); return new SocialUser(user.getUsername(), user.getPassword(), user.getAuthorities()); } }   www.it-ebooks.info Chapter 6 ■ Spring Social 327 Next add the links for your configured service providers to the signin page.   <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> ...

Sign in with Twitter

Sign in with Facebook

    The SimpleSocialUserDetailsService delegates the actual lookup to a UserDetailsService which is passed in through the constructor. When a user is retrieved it uses the retrieved information to construct a SocialUser instance. Finally this bean needs to be added to the configuration.   @Configuration @EnableWebMvcSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter {   @Bean public SocialUserDetailsService socialUserDetailsService(UserDetailsService userDetailsService) { return new SimpleSocialUserDetailsService(userDetailsService); } ... }   This will all allow a user to sign in with his social networking accounts; however, the application needs to know which user the account belongs to. If a user cannot be located for the specific social network a user needs to be created. Basically the application needs a way for users to sign up for the application. By default the SocialAuthenticationFilter redirects the user to the /signup url. You can create a controller which is attached to this URL and renders a form allowing the user to create an account.   package com.apress.springrecipes.social.web;   import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.provisioning.UserDetailsManager; import org.springframework.social.connect.Connection; import org.springframework.social.connect.web.ProviderSignInUtils; import org.springframework.social.security.SocialUser; www.it-ebooks.info Chapter 6 ■ Spring Social 328 import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.context.request.WebRequest;   import java.util.Collections;   @Controller public class SignupController {   private final ProviderSignInUtils providerSignInUtils = new ProviderSignInUtils(); private final UserDetailsManager userDetailsManager;   @Autowired public SignupController(UserDetailsManager userDetailsManager) { this.userDetailsManager = userDetailsManager; }   @RequestMapping(value="/signup", method=RequestMethod.GET) public SignupForm signupForm(WebRequest request) { Connection connection = providerSignInUtils.getConnectionFromSession(request); if (connection != null) { return SignupForm.fromProviderUser(connection.fetchUserProfile()); } else { return new SignupForm(); } }   @RequestMapping(value="/signup", method=RequestMethod.POST) public String signup(@Validated SignupForm form, BindingResult formBinding, WebRequest request) { if (formBinding.hasErrors()) { return null; } SocialUser user = createUser(form, formBinding); if (user != null) { SecurityContextHolder.getContext().setAuthentication( new UsernamePasswordAuthenticationToken(user.getUsername(), null, null)); providerSignInUtils.doPostSignUp(user.getUsername(), request); return "redirect:/"; } return null; }   www.it-ebooks.info Chapter 6 ■ Spring Social 329 private SocialUser createUser(SignupForm form, BindingResult errors) { SocialUser user = new SocialUser( form.getUsername(), form.getPassword(), Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"))); userDetailsManager.createUser(user); return user; } }   First the signupForm method will be called as the initial request will be a GET request to the /signup URL. The signupForm method checks if a connection attempt has been done. This is delegated to the ProviderSignInUtils provided by Spring Social. If that is the case the retrieved UserProfile is used to prepopulate a SignupForm.   package com.apress.springrecipes.social.web;   import org.springframework.social.connect.UserProfile;   public class SignupForm {   private String username;   private String password;   public String getUsername() { return username; }   public void setUsername(String username) { this.username = username; }   public String getPassword() { return password; }   public void setPassword(String password) { this.password = password; }   public static SignupForm fromProviderUser(UserProfile providerUser) { SignupForm form = new SignupForm(); form.setUsername(providerUser.getUsername()); return form; } }   www.it-ebooks.info Chapter 6 ■ Spring Social 330 The HTML form used for filling in the two fields.   <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> Sign Up

Sign Up

    Note■■ there is no hidden input for the CSFR tag here. Spring Security integrates tightly with Spring MVC and this field will be added automatically when using the Spring Framework form tags. After the user filled out the form the signup method will be called. This will create a user with the given username and password. After the user is created a Connection is added for the entered username. Now that the connection has been made the user is logged in to the application and on subsequent visits can use the social network connection to log in to the application. Summary In this chapter you explored Spring Social. The first step taken was to register an application with a service provider and use the generated API key and secret to connect the application to that service provider. Next you looked into connecting a user’s account to the application so that it can be used to access user information; however, this will also allow you to use the service provider’s API. For Twitter you could query a timeline or look at someone’s friends. To make the connections to the service providers more useful they are stored in a JDBC-based storage. Finally you looked at how Spring Social can integrate with Spring Security and how it can be used to allow a service provider to sign in to your application. www.it-ebooks.info 331 Chapter 7 Spring Security In this chapter, you will learn how to secure applications using the Spring Security framework, a subproject of the Spring framework. Spring Security was initially known as Acegi Security, but its name has been changed since joining with the Spring Portfolio projects. Spring Security can be used to secure any Java application, but it’s mostly used for web-based applications. Web applications, especially those that can be accessed through the Internet, are vulnerable to hacker attacks if they are not secured properly. If you’ve already used Spring Security, be advised there have been several changes introduced in the 3.0 release of the Spring Security framework, which was released almost in tandem with the 3.0 release of the Spring framework (i.e., core). These changes include feature enhancements like support for annotations and OpenID, as well as a restructuring that includes class name changes and package partitioning (i.e., multiple JARs). This is especially important if you are running Spring Security 2.x code, on which the first edition of the book was based. If, on the other hand, you’ve never handled security in an application, there are several terms and concepts that you must understand first. Authentication is the process of verifying a principal’s identity against what it claims to be. A principal can be a user, a device, or a system, but most typically, it’s a user. A principal has to provide evidence of identity to be authenticated. This evidence is called a credential, which is usually a password when the target principal is a user. Authorization is the process of granting authority to an authenticated user so that this user is allowed to access particular resources of the target application. The authorization process must be performed after the authentication process. Typically, authorities are granted in terms of roles. Access control means controlling access to an application’s resources. It entails making a decision on whether a user is allowed to access a resource. This decision is called an access control decision, and it’s made by comparing the resource’s access attributes with the user’s granted authorities or other characteristics. After finishing this chapter, you will understand basic security concepts and know how to secure your web applications at the URL access level, the method invocation level, the view-rendering level, and the domain object level. 7-1. Securing URL Access Problem Many web applications have some particular URLs that are critically important and private. You must secure these URLs by preventing unauthorized access to them. Solution Spring Security enables you to secure a web application’s URL access in a declarative way through simple configuration. It handles security by applying servlet filters to HTTP requests. You can configure these filters in Spring’s bean configuration files using XML elements defined in the Spring Security schema. However, as servlet filters must be registered in the web deployment descriptor to take effect, you have to register a DelegatingFilterProxy instance in the web deployment descriptor, which is a servlet filter that delegates request filtering to a filter in Spring’s application context. www.it-ebooks.info Chapter 7 ■ Spring Security 332 Spring Security allows you to configure web application security through the element. If your web application’s security requirements are straightforward and typical, you can set this element’s auto-config attribute to true so that Spring Security will automatically register and configure several basic security services, including the following: Form-based login service: This provides a default page that contains a login form for users to • log into this application. Logout service: This provides a handler mapped with a URL for users to log out of this • application. HTTP Basic authentication: This can process the Basic authentication credentials presented • in HTTP request headers. It can also be used for authenticating requests made with remoting protocols and web services. Anonymous login: This assigns a principal and grants authorities to an anonymous user so • that you can handle an anonymous user like a normal user. Remember-me support: This can remember a user’s identity across multiple browser sessions, • usually by storing a cookie in the user’s browser. Servlet API integration: This allows you to access security information in your web • application via standard Servlet APIs, such as HttpServletRequest.isUserInRole() and HttpServletRequest.getUserPrincipal(). With these security services registered, you can specify the URL patterns that require particular authorities to access. Spring Security will perform security checks according to your configurations. A user must log into an application before accessing the secure URLs, unless these URLs are opened for anonymous access. Spring Security provides a set of authentication providers for you to choose from. An authentication provider authenticates a user and returns the authorities granted to this user. How It Works Suppose you are going to develop an online message board application for users to post their messages on. First, you create the domain class Message with three properties: author, title, and body:   package com.apress.springrecipes.board.domain;   public class Message {   private Long id; private String author; private String title; private String body;   // Getters and Setters ... }   www.it-ebooks.info Chapter 7 ■ Spring Security 333 Next, you define the operations of your message board in a service interface, including listing all messages, posting a message, deleting a message, and finding a message by its ID:   package com.apress.springrecipes.board.service; ... public interface MessageBoardService {   public List listMessages(); public void postMessage(Message message); public void deleteMessage(Message message); public Message findMessageById(Long messageId); }   For testing purposes, let’s implement this interface by using a list to store the posted messages. You can use the message posting time (in milliseconds) as a message’s identifier. You also have to declare the postMessage() and deleteMessage() method as synchronized to make them thread-safe.   package com.apress.springrecipes.board.service; ... public class MessageBoardServiceImpl implements MessageBoardService {   private Map messages = new LinkedHashMap();   public List listMessages() { return new ArrayList(messages.values()); }   public synchronized void postMessage(Message message) { message.setId(System.currentTimeMillis()); messages.put(message.getId(), message); }   public synchronized void deleteMessage(Message message) { messages.remove(message.getId()); }   public Message findMessageById(Long messageId) { return messages.get(messageId); } } Setting Up a Spring MVC Application That Uses Spring Security To develop this application using Spring MVC as the web framework and Spring Security as the security framework, you first create the following directory structure for your web application. www.it-ebooks.info Chapter 7 ■ Spring Security 334 Note■■   Before using Spring Security, you have to have the relevant Spring Security JARs on your classpath. If you are using Maven, add the following dependencies to your Maven project. We include here a few extra dependencies that you will need on a case-by-case basis, including LDAP support and ACL support. In this book, we have used a variable - ${spring.security.version} to extract the version out. In this book, we are building against 3.2.4.RELEASE.   org.springframework.security spring-security-core ${spring.security.version} org.springframework.security spring-security-ldap ${spring.security.version} org.springframework.security spring-security-config ${spring.security.version} org.springframework.security spring-security-web ${spring.security.version} org.springframework.security spring-security-taglibs ${spring.security.version} org.springframework.security spring-security-acl ${spring.security.version}   The Spring configurations for this application are separated into three different files: board-security.xml, board-service.xml, and board-servlet.xml. Each of them configures a particular layer. www.it-ebooks.info Chapter 7 ■ Spring Security 335 Creating the Configuration Files In the web deployment descriptor (i.e., web.xml), you register ContextLoaderListener to load the root application context at startup and Spring MVC’s DispatcherServlet to dispatch requests:   contextConfigLocation /WEB-INF/board-service.xml org.springframework.web.context.ContextLoaderListener board org.springframework.web.servlet.DispatcherServlet board /   If the root application context’s configuration file doesn’t have the default name (i.e., applicationContext.xml), or if you configure it with multiple configuration files, you’ll have to specify the file locations in the contextConfigLocation context parameter. Also note that you have mapped the URL pattern / to DispatcherServlet, meaning everything under the application’s root directory will be handled by this servlet. In the web layer configuration file (i.e., board-servlet.xml), you define a view resolver to resolve view names into JSP files located in the /WEB-INF/jsp/ directory. Later, you will have to configure your controllers in this file.     www.it-ebooks.info Chapter 7 ■ Spring Security 336   In the service layer configuration file (i.e., board-service.xml), you have to declare only the message board service:     Creating the Controllers and Page Views Suppose you have to implement a function for listing all messages posted on the message board. The first step is to create the following controller.   package com.apress.springrecipes.board.web; ...   @Controller @RequestMapping("/messageList*") public class MessageListController {   private MessageBoardService messageBoardService;   @Autowired public MessageListController(MessageBoardService messageBoardService) { this.messageBoardService = messageBoardService; }   @RequestMapping(method = RequestMethod.GET) public String generateList(Model model) { List messages = java.util.Collections.emptyList(); messages = messageBoardService.listMessages(); model.addAttribute("messages",messages); return "messageList"; } }   The controller is mapped to a URL in the form /messageList. The controller’s main method—generateList() —obtains a list of messages from the messageBoardService, saves it to the model object under the messages named, and returns control to a logical view named messageList. In accordance with Spring MVC conventions, this last www.it-ebooks.info Chapter 7 ■ Spring Security 337 logical view is mapped to the JSP /WEB-INF/jsp/messageList.jsp showing all the messages passed from the controller:   <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>   Message List
Author ${message.author}
Title ${message.title}
Body ${message.body}
Delete

Post   Another function you have to implement is for users to post messages on the message board. You create the following form controller for this purpose:   package com.apress.springrecipes.board.web; ...   @Controller @RequestMapping("/messagePost*") public class MessagePostController {   private MessageBoardService messageBoardService;   @Autowired public void MessagePostController(MessageBoardService messageBoardService) { this.messageBoardService = messageBoardService; } www.it-ebooks.info Chapter 7 ■ Spring Security 338 @RequestMapping(method=RequestMethod.GET) public String setupForm(Model model) { Message message = new Message(); model.addAttribute("message",message); return "messagePost"; }   @RequestMapping(method=RequestMethod.POST) public String onSubmit(@ModelAttribute("message") Message message, BindingResult result) { if (result.hasErrors()) { return "messagePost"; } else { messageBoardService.postMessage(message); return "redirect:messageList"; } } }   A user must have logged into the message board before posting a message. You can get a user’s login name with the getRemoteUser() method defined in HttpServletRequest. This login name will be used as the message’s author name. You then create the form view /WEB-INF/jsp/messagePost.jsp with Spring’s form tags for users to input message contents:   <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>   Message Post
Title
Body
  www.it-ebooks.info Chapter 7 ■ Spring Security 339 The last function is to allow a user to delete a posted message by clicking the Delete link on the message list page. You create the following controller for this function:   package com.apress.springrecipes.board.web; ...   @Controller @RequestMapping("/messageDelete*") public class MessageDeleteController {   private MessageBoardService messageBoardService;   @Autowired public void MessageDeleteController(MessageBoardService messageBoardService) { this.messageBoardService = messageBoardService; }   @RequestMapping(method= RequestMethod.GET) public String messageDelte(@RequestParam(required = true,  value = "messageId") Long messageId, Model model) { Message message = messageBoardService.findMessageById(messageId); messageBoardService.deleteMessage(message); model.addAttribute("messages", messageBoardService.listMessages()); return "redirect:messageList"; } }   Now, you can deploy this application to a web container (e.g., Apache Tomcat 7.0). By default, Tomcat listens on port 8080, so if you deploy your application to the board context path, you can list all posted messages with the following URL:   http://localhost:8080/board/messageList.htm   Up to this time, you haven’t configured any security service for this application, so you can access it directly without logging into it. Securing URL Access Now, let’s secure this web application’s URL access with Spring Security. First, you have to configure a DelegatingFilterProxy instance in web.xml to delegate HTTP request filtering to a filter defined in Spring Security:   contextConfigLocation /WEB-INF/board-service.xml /WEB-INF/board-security.xml ... www.it-ebooks.info Chapter 7 ■ Spring Security 340 springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy springSecurityFilterChain /* ...   The responsibility of DelegatingFilterProxy is simply to delegate HTTP request filtering to a Spring bean that implements the javax.servlet.Filter interface. By default, it delegates to a bean whose name is the same as its property, but you can override the bean name in its targetBeanName init parameter. As Spring Security will automatically configure a filter chain with the name springSecurityFilterChain when you enable web application security, you can simply use this name for your DelegatingFilterProxy instance. Although you can configure Spring Security in the same configuration file as the web and service layers, it’s better to separate the security configurations in an isolated file (e.g., board-security.xml). Inside web.xml, you have to add the file’s location to the contextConfigLocation context parameter for ContextLoaderListener to load it at startup. Then, you create it with the following content:     www.it-ebooks.info Chapter 7 ■ Spring Security 341 You may find that this file looks a bit different from a normal bean configuration file. Normally, the default namespace of a bean configuration file is beans, so you can use the and elements without the beans prefix. However, if you use this style to declare the Spring Security services, all security elements must be appended with the security prefix. Because the elements in a security configuration file are mostly Spring Security’s, you can define security as the default namespace instead, so you can use them without the security prefix. If you do it this way, however, when you declare normal Spring beans in this file, you have to include the beans prefix for the and elements. Inside the configuration element, you can restrict access to particular URLs with one or more elements. Each element specifies a URL pattern and a set of access attributes required to access the URLs. Remember that you must always include a wildcard at the end of a URL pattern. Failing to do so will make the URL pattern unable to match a URL that has request parameters. As a result, hackers could easily skip the security check by appending an arbitrary request parameter. Access attributes are compared with a user’s authorities to decide if this user can access the URLs. In most cases, access attributes are defined in terms of roles. For example, users with the ROLE_USER role, or anonymous users, who have the ROLE_ANONYMOUS role by default, are able to access the URL /messageList to list all messages. However, a user must have the ROLE_USER role to post a new message via the URL /messagePost. Only an administrator who has the ROLE_ADMIN role can delete messages via /messageDelete. You can configure authentication services in the element, which is nested inside the element. Spring Security supports several ways of authenticating users, including authenticating against a database or an LDAP repository. It also supports defining user details in directly for simple security requirements. You can specify a username, a password, and a set of authorities for each user. Now, you can redeploy this application to test its security configurations. You can enter the request path /messageList to list all posted messages as usual, because it’s open to anonymous users. But if you click the link to post a new message, you will be redirected to the default login page generated by Spring Security. You must log into this application with a correct username and password to post a message. Finally, to delete a message, you must log in as an administrator. Using Java to configure Spring Security Until now we have used XML to configure our application. However as of Spring Security 3.2 we can also use a full Java based configuration approach and the Servlet 3.0 specification also allows us the remove the web.xml, which allows us to do everything in Java. The web.xml we created in the previous samples can be replaced with a ServletContainerInitializer which gives us the opportunity to configure the ServletContext ourselves. Spring provides us with the SpringServletContainerInitializer which allows us to add one or more WebApplicationInitializer which we can use to add servlets, filters, and listeners to the ServletContext.   package com.apress.springrecipes.board.web;   import com.apress.springrecipes.board.config.MessageBoardConfiguration; import com.apress.springrecipes.board.web.config.MessageBoardSecurityConfiguration; import com.apress.springrecipes.board.web.config.MessageBoardWebConfiguration; import org.springframework.core.annotation.Order; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;   @Order(1) public class MessageBoardApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {   @Override protected Class[] getRootConfigClasses() { return new Class[] {MessageBoardConfiguration.class, MessageBoardSecurityConfiguration.class}; }   www.it-ebooks.info Chapter 7 ■ Spring Security 342 @Override protected Class[] getServletConfigClasses() { return new Class[] {MessageBoardWebConfiguration.class}; }   @Override protected String[] getServletMappings() { return new String[] { "/"}; } }   And the security WebApplicationInitializer.   package com.apress.springrecipes.board.web;   import org.springframework.core.annotation.Order; import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;   @Order(2) public class MessageBoardSecurityInitializer extends AbstractSecurityWebApplicationInitializer { }   The MessageBoardApplicationInitializer takes care of creating a ContextLoaderListener and DispatcherServlet the code that does this is part of the AbstractAnnotationConfigDispatcherServletInitializer class. We only need to provide the configuration classes for the listener and servlet for this we need to implement the getRootConfigClasses and getServletConfigClasses method. Finally we need to supply the servlet mapping for this we let the getServletMappings method return /. We also need to register a DelegatingFilterProxy to make Spring Security work, we can extend the AbstractSecurityWebApplicationInitializer base class for this. This class is provided by Spring Security, this class is also capable of constructing a ContextLoaderListener which can be convenient in an environment where you don’t use Spring MVC but another web technology like JSF. That way you can work with a single WebApplicationInitializer. Notice the @Order annotation on both classes it is important that the DelegatingFilterProxy is the first Filter in the chain of filters. However the Spring provided AbstractAnnotationConfigDispatcherServletInitializer registers any filters before all already configured filters. To make sure that Spring Security is setup correctly it should be the last WebApplicationInitializer to execute so that it can register the DelegatingFilterProxy as the first filter without the risk of being overwritten later on.   package com.apress.springrecipes.board.config;   import com.apress.springrecipes.board.service.MessageBoardService; import com.apress.springrecipes.board.service.MessageBoardServiceImpl; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;   @Configuration public class MessageBoardConfiguration {   @Bean public MessageBoardService messageBoardService() { return new MessageBoardServiceImpl(); } }   www.it-ebooks.info Chapter 7 ■ Spring Security 343 The configuration class for the service isn’t spectacular; it only contains the bean definition for our service class.   package com.apress.springrecipes.board.web.config;   import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.view.InternalResourceViewResolver;   @Configuration @ComponentScan("com.apress.springrecipes.board.web") @EnableWebMvc public class MessageBoardWebConfiguration {   @Bean public ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/jsp/"); viewResolver.setSuffix(".jsp"); return viewResolver; } }   To configure out DispatcherServlet we have the MessageBoardWebConfiguration class. In the MessageBoardWebConfiguration class you define a view resolver to resolve view names into JSP files located in the /WEB-INF/jsp/ directory. We have a @ComponentScan (analogous to ) to pick up our @Controller annotated beans. Finally we specific that we want to use enable Spring MVC by placing the @EnableWebMvc annotation. To configure Spring Security we need to have a configuration class which is annotated with @EnableWebSecurity and for further configuration needs to be a WebSecurityConfigurer. The latter is needed to correctly setup the security configuration. Although you could implement your own class Spring Security provides WebSecurityConfigurerAdapter as a base class which takes away a lot of ground work.   package com.apress.springrecipes.board.web.config;   import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders. AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration. WebSecurityConfigurerAdapter;   @Configuration @EnableWebSecurity public class MessageBoardSecurityConfiguration extends WebSecurityConfigurerAdapter {   www.it-ebooks.info Chapter 7 ■ Spring Security 344 @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/messageList*").hasAnyRole("USER", "ANONYMOUS") .antMatchers("/messagePost*").hasRole("USER") .antMatchers("/messageDelete*").hasRole("ADMIM") .and() .formLogin();   }   @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("admin").password("secret").authorities("ROLE_ADMIN","ROLE_USER") .and().withUser("user1").password("1111").authorities("ROLE_USER"); } }   We are overriding two configure methods from the superclass. The first is the one that takes an HttpSecurity as argument. This is analogous to the element in xml. We use it to configure the protection for our URLs with the antMatchers on conjunction with the hasAnyRole or hasRole method. Notice the omission of the ROLE_ prefix the configured prefix is automatically added to the roles. The configure method taking the AuthenticationManagerBuilder is used to setup the AuthenticationManager, just like the in XML. For now we are going to use in memory authentication this is done by using the inMemoryAuthentication method and configuring the users with the withUser method. 7-2. Logging In to Web Applications Problem A secure application requires its users to log in before they can access certain secure functions. This is especially important for web applications running on the open Internet, because hackers can easily reach them. Most web applications have to provide a way for users to input their credentials to log in. Solution Spring Security supports multiple ways for users to log into a web application. It supports form-based login by providing a default web page that contains a login form. You can also provide a custom web page as the login page. In addition, Spring Security supports HTTP Basic authentication by processing the Basic authentication credentials presented in HTTP request headers. HTTP Basic authentication can also be used for authenticating requests made with remoting protocols and web services. Some parts of your application may allow for anonymous access (e.g., access to the welcome page). Spring Security provides an anonymous login service that can assign a principal and grant authorities to an anonymous user so that you can handle an anonymous user like a normal user when defining security policies. Spring Security also supports remember-me login, which is able to remember a user’s identity across multiple browser sessions so that a user needn’t log in again after logging in for the first time. www.it-ebooks.info Chapter 7 ■ Spring Security 345 How It Works To help you better understand the various login mechanisms in isolation, let’s first disable HTTP auto-configuration by removing the auto-config attribute:     Note that the login services introduced next will be registered automatically if you enable HTTP auto-config. However, if you disable HTTP auto-config or you want to customize these services, you have to configure the corresponding XML elements explicitly. HTTP Basic Authentication The HTTP Basic authentication support can be configured via the element. When HTTP Basic authentication is required, a browser will typically display a login dialog or a specific login page for users to log in.   ...   Or using the httpBasic method when using Java config.   @Configuration @EnableWebSecurity public class MessageBoardSecurityConfiguration extends WebSecurityConfigurerAdapter {   @Override protected void configure(HttpSecurity http) throws Exception { http ... .httpBasic(); } }   Note that when HTTP Basic authentication and form-based login are enabled at the same time, the latter will be used. So, if you want your web application users to log in with this authentication type, you should not enable form-based login. Form-Based Login The form-based login service will render a web page that contains a login form for users to input their login details and process the login form submission. It’s configured via the element:   ...   www.it-ebooks.info Chapter 7 ■ Spring Security 346 Or when using Java config the formLogin method.  @Configuration @EnableWebSecurity public class MessageBoardSecurityConfiguration extends WebSecurityConfigurerAdapter {  @Override protected void configure(HttpSecurity http) throws Exception { http ... .formLogin(); } }  By default, Spring Security automatically creates a login page and maps it to the URL /spring_security_login. So, you can add a link to your application (e.g., in messageList.jsp) referring to this URL for login:  ">Login  If you don’t prefer the default login page, you can provide a custom login page of your own. For example, you can create the following login.jsp file in the root directory of the web application. Note that you shouldn’t put this file inside WEB-INF, which would prevent users from accessing it directly.   <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>  Login
">
Username
Password
Remember me
  www.it-ebooks.info Chapter 7 ■ Spring Security 347 Note that the form action URL and the input field names are Spring Security–specific. However, the action URL can be customized with the login-url attribute of . Now, you have to change the previous login link (i.e., messageList.jsp) to refer to this URL for login:   ">Login   In order for Spring Security to display your custom login page when a login is requested, you have to specify its URL in the login-page attribute:   ...   Or the equivalent in Java based configuration.   @Configuration @EnableWebSecurity public class MessageBoardSecurityConfiguration extends WebSecurityConfigurerAdapter {   @Override protected void configure(HttpSecurity http) throws Exception { http ... .formLogin().loginPage("/login.jsp"); } }   If the login page is displayed by Spring Security when a user requests a secure URL, the user will be redirected to the target URL once the login succeeds. However, if the user requests the login page directly via its URL, by default the user will be redirected to the context path’s root (i.e., http://localhost:8080/board/) after a successful login. If you have not defined a welcome page in your web deployment descriptor, you may wish to redirect the user to a default target URL when the login succeeds:   ....   The equivalent in Java based configuration.   @Configuration @EnableWebSecurity public class MessageBoardSecurityConfiguration extends WebSecurityConfigurerAdapter {   @Override protected void configure(HttpSecurity http) throws Exception { http ... .formLogin().loginPage("/login.jsp").defaultSuccessUrl("/messageList"); } }   www.it-ebooks.info Chapter 7 ■ Spring Security 348 If you use the default login page created by Spring Security, then when a login fails, Spring Security will render the login page again with the error message. However, if you specify a custom login page, you will have to configure the authentication-failure-url attribute to specify which URL to redirect to on login error. For example, you can redirect to the custom login page again with the error request parameter:   ....   The equivalent in Java based configuration.   @Configuration @EnableWebSecurity public class MessageBoardSecurityConfiguration extends WebSecurityConfigurerAdapter {   @Override protected void configure(HttpSecurity http) throws Exception { http ... .formLogin() .loginPage("/login.jsp") .defaultSuccessUrl("/messageList") .failureUrl("login.jsp?error=true"); } }   Then your login page should test whether the error request parameter is present. If an error has occurred, you will have to display the error message by accessing the session scope attribute SPRING_SECURITY_LAST_EXCEPTION, which stores the last exception for the current user.   <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>   Login Login error.
Reason : ${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}
... www.it-ebooks.info Chapter 7 ■ Spring Security 349 The Logout Service The logout service provides a handler to handle logout requests. It can be configured via the element:   ...   For Java based configuration you can use the logout method.   @Configuration @EnableWebSecurity public class MessageBoardSecurityConfiguration extends WebSecurityConfigurerAdapter {   @Override protected void configure(HttpSecurity http) throws Exception { http ... .and() .logout(); } }   By default, it’s mapped to the URL /j_spring_security_logout, so you can add a link to a page referring to this URL for logout. Note that this URL can be customized with the logout-url attribute of .   ">Logout   By default, a user will be redirected to the context path’s root when the logout succeeds, but sometimes, you may wish to direct the user to another URL, which you can do as follows:   ...   In Java config you would use the logoutSuccessUrl method to configure the URL to direct to.   @Configuration @EnableWebSecurity public class MessageBoardSecurityConfiguration extends WebSecurityConfigurerAdapter {   @Override protected void configure(HttpSecurity http) throws Exception { http ... .and() .logout().logoutSuccessUrl("/login.jsp"); } } www.it-ebooks.info Chapter 7 ■ Spring Security 350 Anonymous Login The anonymous login service can be configured via the element or anonymous() in Java config, where you can customize the username and authorities of an anonymous user, whose default values are anonymousUser and ROLE_ANONYMOUS:   ...   When using Java config:   @Configuration @EnableWebSecurity public class MessageBoardSecurityConfiguration extends WebSecurityConfigurerAdapter {   @Override protected void configure(HttpSecurity http) throws Exception { http ... .and() .anonymous().principal("guest").authorities("ROLE_GUEST"); } }  Remember-Me Support Remember-me support can be configured via the element or rememberMe() method in Java config. By default, it encodes the username, password, remember-me expiration time, and a private key as a token, and stores it as a cookie in the user’s browser. The next time the user accesses the same web application, this token will be detected so that the user can log in automatically.   ...   www.it-ebooks.info Chapter 7 ■ Spring Security 351 When using Java config:   @Configuration @EnableWebSecurity public class MessageBoardSecurityConfiguration extends WebSecurityConfigurerAdapter {   @Override protected void configure(HttpSecurity http) throws Exception { http ... .and() .rememberMe(); } }   However, static remember-me tokens can cause security issues, because they may be captured by hackers. Spring Security supports rolling tokens for more advanced security needs, but this requires a database to persist the tokens. For details about rolling remember-me token deployment, please refer to the Spring Security reference documentation. 7-3. Authenticating Users Problem When a user attempts to log into your application to access its secure resources, you have to authenticate the user’s principal and grant authorities to this user. Solution In Spring Security, authentication is performed by one or more authentication providers, connected as a chain. If any of these providers authenticates a user successfully, that user will be able to log into the application. If any provider reports that the user is disabled or locked or that the credential is incorrect, or if no provider can authenticate the user, then the user will be unable to log into this application. Spring Security supports multiple ways of authenticating users and includes built-in provider implementations for them. You can easily configure these providers with the built-in XML elements. Most common authentication providers authenticate users against a user repository storing user details (e.g., in an application’s memory, a relational database, or an LDAP repository). When storing user details in a repository, you should avoid storing user passwords in clear text, because that makes them vulnerable to hackers. Instead, you should always store encrypted passwords in your repository. A typical way of encrypting passwords is to use a one-way hash function to encode the passwords. When a user enters a password to log in, you apply the same hash function to this password and compare the result with the one stored in the repository. Spring Security supports several algorithms for encoding passwords (including MD5 and SHA) and provides built-in password encoders for these algorithms. If you retrieve a user’s details from a user repository every time a user attempts to log in, your application may incur a performance impact. This is because a user repository is usually stored remotely, and it has to perform some kinds of queries in response to a request. For this reason, Spring Security supports caching user details in local memory and storage to save you the overhead of performing remote queries. www.it-ebooks.info Chapter 7 ■ Spring Security 352 How It Works Authenticating Users with In-Memory Definitions If you have only a few users in your application and you seldom modify their details, you can consider defining the user details in Spring Security’s configuration file so that they will be loaded into your application’s memory:    You can define user details in with multiple elements. For each user, you can specify a username, a password, a disabled status, and a set of granted authorities. A disabled user cannot log into an application. Spring Security also allows you to externalize user details in a properties file, such as /WEB-INF/users.properties:    Then, you can create the specified properties file and define the user details in the form of properties:   admin=secret,ROLE_ADMIN,ROLE_USER user1=1111,ROLE_USER user2=2222,disabled,ROLE_USER   Each property in this file represents a user’s details. The property key is the username, and the property value is divided into several parts separated by commas. The first part is the password, and the second part is the enabled status, which is optional; and the default status is enabled. The following parts are the authorities granted to the user. Authenticating Users with In-Memory Definitions using Java Config If you have only a few users in your application and you seldom modify their details, you can consider defining the user details in Spring Security’s configuration file so that they will be loaded into your application’s memory:   @Configuration @EnableWebSecurity public class MessageBoardSecurityConfiguration extends WebSecurityConfigurerAdapter {  ... @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("admin").password("secret").authorities("ROLE_ADMIN","ROLE_USER").and() .withUser("user1").password("1111").authorities("ROLE_USER").and() .withUser("user2").password("2222").disabled(true).authorities("ROLE_USER"); } }   www.it-ebooks.info Chapter 7 ■ Spring Security 353 You can define user details with the inMemoryAuthentication using the withUser method you can define the users. For each user, you can specify a username, a password, a disabled status, and a set of granted authorities. A disabled user cannot log into an application. Authenticating Users Against a Database More typically, user details should be stored in a database for easy maintenance. Spring Security has built-in support for querying user details from a database. By default, it queries user details, including authorities, with the following SQL statements:   SELECT username, password, enabled FROM users WHERE username = ?   SELECT username, authority FROM authorities WHERE username = ?   In order for Spring Security to query user details with these SQL statements, you have to create the corresponding tables in your database. For example, you can create them in the board schema of Apache Derby with the following SQL statements:   CREATE TABLE USERS ( USERNAME VARCHAR(10) NOT NULL, PASSWORD VARCHAR(32) NOT NULL, ENABLED SMALLINT, PRIMARY KEY (USERNAME) );   CREATE TABLE AUTHORITIES ( USERNAME VARCHAR(10) NOT NULL, AUTHORITY VARCHAR(10) NOT NULL, FOREIGN KEY (USERNAME) REFERENCES USERS );   Next, you can input some user details into these tables for testing purposes. The data for these two tables is shown in Tables 7-1 and 7-2. Table 7-1.  Testing User Data for the USERS Table USERNAME PASSWORD ENABLED Admin Secret 1 user1 1111 1 user2 2222 0 www.it-ebooks.info Chapter 7 ■ Spring Security 354 In order for Spring Security to access these tables, you have to declare a data source (e.g., in board-service.xml) for creating connections to this database.     Note■■ to connect to a database in the Apache Derby server, you need the Derby client .jars, as well as the Spring JDBC support. If you are using Apache Maven, add the following dependencies to your project:   org.apache.derby derbyclient 10.10.2.0 org.springframeworkspring-jdbc ${spring.version} The final step is to configure an authentication provider that queries this database for user details. You can achieve this simply by using the element with a data source reference:     Table 7-2.  Testing User Data for the AUTHORITIES Table USERNAME AUTHORITY Admin ROLE_ADMIN Admin ROLE_USER user1 ROLE_USER user2 ROLE_USER www.it-ebooks.info Chapter 7 ■ Spring Security 355 For Java Config use the jdbcAuthentication and pass it a DataSource.   @Configuration @EnableWebSecurity public class MessageBoardSecurityConfiguration extends WebSecurityConfigurerAdapter {   @Autowired private DataSource dataSource;   ...   @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.jdbcAuthentication().dataSource(dataSource); } }   However, in some cases, you may already have your own user repository defined in a legacy database. For example, suppose that the tables are created with the following SQL statements, and that all users in the MEMBER table have the enabled status:   CREATE TABLE MEMBER ( ID BIGINT NOT NULL, USERNAME VARCHAR(10) NOT NULL, PASSWORD VARCHAR(32) NOT NULL, PRIMARY KEY (ID) );   CREATE TABLE MEMBER_ROLE ( MEMBER_ID BIGINT NOT NULL, ROLE VARCHAR(10) NOT NULL, FOREIGN KEY (MEMBER_ID) REFERENCES MEMBER );   Suppose you have the legacy user data stored in these tables as shown in Tables 7-3 and 7-4. Table 7-3.  Legacy User Data in the MEMBER Table ID USERNAME PASSWORD 1 Admin Secret 2 user1 1111 Table 7-4.  Legacy User Data in the MEMBER_ROLE Table MEMBER_ID ROLE 1 ROLE_ADMIN 1 ROLE_USER 2 ROLE_USER www.it-ebooks.info Chapter 7 ■ Spring Security 356 Fortunately, Spring Security also supports using custom SQL statements to query a legacy database for user details. You can specify the statements for querying a user’s information and authorities in the users-by-username-query and authorities-by-username-query attributes:       The equivalent in Java config using usersByUsernameQuery and authoritiesByUsernameQuery methods:   @Configuration @EnableWebSecurity public class MessageBoardSecurityConfiguration extends WebSecurityConfigurerAdapter {   ...   @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.jdbcAuthentication() .dataSource(dataSource) .usersByUsernameQuery( "SELECT username, password, 'true' as enabled FROM member WHERE username = ?") .authoritiesByUsernameQuery( "SELECT member.username, member_role.role as authorities " + "FROM member, member_role " + "WHERE member.username = ? AND member.id = member_role.member_id"); } }  Encrypting Passwords Until now, you have been storing user details with clear-text passwords. But this approach is vulnerable to hacker attacks, so you should encrypt the passwords before storing them. Spring Security supports several algorithms for encrypting passwords. For example, you can choose MD5 (Message-Digest algorithm 5), a one-way hash algorithm, to encrypt your passwords. Note■■ you may need a utility to calculate MD5 digests for your passwords. One such utility is Jacksum, which you can download from http://sourceforge.net/projects/jacksum/ and extract to a directory of your choice. Then execute the following command to calculate a digest for a text: java -jar jacksum.jar -a md5 -q "txt:secret" www.it-ebooks.info Chapter 7 ■ Spring Security 357 Now, you can store the encrypted passwords in your user repository. For example, if you are using in-memory user definitions, you can specify the encrypted passwords in the password attributes. Then, you can configure a element with a hashing algorithm specified in the hash attribute.     A password encoder is also applicable to a user repository stored in a database:     In Java config you can specify the pass encoder using the passwordEncoder method on AuthenticationManagerBuilder:   @Configuration @EnableWebSecurity public class MessageBoardSecurityConfiguration extends WebSecurityConfigurerAdapter {   ...   @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.jdbcAuthentication() .dataSource(dataSource) .passwordEncoder(new Md5PasswordEncoder()); ... } }   Of course, you have to store the encrypted passwords in the database tables, instead of the clear-text passwords, as shown in Table 7-5. www.it-ebooks.info Chapter 7 ■ Spring Security 358 Authenticating Users Against an LDAP Repository Spring Security also supports accessing an LDAP repository for authenticating users. First, you have to prepare some user data for populating the LDAP repository. Let’s prepare the user data in the LDAP Data Interchange Format (LDIF), a standard plain-text data format for importing and exporting LDAP directory data. For example, create the users.ldif file containing the following contents:   dn: dc=springrecipes,dc=com objectClass: top objectClass: domain dc: springrecipes   dn: ou=groups,dc=springrecipes,dc=com objectclass: top objectclass: organizationalUnit ou: groups   dn: ou=people,dc=springrecipes,dc=com objectclass: top objectclass: organizationalUnit ou: people   dn: uid=admin,ou=people,dc=springrecipes,dc=com objectclass: top objectclass: uidObject objectclass: person uid: admin cn: admin sn: admin userPassword: secret   dn: uid=user1,ou=people,dc=springrecipes,dc=com objectclass: top objectclass: uidObject objectclass: person uid: user1 cn: user1 sn: user1 userPassword: 1111   Table 7-5.  Testing User Data with Encrypted Passwords for the USERS Table USERNAME PASSWORD ENABLED Admin 5ebe2294ecd0e0f08eab7690d2a6ee69 1 user1 b59c67bf196a4758191e42f76670ceba 1 user2 934b535800b1cba8f96a5d72f72f1611 0 www.it-ebooks.info Chapter 7 ■ Spring Security 359 dn: cn=admin,ou=groups,dc=springrecipes,dc=com objectclass: top objectclass: groupOfNames cn: admin member: uid=admin,ou=people,dc=springrecipes,dc=com   dn: cn=user,ou=groups,dc=springrecipes,dc=com objectclass: top objectclass: groupOfNames cn: user member: uid=admin,ou=people,dc=springrecipes,dc=com member: uid=user1,ou=people,dc=springrecipes,dc=com   Don’t worry if you don’t understand this LDIF file very well. You probably won’t need to use this file format to define LDAP data often, because most LDAP servers support GUI-based configuration. This users.ldif file includes the following contents: The default LDAP domain, • dc=springrecipes,dc=com The • groups and people organization units for storing groups and users The • admin and user1 users with the passwords secret and 1111 The • admin group (including the admin user) and the user group (including the admin and user1 users) For testing purposes, you can install an LDAP server on your local machine to host this user repository. For the sake of easy installation and configuration, we recommend installing OpenDS (http://www.opends.org/), a Java-based open source directory service engine that supports LDAP. Note■■   OpenDS supports two types of installation interfaces: command line and GUI. This example uses the command-line interface, so you have to download the ZIP distribution and extract it to an arbitrary directory (e.g., C:\OpenDS-2.2.1), and then execute the setup script from the root of this directory.  C:\OpenDS-2.2.0>setup --cli   OpenDS Directory Server 2.2.0 Please wait while the setup program initializes...   What would you like to use as the initial root user DN for the Directory Server? [cn=Directory Manager]: Please provide the password to use for the initial root user: ldap Please re-enter the password for confirmation: ldap   On which port would you like the Directory Server to accept connections from LDAP clients? [1389]:   On which port would you like the Administration Connector to accept connections? [4444]:   www.it-ebooks.info Chapter 7 ■ Spring Security 360 What do you wish to use as the base DN for the directory data? [dc=example,dc=com]:dc=springrecipes,dc=com Options for populating the database:   1) Only create the base entry 2) Leave the database empty 3) Import data from an LDIF file 4) Load automatically-generated sample data   Enter choice [1]: 3   Please specify the path to the LDIF file containing the data to import: users.ldif   Do you want to enable SSL? (yes / no) [no]:   Do you want to enable Start TLS? (yes / no) [no]:   Do you want to start the server when the configuration is completed? (yes/no) [yes]:   Enable OpenDS to run as a Windows Service? (yes / no) [no]:   Do you want to start the server when the configuration is completed? (yes/no) [yes]:   What would you like to do? 1) Setup the server with the parameters above 2) Provide the setup parameters again 3) Cancel the setup   Enter choice [1]:   Configuring Directory Server ..... Done. Importing LDIF file users.ldif ....... Done. Starting Directory Server ........ Done.   Note that the root user and password for this LDAP server are cn=Directory Manager and ldap, respectively. Later, you will have to use this user to connect to this server. After the LDAP server has started up, you can configure Spring Security to authenticate users against its repository. www.it-ebooks.info Chapter 7 ■ Spring Security 361 Note■■ to authenticate users against an LDAP repository, you have to have the Spring LDAP project on your ­CLASSPATH. If you are using Maven, add the following dependency to your Maven project:   org.springframework.ldap spring-ldap-core 2.0.2.RELEASE ...   You have to configure an element to define how to search users from an LDAP repository. You can specify the search filters and search bases for searching users and groups via several attributes, whose values must be consistent with the repository’s directory structure. With the preceding attribute values, Spring Security will search a user from the people organization unit with a particular user ID and search a user’s groups from the groups organization unit. Spring Security will automatically insert the ROLE_ prefix to each group as an authority. As OpenDS uses SSHA (Salted Secure Hash Algorithm) to encode user passwords by default, you have to specify {sha} as the hash algorithm in . Note that this value is different from sha, as it’s specific to LDAP password encoding. Finally, has to refer to an LDAP server definition, which defines how to create connections to an LDAP server. You can specify the root user’s username and password to connect to the LDAP server running on localhost. Authenticating Users Against an LDAP Repository using Java Config You have to configure the LDAP repository using the ldapAuthentication method. You can specify the search filters and search bases for searching users and groups via several callback methods, whose values must be consistent with the repository’s directory structure. With the preceding attribute values, Spring Security will search a user from the people organization unit with a particular user ID and search a user’s groups from the groups organization unit. Spring Security will automatically insert the ROLE_ prefix to each group as an authority.   www.it-ebooks.info Chapter 7 ■ Spring Security 362 @Configuration @EnableWebSecurity public class MessageBoardSecurityConfiguration extends WebSecurityConfigurerAdapter { ... @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .ldapAuthentication() .contextSource() .url("ldap://localhost:1389/dc=springrecipes,dc=com") .managerDn("cn=Directory Manager").managerPassword("ldap") .and() .userSearchFilter("uid={0}").userSearchBase("ou=people") .groupSearchFilter("member={0}").groupSearchBase("ou=groups")   .passwordEncoder(new LdapShaPasswordEncoder()) .passwordCompare().passwordAttribute("userPassword"); } }   As OpenDS uses SSHA (Salted Secure Hash Algorithm) to encode user passwords by default, you have to specify a LdapShaPasswordEncoder as the password encoder. Note that this value is different from sha, as it’s specific to LDAP password encoding. We also need to specify the passwordAttribute because the password encoder needs to know which field in LDAP is the password. Finally, we have to refer to an LDAP server definition, which defines how to create connections to an LDAP server. You can specify the root user’s username and password to connect to the LDAP server running on localhost, configure the server using the contextSource method. Caching User Details Both and support caching user details. First of all, you have to choose a cache implementation that provides a caching service. As Spring and Spring Security have built-in support for Ehcache (http://ehcache.sourceforge.net/), you can choose it as your cache implementation and create a configuration file for it (e.g., ehcache.xml in the classpath root) with the following contents:     www.it-ebooks.info Chapter 7 ■ Spring Security 363 Note■■ to use Ehcache to cache objects, you have to have the Ehcache library on your CLASSPATH. If you are using Maven, add the following dependency to your Maven project:   net.sf.ehcache 2.8.3 ehcache-core This Ehcache configuration file defines two types of cache configurations. One is for the default, and the other is for caching user details. If the user cache configuration is used, a cache instance will cache the details of at most 100 users in memory. The cached users will overflow to disk when this limit is exceeded. A cached user will expire if it has been idle for 10 minutes or live for 1 hour after its creation. To enable caching user details in Spring Security, you can set the cache-ref attribute of either or to refer to a UserCache object. Spring Security comes with two UserCache implementations, EhCacheBasedUserCache, which has to refer to an Ehcache instance and a SpringCacheBasedUserCache, which uses Spring’s caching abstraction.   ... ...   In Spring, an Ehcache instance can be created via EhCacheFactoryBean by providing a cache manager and a cache name. Spring also provides EhCacheManagerFactoryBean for you to create an Ehcache manager by loading a configuration file. By default, it loads ehcache.xml (located in the root of the classpath). As an Ehcache manager may be used by other service components, it should be defined in board-service.xml.       www.it-ebooks.info Chapter 7 ■ Spring Security 364 In Java based configuration, at the moment of writing, only the jdbcAuthentication() allows for easy configuration of a user cache. However configuration is quite similar to the one in XML. For a Spring Caching based cache solution (which still delegates to EhCache) we need to configure a CacheManager and make this aware of EhCache.   @Configuration public class MessageBoardConfiguration { ... @Bean public EhCacheCacheManager cacheManager() { EhCacheCacheManager cacheManager = new EhCacheCacheManager(); cacheManager.setCacheManager(ehCacheManager().getObject()); return cacheManager; }   @Bean public EhCacheManagerFactoryBean ehCacheManager() { return new EhCacheManagerFactoryBean(); } }   This is best added to the configuration of the services as the caching can also be used for others means (see the recipes regarding Spring Caching). Now that we have the CacheManager setup we need to configure a SpringCacheBasedUserCache.   @Configuration @EnableWebMvcSecurity public class MessageBoardSecurityConfiguration extends WebSecurityConfigurerAdapter {   @Autowired private CacheManager cacheManager;   @Bean public SpringCacheBasedUserCache userCache() throws Exception { Cache cache = cacheManager.getCache("userCache"); return new SpringCacheBasedUserCache(cache); }   @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.jdbcAuthentication() .userCache(userCache()) ... }   Notice the autowiring of the CacheManager into the configuration class. We need access to it because we need to retrieve a Cache instance which we pass into the constructor of the SpringCacheBasedUseCache. We are going to use the cache named userCache (which we configured in the ehcache.xml). Finally we pass the configured UserCache into the jdbcAuthentications userCache(). www.it-ebooks.info Chapter 7 ■ Spring Security 365 7-4. Making Access Control Decisions Problem In the authentication process, an application will grant a successfully authenticated user a set of authorities. When this user attempts to access a resource in the application, the application has to decide whether the resource is accessible with the granted authorities or other characteristics. Solution The decision on whether a user is allowed to access a resource in an application is called an access control decision. It is made based on the user’s authentication status, and the resource’s nature and access attributes. In Spring Security, access control decisions are made by access decision managers, which have to implement the AccessDecisionManager interface. You are free to create your own access decision managers by implementing this interface, but Spring Security comes with three convenient access decision managers based on the voting approach. They are shown in Table 7-6. Table 7-6.  Access Decision Managers That Come with Spring Security Access Decision Manager When to Grant Access AffirmativeBased At least one voter votes to grant access. ConsensusBased A consensus of voters votes to grant access. UnanimousBased All voters vote to abstain or grant access (no voter votes to deny access). All these access decision managers require a group of voters to be configured for voting on access control decisions. Each voter has to implement the AccessDecisionVoter interface. A voter can vote to grant, abstain, or deny access to a resource. The voting results are represented by the ACCESS_GRANTED, ACCESS_DENIED, and ACCESS_ABSTAIN constant fields defined in the AccessDecisionVoter interface. By default, if no access decision manager is specified explicitly, Spring Security will automatically configure an AffirmativeBased access decision manager with the following two voters configured: RoleVoter votes for an access control decision based on a user’s role. It will only process access attributes that start with the ROLE_ prefix, but this prefix can be customized. It votes to grant access if the user has the same role as required to access the resource or to deny access if the user lacks any role required to access the resource. If the resource does not have an access attribute starting with ROLE_, it will abstain from voting. AuthenticatedVoter votes for an access control decision based on a user’s authentication level. It will only process the access attributes IS_AUTHENTICATED_FULLY, IS_AUTHENTICATED_REMEMBERED, and IS_AUTHENTICATED_ANONYMOUSLY. It votes to grant access if the user’s authentication level is higher than the required attribute. From highest to lowest, authentication levels are fully authenticated, authentication remembered, and anonymously authenticated. www.it-ebooks.info Chapter 7 ■ Spring Security 366 How It Works By default, Spring Security will automatically configure an access decision manager if none is specified. This default access decision manager is equivalent to the one defined with the following bean configuration:     This default access decision manager and its decision voters should satisfy most typical authorization requirements. However, if they don’t satisfy yours, you can create your own. In most cases, you’ll only need to create a custom voter. For example, you can create a voter to vote for a decision based on a user’s IP address:   package com.apress.springrecipes.board.security;   import org.springframework.security.core.Authentication; import org.springframework.security.access.ConfigAttribute;   import org.springframework.security.web.authentication.WebAuthenticationDetails; import org.springframework.security.access.AccessDecisionVoter;   import java.util.Collection;   public class IpAddressVoter implements AccessDecisionVoter {   public static final String IP_PREFIX = "IP_"; public static final String IP_LOCAL_HOST = "IP_LOCAL_HOST";   public boolean supports(ConfigAttribute attribute) { return attribute.getAttribute() != null && attribute.getAttribute().startsWith(IP_PREFIX); }   public boolean supports(Class clazz) { return true; }   public int vote(Authentication authentication, Object object, Collection configList) { if (!(authentication.getDetails() instanceof WebAuthenticationDetails)) { return ACCESS_DENIED; }   WebAuthenticationDetails details = (WebAuthenticationDetails) authentication.getDetails(); String address = details.getRemoteAddress();   www.it-ebooks.info Chapter 7 ■ Spring Security 367 int result = ACCESS_ABSTAIN; for (ConfigAttribute config : configList) {   result = ACCESS_DENIED; if (IP_LOCAL_HOST.equals(config.getAttribute())) { if (address.equals("127.0.0.1") || address.equals("0:0:0:0:0:0:0:1")) { return ACCESS_GRANTED; } } } return result; } }   Note that this voter will only process the access attributes that start with the IP_ prefix. At the moment, it only supports the IP_LOCAL_HOST access attribute. If the user is a web client whose IP address is equal to 127.0.0.1 or 0:0:0:0:0:0:0:1—the last value being returned by networkless Linux workstations—this voter will vote to grant access. Otherwise, it will vote to deny access. If the resource does not have an access attribute starting with IP_, it will abstain from voting. Next, you have to define a custom access decision manager that includes this voter. If you define this access decision manager in board-security.xml, you will have to include the beans prefix, because the default schema is security.     Now, suppose you would like to allow users of the machine running the web container (i.e., the server administrators) to delete messages without logging in. You have to refer to this access decision manager from the configuration element and add the access attribute IP_LOCAL_HOST to the URL pattern /messageDelete.htm*:   ...   Then, if you access this message board application from localhost, you needn’t log in as an administrator to delete a posted message. www.it-ebooks.info Chapter 7 ■ Spring Security 368 Using Expression to make Access Control Decisions Although the AccessDecisionVoters allow for a certain degree of flexibility sometimes one wants more complex access control rules or be more flexible. With Spring Security it is also possible to use Springs Expression Language (SpEL) to create powerful access control rules. To enable the use of expressions (by default this is disabled, unless Java configuration is used) you have to set the use-expressions attribute on the element to true.     One thing to notice is the value in the access attribute of the elements. Those have to be rewritten to be valid expressions. Spring Security supports a couple of expressions out of the box (see Table 7-7 for a list). Using constructs like and, or, and not one can create very powerful and flexible expressions. Table 7-7.  Spring Security build in expressions Expression Description hasRole('role') hasAuthority('authority') Returns true if the current user has the given role (authority is the same as role) hasAnyRole('role1','role2') hasAnyAuthority('auth1','auth2') Returns true if the current user has at least one of the given roles (authority is the same as role) hasIpAddress('ip-address') Returns true if the current user has the given ip-address. principal The current user Authentication Access to the Spring Security authentication object. permitAll Always evaluates to true denyAll Always evaluates to false isAnonymous() Returns true if the current user is anonymous isRememberMe() Returns true if the current user logged in by the means of remember-me functionality isAuthenticated() Returns true if this is not an anonymous user isFullyAuthenticated() Returns true if the user is not an anonymous nor a remember-me user. When use-expressions is set to true Spring Security will automatically configure an access decision manager with a WebExpressionVoter. This access decision manager is equivalent to the one defined with the following bean configuration.   www.it-ebooks.info Chapter 7 ■ Spring Security 369 ... ...   When using XML configuration you have to wire up the custom implementation using the element and create a bean for the ExtendedWebSecurityExpressionHandler. 7-5. Securing Method Invocations Problem As an alternative or a complement to securing URL access in the web layer, sometimes you may need to secure method invocations in the service layer. For example, in the case that a single controller has to invoke multiple methods in the service layer, you may wish to enforce fine-grained security controls on these methods. Solution Spring Security enables you to secure method invocations in a declarative way. First, you can embed a element in a bean definition to secure its methods. Alternatively, you can configure a global element to secure multiple methods matched with AspectJ pointcut expressions. You can also annotate methods declared in a bean interface or an implementation class with the @Secured annotation and then enable security for them in or using the @EnableGlobalMethodSecurity annotation. How It Works Securing Methods by Embedding a Security Interceptor First, you can secure a bean’s methods by embedding a element in the bean definition. For example, you can secure the methods of the messageBoardService bean defined in board-service.xml. As this element is defined in the security schema, you have to import it beforehand.   ...   In a bean’s , you can specify multiple elements to specify access attributes for this bean’s methods. You can match multiple methods by specifying a method name pattern with wildcards. If you would like to use a custom access decision manager, you can specify it in the access-decision-manager-ref attribute. Securing Methods with Pointcuts Second, you can define global pointcuts in to secure methods using AspectJ pointcut expressions, instead of embedding a security interceptor in each bean whose methods require security. You should configure the element in board-security.xml for centralizing security configurations. As the default namespace of this configuration file is security, you needn’t specify a prefix for this element explicitly. You can also specify a custom access decision manager in the access-decision-manager-ref attribute.   www.it-ebooks.info Chapter 7 ■ Spring Security 373   To test this approach, you have to delete the preceding element. Securing Methods with Annotations The third approach to securing methods is by annotating them with @Secured. For example, you can annotate the methods in MessageBoardServiceImpl with the @Secured annotation and specify the access attributes as its value, whose type is String[].   package com.apress.springrecipes.board.service; ... import org.springframework.security.access.annotation.Secured;   public class MessageBoardServiceImpl implements MessageBoardService { ... @Secured({"ROLE_USER", "ROLE_GUEST"}) public List listMessages() { ... }   @Secured("ROLE_USER") public synchronized void postMessage(Message message) { ... }   @Secured({"ROLE_ADMIN", "IP_LOCAL_HOST"}) public synchronized void deleteMessage(Message message) { ... }   @Secured({"ROLE_USER", "ROLE_GUEST"}) public Message findMessageById(Long messageId) { return messages.get(messageId); } }   Then, in , you have to enable security for methods annotated with @Secured.     To do the same in Java config we have to add the @EnableGlobalMethodSecurity annotation to our configuration class.   @Configuration @EnableGlobalMethodSecurity(securedEnabled = true) public class MessageBoardConfiguration { ... }  www.it-ebooks.info Chapter 7 ■ Spring Security 374 Note■■ it is important that you add the @EnableGlobalMethodSecurity annotation or element to the application context configuration that contains the beans you want to secure! 7-6. Handling Security in Views Problem Sometimes, you may wish to display a user’s authentication information, such as the principal name and the granted authorities, in the views of your web application. In addition, you would like to render the view contents conditionally according to the user’s authorities. Solution Although you can write JSP scriptlets in your JSP files to retrieve authentication and authorization information through the Spring Security API, it’s not an efficient solution. Spring Security provides a JSP tag library for you to handle security in JSP views. It includes tags that can display a user’s authentication information and render the view contents conditionally according to the user’s authorities. How It Works Displaying Authentication Information Suppose you would like to display a user’s principal name and granted authorities in the header of the message listing page (i.e., messageList.jsp). First, you have to import Spring Security’s tag library definition.   <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>   Message List   Welcome!
  • ${authority.authority}

...   www.it-ebooks.info Chapter 7 ■ Spring Security 375 The tag exposes the current user’s Authentication object for you to render its properties. You can specify a property name or property path in its property attribute. For example, you can render a user’s principal name through the name property. In addition to rendering an authentication property directly, this tag supports storing the property in a JSP variable, whose name is specified in the var attribute. For example, you can store the authorities property, which contains the authorities granted to the user, in the JSP variable authorities, and render them one by one with a tag. You can further specify the variable scope with the scope ascope attribute. Rendering View Contents Conditionally If you would like to render view contents conditionally according to a user’s authorities, you can use the tag. For example, you can decide whether to render the message authors according to the user’s authorities:   <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>   Message List   ... ...
Author ${message.author}

...   If you want the enclosing content to be rendered only when the user has been granted certain authorities at the same time, you have to specify them in the ifAllGranted attribute. Otherwise, if the enclosing content can be rendered with any of the authorities, you have to specify them in the ifAnyGranted attribute:   Author ${message.author}   www.it-ebooks.info Chapter 7 ■ Spring Security 376 You can also render the enclosing content when a user has not been granted any of the authorities specified in the ifNotGranted attribute:   Author ${message.author} 7-7. Handling Domain Object Security Problem Sometimes, you may have complicated security requirements that require handling security at the domain object level. That means you have to allow each domain object to have different access attributes for different principals. Solution Spring Security provides a module named ACL that allows each domain object to have its own access control list (ACL). An ACL contains a domain object’s object identity to associate with the object, and also holds multiple access control entries (ACEs), each of which contains the following two core parts: Permissions: An ACE’s permissions are represented by a particular bit mask, with each bit • value for a particular type of permission. The BasePermission class predefines five basic permissions as constant values for you to use: READ (bit 0 or integer 1), WRITE (bit 1 or integer 2), CREATE (bit 2 or integer 4), DELETE (bit 3 or integer 8), and ADMINISTRATION (bit 4 or integer 16). You can also define your own using other unused bits. Security Identity (SID): Each ACE contains permissions for a particular SID. An SID can be a • principal (PrincipalSid) or an authority (GrantedAuthoritySid) to associate with permissions. In addition to defining the ACL object model, Spring Security defines APIs for reading and maintaining the model, and provides high-performance JDBC implementations for these APIs. To simplify ACL’s usages, Spring Security also provides facilities, such as access decision voters and JSP tags, for you to use ACL consistently with other security facilities in your application. How It Works Setting Up an ACL Service Spring Security provides built-in support for storing ACL data in a relational database and accessing it with JDBC. First, you have to create the following tables in your database for storing ACL data:   CREATE TABLE ACL_SID( ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, SID VARCHAR(100) NOT NULL, PRINCIPAL SMALLINT NOT NULL, PRIMARY KEY (ID), UNIQUE (SID, PRINCIPAL) );   www.it-ebooks.info Chapter 7 ■ Spring Security 377 CREATE TABLE ACL_CLASS( ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, CLASS VARCHAR(100) NOT NULL, PRIMARY KEY (ID), UNIQUE (CLASS) );   CREATE TABLE ACL_OBJECT_IDENTITY( ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, OBJECT_ID_CLASS BIGINT NOT NULL, OBJECT_ID_IDENTITY BIGINT NOT NULL, PARENT_OBJECT BIGINT, OWNER_SID BIGINT, ENTRIES_INHERITING SMALLINT NOT NULL, PRIMARY KEY (ID), UNIQUE (OBJECT_ID_CLASS, OBJECT_ID_IDENTITY), FOREIGN KEY (PARENT_OBJECT) REFERENCES ACL_OBJECT_IDENTITY, FOREIGN KEY (OBJECT_ID_CLASS) REFERENCES ACL_CLASS, FOREIGN KEY (OWNER_SID) REFERENCES ACL_SID );   CREATE TABLE ACL_ENTRY( ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, ACL_OBJECT_IDENTITY BIGINT NOT NULL, ACE_ORDER INT NOT NULL, SID BIGINT NOT NULL, MASK INTEGER NOT NULL, GRANTING SMALLINT NOT NULL, AUDIT_SUCCESS SMALLINT NOT NULL, AUDIT_FAILURE SMALLINT NOT NULL, PRIMARY KEY (ID), UNIQUE (ACL_OBJECT_IDENTITY, ACE_ORDER), FOREIGN KEY (ACL_OBJECT_IDENTITY) REFERENCES ACL_OBJECT_IDENTITY, FOREIGN KEY (SID) REFERENCES ACL_SID );   Spring Security defines APIs and provides high-performance JDBC implementations for you to access ACL data stored in these tables, so you’ll seldom have a need to access ACL data from the database directly. As each domain object can have its own ACL, there may be a large number of ACLs in your application. Fortunately, Spring Security supports caching ACL objects. You can continue to use Ehcache as your cache implementation and create a new configuration for ACL caching in ehcache.xml (located in the classpath root).   ...   www.it-ebooks.info Chapter 7 ■ Spring Security 378 Next, you have to set up an ACL service for your application. However, as Spring Security doesn’t support configuring the ACL module with XML schema-based configurations, you have to configure this module with a group of normal Spring beans. As the default namespace of board-security.xml is security, it’s cumbersome to configure an ACL in this file using the standard XML elements in the beans namespace. For this reason, let’s create a separate bean configuration file named board-acl.xml, which will store ACL-specific configurations, and add its location in the web deployment descriptor:   ... contextConfigLocation /WEB-INF/board-service.xml /WEB-INF/board-security.xml /WEB-INF/board-acl.xml   In an ACL configuration file, the core bean is an ACL service. In Spring Security, there are two interfaces that define operations of an ACL service: AclService and MutableAclService. AclService defines operations for you to read ACLs. MutableAclService is a subinterface of AclService that defines operations for you to create, update, and delete ACLs. If your application only needs to read ACLs, you can simply choose an AclService implementation, such as JdbcAclService. Otherwise, you should choose a MutableAclService implementation, such as JdbcMutableAclService.     The core bean definition in this ACL configuration file is the ACL service, which is an instance of JdbcMutableAclService that allows you to maintain ACLs. This class requires three constructor arguments. The first is a data source for creating connections to a database that stores ACL data. You should have a data source defined in board-service.xml beforehand so that you can simply refer to it here (assuming that you have created the ACL tables in the same database). The third constructor argument is a cache instance to use with an ACL, which you can configure using Ehcache as the back-end cache implementation. The second argument—sidIdentityQuery—is a lookup strategy that performs lookup for an ACL service. Note, if you’re using HSQLDB, that the sidIdentityQuery’s property is not necessary, because it defaults to this database. If using another database—as in this case for Apache Derby—an explicit value is necessary. The only implementation that comes with Spring Security is BasicLookupStrategy, which performs basic lookup using standard and compatible SQL statements. If you want to make use of advanced database features to increase lookup performance, you can create your own lookup strategy by implementing the LookupStrategy interface. A BasicLookupStrategy instance also requires a data source and a cache instance. Besides, it requires a constructor argument whose type is AclAuthorizationStrategy. This object determines whether a principal is authorized to change certain properties of an ACL, usually by specifying a required authority for each category of properties. For the preceding configurations, only a user who has the ROLE_ADMIN role can change an ACL’s ownership, an ACE’s auditing details, or other ACL and ACE details, respectively. Finally it needs a constructor argument whose type is PermissionGrantingStrategy. This objects responsibility it to check if the Acl grants access to the given Sid with the Permissions it has. Finally, JdbcMutableAclService embeds standard SQL statements for maintaining ACL data in a relational database. However, those SQL statements may not be compatible with all database products. For example, you have to customize the identity query statement for Apache Derby. Maintaining ACLs for Domain Objects In your back-end services and DAOs, you can maintain ACLs for domain objects with the previously defined ACL service via dependency injection. For your message board, you have to create an ACL for a message when it is posted and delete the ACL when this message is deleted:   package com.apress.springrecipes.board.service; ... www.it-ebooks.info Chapter 7 ■ Spring Security 380 import org.springframework.security.acls.model.MutableAcl; import org.springframework.security.acls.model.MutableAclService; import org.springframework.security.acls.domain.BasePermission; import org.springframework.security.acls.model.ObjectIdentity; import org.springframework.security.acls.domain.ObjectIdentityImpl; import org.springframework.security.acls.domain.GrantedAuthoritySid; import org.springframework.security.acls.domain.PrincipalSid; import org.springframework.security.access.annotation.Secured; import org.springframework.transaction.annotation.Transactional;   public class MessageBoardServiceImpl implements MessageBoardService { ... private MutableAclService mutableAclService;   public void setMutableAclService(MutableAclService mutableAclService) { this.mutableAclService = mutableAclService; }   @Transactional @Secured("ROLE_USER") public synchronized void postMessage(Message message) { ... ObjectIdentity oid = new ObjectIdentityImpl(Message.class, message.getId()); MutableAcl acl = mutableAclService.createAcl(oid); acl.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(message.getAuthor()), true); acl.insertAce(1, BasePermission.DELETE, new GrantedAuthoritySid("ROLE_ADMIN"), true); acl.insertAce(2, BasePermission.READ, new GrantedAuthoritySid("ROLE_USER"), true); mutableAclService.updateAcl(acl); }   @Transactional @Secured({"ROLE_ADMIN", "IP_LOCAL_HOST"}) public synchronized void deleteMessage(Message message) { ... ObjectIdentity oid = new ObjectIdentityImpl(Message.class, message.getId()); mutableAclService.deleteAcl(oid, false); } }   When a user posts a message, you create a new ACL for this message at the same time, using the message ID as the ACL’s object identity. When a user deletes a message, you delete the corresponding ACL as well. For a new message, you insert the following three ACEs into its ACL: The message author is permitted to administrate this message.• A user who has the • ROLE_ADMIN role is permitted to delete this message. A user who has the • ROLE_USER role is permitted to read this message. JdbcMutableAclService requires that the calling methods have transactions enabled so that its SQL statements can run within transactions. So, you annotate the two methods involving ACL maintenance with the @Transactional annotation and then define a transaction manager and in board-service.xml. Also, don’t forget to inject the ACL service into the message board service for it to maintain ACLs.   www.it-ebooks.info Chapter 7 ■ Spring Security 381 ...   Making Access Control Decisions using expressions With an ACL for each domain object, you can use an object’s ACL to make access control decisions on methods that involve this object. For example, when a user attempts to delete a posted message, you can consult this message’s ACL about whether the user is permitted to delete this message. Configuring ACL can be a daunting task, luckily you can use annotations and expressions to make your life easier. We can use the @PreAuthorize and @PreFilter annotations to check if someone is allowed to execute the method or use certain method arguments. The @PostAuthorize and @PostFilter can be used to check if a user is allowed to access the result or to filter results based on the ACL. To enable the processing of these annotations you need to set the pre-post-annotations attribute of the element to enabled or when using annotations, the prePostEnabled attribute of the @EnableGlobalMethodSecurity annotation to true.     Or for Java based configuration:   @EnableGlobalMethodSecurity(prePostEnabled=true)   In addition we need to configure infrastructure components to be able to make decisions. We need to setup an AclPermissionEvaluator which is needed to evaluate the permission for an object.      The equivalent in Java config:   package com.apress.springrecipes.board.web.config;   www.it-ebooks.info Chapter 7 ■ Spring Security 382 import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.acls.AclPermissionEvaluator; import org.springframework.security.acls.domain.AclAuthorizationStrategyImpl; import org.springframework.security.acls.domain.ConsoleAuditLogger; import org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy; import org.springframework.security.acls.domain.SpringCacheBasedAclCache; import org.springframework.security.acls.jdbc.BasicLookupStrategy; import org.springframework.security.acls.jdbc.JdbcMutableAclService; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;   import javax.sql.DataSource;   @Configuration public class MessageBoardAclSecurityConfiguration {   @Autowired private DataSource dataSource;   @Autowired private CacheManager cacheManager;   @Bean public SpringCacheBasedAclCache aclCache() { Cache cache = cacheManager.getCache("aclCache"); return new SpringCacheBasedAclCache(cache, permissionGrantingStrategy(), authorizationStrategy()); }   @Bean public BasicLookupStrategy lookupStrategy() { return new BasicLookupStrategy(dataSource, aclCache(), authorizationStrategy(), permissionGrantingStrategy()); }   @Bean public DefaultPermissionGrantingStrategy permissionGrantingStrategy() { return new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()); }   @Bean public AclAuthorizationStrategyImpl authorizationStrategy() { return new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_ADMIN")); }   @Bean public JdbcMutableAclService jdbcMutableAclService() { return new JdbcMutableAclService(dataSource, lookupStrategy(), aclCache()); }   www.it-ebooks.info Chapter 7 ■ Spring Security 383 @Bean public AclPermissionEvaluator permissionEvaluator() { return new AclPermissionEvaluator(jdbcMutableAclService()); }   }   This might look a little verbose but remember that we did not yet configure the collaborating classes in Java yet, until now ACL was done it in XML. We put the configuration in a separate class MessageBoardAclSecurityConfiguration. This class needs also be added to the getRootConfigClasses() method of the MessageBoardApplicationInitializer class.   @Override protected Class[] getRootConfigClasses() { return new Class[]{MessageBoardConfiguration.class, MessageBoardSecurityConfiguration.class, MessageBoardAclSecurityConfiguration.class}; }   The AclPermissionEvaluator requires an AclService to obtain the ACL for the objects it needs to check. When doing Java based configuration this is enough as the PermissionEvaluator will be automatically detected and wired to the DefaultMethodSecurityExpressionHandler. When using XML based configuration an extra step is needed, we need to explicitly configure the DefaultMethodSecurityExpressionHandler and we need to explicitly wire it to the element.     Now everything is in place to use the annotations together with expressions to control our access.   public class MessageBoardServiceImpl implements MessageBoardService {   @Transactional @PreAuthorize("hasPermission(#message, 'delete') or hasPermission(#message, 'admin')") public synchronized void deleteMessage(Message message) { messages.remove(message.getId()); ObjectIdentity oid = new ObjectIdentityImpl(Message.class, message.getId()); mutableAclService.deleteAcl(oid, false);   }   @PostFilter("hasPermission(filterObject, 'read')") public List listMessages() { return new ArrayList(messages.values()); }   www.it-ebooks.info Chapter 7 ■ Spring Security 384 @PostAuthorize("hasPermission(returnObject, 'read')") public Message findMessageById(Long messageId) { return messages.get(messageId); } }   You probably noticed the different annotations and the expressions inside these annotations. The @PreAuthorize annotation can be used to check if someone has the correct permissions to execute the method. The expression uses #message this refers to the method argument with the name message. The hasPermission expression is a built-in expression from Spring Security (see Table 7-7). The @PostFilter annotation allows you to filter the collection and remove the elements someone isn’t allowed to read. In the expression the keyword filterObject refers to an element in the collection, to remain in the collection the logged in user needs to have read permission. @PostAuthorize can be used to check if a single return value can be used (i.e., if the user has the right permissions). To use the return value in an expression use the keyword returnObject. Summary In this chapter, you learned how to secure applications using Spring Security 3.2. It can be used to secure any Java application, but it’s mostly used for web applications. The concepts of authentication, authorization, and access control are essential in the security area, so you should have a clear understanding of them. You often have to secure critical URLs by preventing unauthorized access to them. Spring Security can help you to achieve this in a declarative way. It handles security by applying servlet filters, which can be configured with simple XML elements or java based configuration. If your web application’s security requirements are simple and typical, you can enable the HTTP auto-config feature so that Spring Security will automatically configure the basic security services for you. Spring Security supports multiple ways for users to log into a web application, such as form-based login and HTTP Basic authentication. It also provides an anonymous login service that allows you to handle an anonymous user just like a normal user. Remember-me support allows an application to remember a user’s identity across multiple browser sessions. Spring Security supports multiple ways of authenticating users and has built-in provider implementations for them. For example, it supports authenticating users against in-memory definitions, a relational database, and an LDAP repository. You should always store encrypted passwords in your user repository, because clear-text passwords are vulnerable to hacker attacks. Spring Security also supports caching user details locally to save you the overhead of performing remote queries. Decisions on whether a user is allowed to access a given resource are made by access decision managers. Spring Security comes with three access decision managers that are based on the voting approach. All of them require a group of voters to be configured for voting on access control decisions. Spring Security enables you to secure method invocations in a declarative way, either by embedding a security interceptor in a bean definition, or matching multiple methods with AspectJ pointcut expressions or annotations. Spring Security also allows you to display a user’s authentication information in JSP views and render view contents conditionally according to a user’s authorities. Spring Security provides an ACL module that allows each domain object to have an ACL for controlling access. You can read and maintain an ACL for each domain object with Spring Security’s high-performance APIs, which are implemented with JDBC. Spring Security also provides facilities such as access decision voters and JSP tags for you to use ACLs consistently with other security facilities. www.it-ebooks.info 385 Chapter 8 Spring Mobile In the current world, more and more mobile devices exist. Most of these mobile devices can access the internet and access websites. Mobile devices might have a browser that lacks certain HTML or JavaScript features you might use on your website, you also might want to show a different website to your mobile users or maybe give them the choice. You could write all the device detection routines yourself; however, Spring Mobile provides ways to detect the device and to act upon it. Recipe 8-1. Device detection without Spring Mobile Problem You want to detect the type of device that connects to your website. Solution Create a Filter that detects the User-Agent of the incoming request and sets a request attribute so that it can be retrieved in a controller. How It Works First, let’s take a look at the Filter implementation we need to do User-Agent based device detection.   package com.apress.springrecipes.mobile.web.filter;   import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter;   import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;   public class DeviceResolverRequestFilter extends OncePerRequestFilter {   public static final String CURRENT_DEVICE_ATTRIBUTE = "currentDevice";   www.it-ebooks.info Chapter 8 ■ Spring Mobile 386 public static final String DEVICE_MOBILE = "MOBILE"; public static final String DEVICE_TABLET = "TABLET"; public static final String DEVICE_NORMAL = "NORMAL";   @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String userAgent = request.getHeader("User-Agent"); String device = DEVICE_NORMAL;   if (StringUtils.hasText(userAgent)) { userAgent = userAgent.toLowerCase(); if (userAgent.contains("android")) { device = userAgent.contains("mobile") ? DEVICE_NORMAL : DEVICE_TABLET; } else if (userAgent.contains("ipad") || userAgent.contains("playbook") || userAgent. contains("kindle")) { device = DEVICE_TABLET; } else if (userAgent.contains("mobil") || userAgent.contains("ipod") || userAgent. contains("nintendo DS")) { device = DEVICE_MOBILE; } } request.setAttribute(CURRENT_DEVICE_ATTRIBUTE, device); filterChain.doFilter(request, response); } }    First there is the retrieval of the User-Agent header from the incoming request. When there is a value in there the filter needs to check what is in the header. There are some if/else constructs in there to do a basic detection of the type of device. There is a special case for Android as that can be a tablet or mobile. When the filter determined what the type of device is, it is stored as a request attribute so that it is available to other components. Next there is a controller and jsp to display some information on what is going on. The controller simply directs to a home.jsp which is located in the WEB-INF/views directory. A configured InternalResourceViewResolver takes care of resolving the name to an actual JSP (for more information check the recipes in Chapter 4).   package com.apress.springrecipes.mobile.web;   import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping;   import javax.servlet.http.HttpServletRequest;   @Controller public class HomeController {   www.it-ebooks.info Chapter 8 ■ Spring Mobile 387 @RequestMapping("/home") public String index(HttpServletRequest request) { return "home"; }   }   The home.jsp:   <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>  

Welcome

Your User-Agent header:

Your type of device:

    The JSP shows the User-Agent header (if any) and the type of device, which has been determined by your own implemented DeviceResolverRequestFilter. Finally there is the configuration and bootstrapping logic.   package com.apress.springrecipes.mobile.web.config;   import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.view.InternalResourceView; import org.springframework.web.servlet.view.InternalResourceViewResolver;   @Configuration @EnableWebMvc @ComponentScan("com.apress.springrecipes.mobile.web") public class MobileConfiguration {   @Bean public ViewResolver viewResolver() { www.it-ebooks.info Chapter 8 ■ Spring Mobile 388 InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/views/"); viewResolver.setSuffix(".jsp"); return viewResolver; }   }    Spring MVC is enabled by using the @EnableWebMvc annotation and the controller is picked up by the @ComponentScan. For bootstrapping the application there is the MobileApplicationInitializer this bootstraps the DispatcherServlet and optionally the ContextLoaderListener.    package com.apress.springrecipes.mobile.web;   import com.apress.springrecipes.mobile.web.config.MobileConfiguration; import com.apress.springrecipes.mobile.web.filter.DeviceResolverRequestFilter; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;   import javax.servlet.Filter;   public class MobileApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class[] getRootConfigClasses() { return null; }   @Override protected Class[] getServletConfigClasses() { return new Class[] { MobileConfiguration.class }; }   @Override protected Filter[] getServletFilters() { return new Filter[] {new DeviceResolverRequestFilter()}; }   @Override protected String[] getServletMappings() { return new String[] {"/"}; } }   There are two things to notice here. First the earlier mentioned configuration class is passed to the DispatcherServlet by implementing the getServletConfigClasses method. Second the implementation of the getServletFilters method, which takes care of registering the filter and mapping it to the DispatcherServlet. When the application is deployed, using http://localhost:8080/mobile/home will show you the User-Agent used and what type the filter thinks it is (see Figure 8-1). www.it-ebooks.info Chapter 8 ■ Spring Mobile 389 Using Chrome on an iMac produces the above result. When using an iPhone 4 it looks like Figure 8-2: Figure 8-1.  Viewing the application in Chrome Figure 8-2.  Viewing the application using an iPhone 4 Note■■   For testing different browsers, you can either use a tablet or mobile on your internal network or use a browser plugin like User-Agent switcher for Chrome or Firefox. Although the filter does its job it is far from complete not to mention the mobile devices that don’t match the rules (for instance the Kindle Fire has a different header then a normal Kindle device). It is also quite hard to maintain the list of rules and devices or to test with many devices. Using a library, like Spring Mobile, is much easier than to roll your own. www.it-ebooks.info Chapter 8 ■ Spring Mobile 390 Recipe 8-2. Device detection with Spring Mobile Problem You want to detect the type of device that connects to your website and want to use Spring Mobile to help you with this. Solution Use the Spring Mobile DeviceResolver and helper classes to determine the type of device by either configuring the DeviceResolverRequestFilter or DeviceResolverHandlerInterceptor. How It Works Both the DeviceResolverRequestFilter and the DeviceResolverHandlerInterceptor delegate detection of the type of device to a DeviceResolver. Spring Mobile provides an implementation of that interface named the LiteDeviceResolver. The DeviceResolver returns a Device object which indicates the type, this Device is stored as a request attribute so that it can be used further down the chain. Spring Mobile comes with a single default implementation of the Device interface, LiteDevice. Using the DeviceResolverRequestFilter To use the DeviceResolverRequestFilter is a matter of adding it to the web application and mapping it to the servlet or requests that you want it to handle. For your application that means adding it to the getServletFilters method. The advantage of using this filter is that it is possible to use it even outside a Spring based application. It could also be used in a, for instance, JSF based application.   package com.apress.springrecipes.mobile.web; ... import org.springframework.mobile.device.DeviceResolverRequestFilter;   public class MobileApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { ... @Override protected Filter[] getServletFilters() { return new Filter[] {new DeviceResolverRequestFilter()}; } }    This configuration registers the DeviceResolverRequestFilter and will automatically attach it to requests handled by the DispatcherServlet. www.it-ebooks.info Chapter 8 ■ Spring Mobile 391 The output for the device is the text as created for the toString method on the LiteDevice class provided by Spring Mobile. Using the DeviceResolverHandlerInterceptor When using Spring Mobile in a Spring MVC based application it is easier to work with the DeviceResolverHandlerInterceptor. This needs to be configured in your configuration class and needs to be registered with the addInterceptors helper method.   package com.apress.springrecipes.mobile.web.config;   import org.springframework.mobile.device.DeviceResolverHandlerInterceptor; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;   @Configuration @EnableWebMvc @ComponentScan("com.apress.springrecipes.mobile.web") public class MobileConfiguration extends WebMvcConfigurerAdapter { ... @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new DeviceResolverHandlerInterceptor()); } }   The MobileConfiguration class extends WebMvcConfigurerAdapter from this class we can override the addInterceptors method. All the interceptors added to the registry will be added to the HandlerMapping beans in the application context. When the application is deployed and a request is done for http://localhost:8080/mobile/ home the result should be the same as for the filter. To test issue a request to http://localhost:8080/mobile/home, which should display something like the following: www.it-ebooks.info Chapter 8 ■ Spring Mobile 392 8-3. Using Site preferences Problem You want to allow the user to choose which type of site they visit with their device and store this for future reference. Solution Use the SitePreference support provided by Spring Mobile. How It Works Both the SitePreferenceRequestFilter and the SitePreferenceHandlerInterceptor delegate retrieval of the current SitePreference to a SitePreferenceHandler. The default implementation uses a SitePreferenceRepository to store the preferences, by default this is done in a cookie. Using the SitePreferenceRequestFilter To use the SitePreferenceRequestFilter is a matter of adding it to the web application and mapping it to the servlet or requests that you want it to handle. For your application that means adding it to the getServletFilters method. The advantage of using a filter is that it is possible to use it even outside a Spring based application. It could also be used in a, JSF-based application.   package com.apress.springrecipes.mobile.web;   import org.springframework.mobile.device.site.SitePreferenceRequestFilter; ...   public class MobileApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {   @Override protected Filter[] getServletFilters() { return new Filter[] { new DeviceResolverRequestFilter(), new SitePreferenceRequestFilter()}; }   ... }   Now that the SitePreferenceRequestFilter is registered it will inspect incoming requests. If a request has a parameter named site_preference it will use the passed in value (NORMAL, MOBILE, or TABLET) to set the SitePreference. The determined value is stored in a cookie and used for future reference, if a new value is detected the cookie value will be reset. www.it-ebooks.info Chapter 8 ■ Spring Mobile 393 Modify the home.jsp pages to include the following that will display the current SitePreference:  

Your site preferences

  When opening the page using the URL http://localhost:8080/mobile/home?site_preference=TABLET will set the SitePreference to TABLET. Using the SitePreferenceHandlerInterceptor When using Spring Mobile in a Spring MVC based application it is easier to work with the SitePreferenceHandlerInterceptor. This needs to be configured in your configuration class and needs to be registered with the addInterceptors helper method.   package com.apress.springrecipes.mobile.web.config;   import org.springframework.mobile.device.DeviceResolverHandlerInterceptor; import org.springframework.mobile.device.site. SitePreferenceHandlerInterceptor; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;   @Configuration @EnableWebMvc @ComponentScan("com.apress.springrecipes.mobile.web") public class MobileConfiguration extends WebMvcConfigurerAdapter { ... @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new DeviceResolverHandlerInterceptor()); registry.addInterceptor(new SitePreferenceHandlerInterceptor()); } }   www.it-ebooks.info Chapter 8 ■ Spring Mobile 394 The MobileConfiguration class extends WebMvcConfigurerAdapter from this class we can override the addInterceptors method. All the interceptors added to the registry will be added to the HandlerMapping beans in the application context. When the application is deployed and a request is done for http://localhost:8080/mobile/ home?site_preference=TABLET the result should be the same as for the filter in the previous section. 8-4. Using the Device Information to Render Views Problem You want to render a different view based on the device or site preferences. Solution Use the current Device and SitePreferences to determine which view to render. This can be done manually or by using the LiteDeviceDelegatingViewResolver. How It Works Now that the type of Device is known it can be used to your advantage. First let’s create some additional views for each type of device supported, put them respectively in a mobile and tablet directory under WEB-INF/views. The source for mobile/home.jsp:   <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>  

Welcome Mobile User

Your User-Agent header:

Your type of device:

Your site preferences

  The tablet/home.jsp:   <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>   www.it-ebooks.info Chapter 8 ■ Spring Mobile 395

Welcome Tablet User

Your User-Agent header:

Your type of device:

Your site preferences

  Now that the different views are in place we need to find a way to render the different views based on the Device that has been detected. One way would be to manually get access to the current device from the request and use that to determine which view to render.   package com.apress.springrecipes.mobile.web;   import org.springframework.mobile.device.Device; import org.springframework.mobile.device.DeviceUtils; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping;   import javax.servlet.http.HttpServletRequest;   @Controller public class HomeController {   @RequestMapping("/home") public String index(HttpServletRequest request) { Device device = DeviceUtils.getCurrentDevice(request); if (device.isMobile()) { return "mobile/home"; } else if (device.isTablet()) { return "tablet/home"; } else { return "home"; } } }   Spring Mobile has a DeviceUtils class that can be used to retrieve the current device. The current device is retrieved from a request attribute (currentDevice) which has been set by the filter or interceptor. The Device can be used to determine which view to render. Getting the device in each method that needs it isn’t very convenient. It would be a lot easier if it could be passed into the controller method as a method argument. For this there is the DeviceHandlerMethodArgumentResolver which can be registered and will resolve the method argument to the current device. To retrieve the current SitePreferences you can add the SitePreferenceHandlerMethodArgumentResolver.   www.it-ebooks.info Chapter 8 ■ Spring Mobile 396 package com.apress.springrecipes.mobile.web.config;   import org.springframework.mobile.device.DeviceHandlerMethodArgumentResolver; ... import java.util.List;   @Configuration @EnableWebMvc @ComponentScan("com.apress.springrecipes.mobile.web") public class MobileConfiguration extends WebMvcConfigurerAdapter {   ... @Override public void addArgumentResolvers(List argumentResolvers) { argumentResolvers.add(new DeviceHandlerMethodArgumentResolver()); argumentResolvers.add(new SitePreferenceHandlerMethodArgumentResolver()); } }   Now that these have been registered the controller method can be simplified and the Device can be passed in as a method argument   package com.apress.springrecipes.mobile.web;   import org.springframework.mobile.device.Device; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping;   import javax.servlet.http.HttpServletRequest;   @Controller public class HomeController {   @RequestMapping("/home") public String index(Device device) { if (device.isMobile()) { return "mobile/home"; } else if (device.isTablet()) { return "tablet/home"; } else { return "home"; } } }   The method signature changed from having a HttpServletRequest to a Device. That takes care of the lookup and will pass in the current device. However while this is more convenient then manual retrieval it is still quite an antiquated way to determine which view to render. Currently the preferences aren’t taken into account but this could be added to the method signature as well and could be used to determine the preference. However it would complicate the detection algorithm. Imagine this code in multiple controller methods and it will soon become a maintenance nightmare. www.it-ebooks.info Chapter 8 ■ Spring Mobile 397 Spring Mobile ships with a LiteDeviceDelegatingViewResolver which can be used to add additional prefixes and/or suffixes to the view name, before it is passed on to the actual view resolver. It also takes into account the optional site preferences of the user.   package com.apress.springrecipes.mobile.web.config;   import org.springframework.mobile.device.view.LiteDeviceDelegatingViewResolver; ...   @Configuration @EnableWebMvc @ComponentScan("com.apress.springrecipes.mobile.web") public class MobileConfiguration extends WebMvcConfigurerAdapter { ... @Bean public ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/views/"); viewResolver.setSuffix(".jsp"); viewResolver.setOrder(2); return viewResolver; }   @Bean public ViewResolver mobileViewResolver() { LiteDeviceDelegatingViewResolver delegatingViewResolver = new LiteDeviceDelegatingViewResolver(viewResolver()); delegatingViewResolver.setOrder(1); delegatingViewResolver.setMobilePrefix("mobile/"); delegatingViewResolver.setTabletPrefix("tablet/"); return delegatingViewResolver;   } }   The LiteDeviceDelegatingViewResolver takes a delegate view resolver as a constructor argument, the earlier configured InternalResourceViewResolver is passed in as the delegate. Also note the ordering of the view resolvers, you have to make sure that the LiteDeviceDelegatingViewResolver executes before any other view resolver. This way it has a chance to determine if a custom view for a particular device exists. Next notice the configuration as the views for mobile devices are located in the mobile directory and for the tablet they are in the tablet directory. To add these directories to the view names the prefixes for those device types are set to their respective directories. Now when a controller returns home as the view name to select for a mobile device it would be turned into mobile/home. This modified name is passed on to the InternalResourceViewResolver which turns it into /WEB-INF/views/mobile/home.jsp, the page we actually want to render.   package com.apress.springrecipes.mobile.web;   import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping;   www.it-ebooks.info Chapter 8 ■ Spring Mobile 398 @Controller public class HomeController {   @RequestMapping("/home") public String index() { return "home"; } }   The controller is quite clean now. Its only concern is to return the name of the view. The determination of which view to render is left to the configured view resolvers. The LiteDeviceDelegatingViewResolver takes into account any SitePreferences when found. 8-5. Site Switching Problem Your mobile site is hosted on a different URL then your normal website. Solution Use Spring Mobile’s site switching support to redirect to the appropriate part of your website. How It Works Spring Mobile comes with a SiteSwitcherHandlerInterceptor which you can use to switch to a mobile version of your site based on the detected Device. To configure the SiteSwitcherHandlerInterceptor there are a couple of factory methods which provide ready to use settings. Table 8-1.  Overview of factory methods on SiteSwitcherHandlerInterceptor Factory Method Description mDot Redirects to a domain starting with m., For instance http://www.yourdomain.com would redirect to http://m.yourdomain.com. dotMobi Redirects to a domain ending with .mobi. A request to http://www.yourdomain.com would redirect to http://www.yourdomain.mobi. urlPath Setup different context roots for different devices. Will redirect to the configured URL path for that device. For instance http://www.yourdomain.com could be redirected to http://www.yourdomain.com/mobile. standard Most flexible configurable factory method allows to specify a domain to redirect to for the mobile, tablet and normal version of your website. www.it-ebooks.info Chapter 8 ■ Spring Mobile 399 The SiteSwitcherHandlerInterceptor also provides the ability to use site preferences. When using the SiteSwitcherHandlerInterceptor it isn’t needed to register the SitePreferencesHandlerInterceptor anymore as this is already taken care of. Configuration is as simple as adding it to the list of interceptors you want to apply, the only thing to remember is that you need to place it after the DeviceResolverHandlerInterceptor as the device information is need to calculate the redirect.   package com.apress.springrecipes.mobile.web.config;   ... import org.springframework.mobile.device.switcher.SiteSwitcherHandlerInterceptor;   @Configuration @EnableWebMvc @ComponentScan("com.apress.springrecipes.mobile.web") public class MobileConfiguration extends WebMvcConfigurerAdapter {   @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new DeviceResolverHandlerInterceptor()); registry.addInterceptor(siteSwitcherHandlerInterceptor()); }   @Bean public SiteSwitcherHandlerInterceptor siteSwitcherHandlerInterceptor() { return SiteSwitcherHandlerInterceptor.mDot("yourdomain.com", true); } ... }   Notice the bean declaration for the SiteSwitcherHandlerInterceptor the factory method mDot is used to create an instance. The method takes two arguments the first is the base domain name to use and the second is a boolean indicating whether tablets need to be considered a mobile device the default is false. This configuration would lead to redirecting request to the normal website from mobile devices to m.yourdomain.com.   @Bean public SiteSwitcherHandlerInterceptor siteSwitcherHandlerInterceptor() { return SiteSwitcherHandlerInterceptor.dotMobi("yourdomain.com", true); }   The configuration above uses the dotMobi factory method which takes two arguments. The first is the base domain name to use and the second is a boolean indicating if tables are to be considered as mobile device, the default is false. This would lead to redirecting request on our normal website from mobile devices to be redirected to yourdomain.mobi.   @Bean public SiteSwitcherHandlerInterceptor siteSwitcherHandlerInterceptor() { return SiteSwitcherHandlerInterceptor.urlPath("/mobile", "/tablet", "/home"); }   www.it-ebooks.info Chapter 8 ■ Spring Mobile 400 The configuration above uses the urlPath factory method with three arguments. The first argument is the context root for mobile devices, the second the context root for tables. The final argument is the root path or your application. There are two more variations of the urlPath factory method: one which only takes only path for mobile devices and another which takes a path for mobile devices and a root path. The configuration above will lead to request from mobile devices to be redirected to yourdomain.com/home/mobile and for tables to yourdomain.com/home/tablet. Finally there is the standard factory method, which is the most flexible and elaborate to configure.   @Bean public SiteSwitcherHandlerInterceptor siteSwitcherHandlerInterceptor() { return SiteSwitcherHandlerInterceptor .standard("yourdomain.com", "mobile.yourdomain.com", "tablet.yourdomain.com", "*.yourdomain.com"); }   The configuration above uses the standard factory method. It specifies a different domain for the normal, mobile, and tables versions of the website. Finally it specifies the domain name of the cookie to use for storing the site preferences. This is needed because of the different subdomains specified. There are several other variations of the standard factory method which allow for a subset of configuration of what is shown above. Summary In this chapter you learned how to use Spring Mobile. It can be used to detect the device that is requesting a page or to allow the user to select a certain page based on preferences. You learned how you can detect the users device using the DeviceResolverRequestFilter or DeviceResolverHandlerInterceptor. You also learned how you can use SitePreferences to allow the user to override the detected Device. Next you looked at how you can use the device information and preferences to render a view for that device. Finally you learned how to redirect the user to a different part of your website based on their device or site preferences. In the next chapter you will explore how to integrate Spring with other frameworks like JSF. www.it-ebooks.info 401 Chapter 9 Spring with Other Web Frameworks In this chapter, you will learn how to integrate the Spring framework with several popular web application frameworks, like JSF. Spring’s powerful IoC container and enterprise support features make it very suitable for implementing the service and persistence layers of your Java EE applications. However, for the presentation layer, you have a choice between many different web frameworks. So, you often need to integrate Spring with whatever web application framework you are using. The integration mainly focuses on accessing beans declared in the Spring IoC container within these frameworks. JavaServer Faces, or JSF (http://java.sun.com/javaee/javaserverfaces/), is an excellent component-based and event-driven web application framework included as part of the Java EE specification. You can use the rich set of standard JSF components and also develop custom components for reuse. JSF can cleanly separate presentation logic from UIs by encapsulating it in one or more managed beans. Due to its component-based approach and popularity, JSF is supported by a wide range of IDEs for visual development. After finishing this chapter, you will be able to integrate Spring into web applications implemented with Servlet/ JSP and popular web application frameworks like JSF. 9-1. Accessing Spring in Generic Web Applications Problem You would like to access beans declared in the Spring IoC container in a web application, regardless of which framework it uses. Solution A web application can load Spring’s application context by registering the servlet listener ContextLoaderListener. This listener stores the loaded application context into the web application’s servlet context. Later, a servlet, or any object that can access the servlet context, can also access Spring’s application context through a utility method. How It Works Suppose you are going to develop a web application for users to find the distance (measured in kilometers) between two cities. First, you define the following service interface:   package com.apress.springrecipes.city;   public interface CityService {   public double findDistance(String srcCity, String destCity); }   www.it-ebooks.info Chapter 9 ■ Spring with Other Web Frameworks 402 For simplicity’s sake, let’s implement this interface by using a Java map to store the distance data. This map’s keys are source cities while its values are nested maps that contain destination cities and their distances from the source city.   package com.apress.springrecipes.city; ... public class CityServiceImpl implements CityService {   private Map>distanceMap;   public void setDistanceMap(Map>distanceMap) { this.distanceMap = distanceMap; }   public double findDistance(String srcCity, String destCity) { Map destinationMap = distanceMap.get(srcCity); if (destinationMap == null) { throw new IllegalArgumentException("Source city not found"); } Double distance = destinationMap.get(destCity); if (distance == null) { throw new IllegalArgumentException("Destination city not found"); } return distance; } }   Next the service needs to be configured by creating a class and annotating it with the @Configuration annotation and configure the bean appropriately. It could look like the following:   package com.apress.springrecipes.city.config;   import com.apress.springrecipes.city.CityService; import com.apress.springrecipes.city.CityServiceImpl; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;   import java.util.HashMap; import java.util.Map;   @Configuration public class DistanceConfiguration {   @Bean public CityService cityService() { CityServiceImpl cityService = new CityServiceImpl(); Map>distances = new HashMap<>(); Map newYorkDistances = new HashMap<>(); newYorkDistances.put("London", 5574.0); newYorkDistances.put("Beijing", 10976.0); distances.put("New York", newYorkDistances); cityService.setDistanceMap(distances); return cityService; } }   www.it-ebooks.info Chapter 9 ■ Spring with Other Web Frameworks 403 The CityServiceImpl is constructed and a Map is filled with some distance data. Next you need a servlet to process the distance requests and a page to display the form and the results. When this servlet is accessed with the HTTP GET method, it simply displays the form. Later, when the form is submitted with the POST method, this servlet finds the distance between the two input cities and displays it in the form again. Note■■ to develop web applications that use the Servlet API, you have to include the Servlet API. If you are using Maven, add the following dependency to your project: javax.servlet javax.servlet-api 3.0.1   package com.apress.springrecipes.city.servlet;   import com.apress.springrecipes.city.CityService; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils;   import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;   public class DistanceServlet extends HttpServlet {   private CityService cityService;   @Override public void init() throws ServletException { WebApplicationContext context; context =WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); cityService = context.getBean(CityService.class); }   @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { forward(request, response); }   @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String srcCity = request.getParameter("srcCity"); String destCity = request.getParameter("destCity");   www.it-ebooks.info Chapter 9 ■ Spring with Other Web Frameworks 404 double distance = cityService.findDistance(srcCity, destCity); request.setAttribute("distance", distance);   forward(request, response); }   private void forward(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { RequestDispatcher dispatcher = request.getRequestDispatcher("WEB-INF/jsp/distance.jsp"); dispatcher.forward(request, response); } }   This servlet needs to access the cityService bean declared in the Spring IoC container to find distances. As Spring’s application context is stored in the servlet context, you can retrieve it through the WebApplicationContextUtils. getRequiredWebApplicationContext() method by passing in a servlet context. The dependency lookup is done in the servlets init method. To allow users to query distances between cities, you have to create a JSP file that contains a form. You can name it distance.jsp and put it in the WEB-INF/jsp directory to prevent direct access to it. There are two text fields in this form for users to input the source and destination cities. There’s also a table grid for showing the actual distance.   City Distance
Source City
Destination City
Distance ${distance}
  www.it-ebooks.info Chapter 9 ■ Spring with Other Web Frameworks 405 Finally you need to tie everything together by loading the configuration using a ContextLoaderListener and we need to register the DistanceServlet and map it to the URL pattern /distance. For this create a class that implements the ServletContainerInitializer interface. This class is used by the Servlet container (Tomcat for instance) to bootstrap your application. This interface is part of the Servlet specification since version 3.0.   package com.apress.springrecipes.city.servlet;   import com.apress.springrecipes.city.config.DistanceConfiguration; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;   import javax.servlet.ServletContainerInitializer; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRegistration; import java.util.Set;   public class DistanceApplicationInitializer implements ServletContainerInitializer {   @Override public void onStartup(Set>c, ServletContext ctx) throws ServletException { // Register the ContextLoaderListener AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext(); appContext.register(DistanceConfiguration.class);   ctx.addListener(new ContextLoaderListener(appContext));   // Register the Servlet ServletRegistration.Dynamic registration = ctx.addServlet("distance", new DistanceServlet()); registration.setLoadOnStartup(1); registration.addMapping("/distance"); } }   First an instance of the AnnotationConfigWebApplicationContext is created and is passed the DistanceConfiguration class. Next an instance of the ContextLoaderListener is created and handed the earlier created application context. The next part registers the servlet under the name distance and is mapped to the /distance url pattern. The loadOnStartup property makes sure the servlet is started as soon as the application starts. Finally you need to create a file named javax.servlet.ServletContainerInitializer inside the /META-INF/services directory, the content of the file is the fully qualified classname of the DistanceServletContainerInitializer. The servlet container uses this file to determine which ServletContainerInitializer need to be executed.   com.apress.springrecipes.city.servlet.DistanceApplicationInitializer   Now, you can deploy this web application to a web container (e.g., Apache Tomcat 7.x). By default, Tomcat listens on port 8080, so if you deploy your application to the city context path, you can access it with the following URL once it has been started up: http://localhost:8080/city/distance. www.it-ebooks.info Chapter 9 ■ Spring with Other Web Frameworks 406 When using Spring instead of implementing the ServletContainerInitializer you can also use the Spring- provided WebApplicationInitializer interface. Spring automatically detects the classes implementing this interface and will use them to bootstrap the application. Added benefit is that you don’t need the javax.servlet. ServletContainerInitializer file anymore.   package com.apress.springrecipes.city.servlet;   import com.apress.springrecipes.city.config.DistanceConfiguration; import org.springframework.web.WebApplicationInitializer; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;   import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRegistration;   public class DistanceApplicationInitializer implements WebApplicationInitializer {   @Override public void onStartup(ServletContext ctx) throws ServletException { // Register the ContextLoaderListener AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext(); appContext.register(DistanceConfiguration.class);   ctx.addListener(new ContextLoaderListener(appContext));   // Register the Servlet ServletRegistration.Dynamic registration = ctx.addServlet("distance", new DistanceServlet()); registration.setLoadOnStartup(1); registration.addMapping("/distance"); } } 9-2. Using Spring in Your Servlets and Filters Problem The servlet specification provides servlets and filters. Servlets handle requests and responses and are responsible ultimately for producing output and acting on the inbound request. Filters are given the opportunity to react to the state of a given request and response before and after the servlet to which a request is destined has processed it. Filters provide the same effect as aspect-oriented programming, which lets you intercept and modify the state of a method invocation. Filters can be added in arbitrary depths to any existing servlet. It is possible, thus, to reuse filters to provide generic functionality to any servlet, such as gzip compression. These artifacts are declared in the web.xml file. The servlet container reads the configuration and instantiates the servlets or filters on your behalf and manages the life cycles of these objects inside the container. Because the life cycle is handled by the servlet container, and not by Spring, accessing the services of the Spring container—for example using traditional dependency injection and AOP—proves difficult. You can use WebApplicationContextUtils to look up and acquire the dependencies you need, but that defeats the value proposition of dependency injection—your code is required to acquire instances explicitly, which is not much better than using JNDI, for example. It is desirable to let Spring handle dependency injection for you and to let Spring manage the life cycles of your beans. www.it-ebooks.info Chapter 9 ■ Spring with Other Web Frameworks 407 Solution If you want to implement filter-like functionality but want to have full access to the Spring context’s life cycle machinery and dependency injection, use the DelegatingFilterProxy class. Similarly, if you want to implement servlet-like functionality but want to have full access to the Spring context’s life cycle machinery and dependency injection, use HttpRequestHandlerServlet. These classes are configured normally in web.xml, but they then delegate their obligations to a bean that you configure in the Spring application context. How It Works Servlets Let’s revisit our previous example. Suppose we wanted to rewrite the servlet functionality to leverage Spring’s application context machinery and configuration. The HttpRequestHandlerServlet will handle this for us. It uses a little bit of indirection to achieve its work: you configure an instance of org.springframework.web.context.support. HttpRequestHandlerServlet and assign it a name. The servlet takes the name as configured and looks up a bean in the root Spring application context. Assuming the bean exists and that it implements the HttpRequestHandler interface, the servlet delegates all requests to that bean by invoking the handleRequest method. You must first write a bean that implements the org.springframework.web.HttpRequestHandler interface. We will endeavor to replace our existing DistanceServlet with a POJO that implements the HttpRequestHandler interface. The logic is identical; it’s just been reorganized a bit. The POJO’s definition is as follows:   package com.apress.springrecipes.city.servlet;   import com.apress.springrecipes.city.CityService; import org.springframework.web.HttpRequestHandler;   import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;   public class DistanceHttpRequestHandler implements HttpRequestHandler {   private final CityService cityService;   public DistanceHttpRequestHandler(CityService cityService) { this.cityService = cityService; }   @Override public void handleRequest(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { if (request.getMethod().toUpperCase().equals("POST")) { String srcCity = request.getParameter("srcCity"); String destCity = request.getParameter("destCity"); double distance = cityService.findDistance(srcCity, destCity); request.setAttribute("distance", distance); } forward(request, response); }   www.it-ebooks.info Chapter 9 ■ Spring with Other Web Frameworks 408 private void forward(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { RequestDispatcher dispatcher = request.getRequestDispatcher("WEB-INF/jsp/distance.jsp"); dispatcher.forward(request, response); } }   Now, we must wire the bean up in the configuration class like so:   package com.apress.springrecipes.city.config; ... import com.apress.springrecipes.city.servlet.DistanceHttpRequestHandler;   @Configuration public class DistanceConfiguration {   @Bean public CityService cityService() { ... }   @Bean public DistanceHttpRequestHandler distance() { return new DistanceHttpRequestHandler(cityService()); } }   Here, we’ve configured our bean and injected a reference to the CityServiceImpl instance. The bean will be available under the name distance as Spring by default used the method name as the bean name. We need this to configure the HttpRequestHandlerServlet.   package com.apress.springrecipes.city.servlet;   import com.apress.springrecipes.city.config.DistanceConfiguration; import org.springframework.web.WebApplicationInitializer; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.context.support.HttpRequestHandlerServlet;   import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRegistration;   public class DistanceApplicationInitializer implements WebApplicationInitializer {   @Override public void onStartup(ServletContext ctx) throws ServletException { // Register the ContextLoaderListener AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext(); appContext.register(DistanceConfiguration.class);   ctx.addListener(new ContextLoaderListener(appContext));   www.it-ebooks.info Chapter 9 ■ Spring with Other Web Frameworks 409 // Register the Servlet ServletRegistration.Dynamic registration = ctx.addServlet("distance", new HttpRequestHandlerServlet()); registration.setLoadOnStartup(1); registration.addMapping("/distance"); } }   The use of the bean is identical as in the previous application—all code referencing the /distance endpoint will continue to work from the perspective of a bean. Try it yourself by launching your browser and pointing it to http://localhost:8080/city/distance?srcCity=New%20York&destCity=London. Filters The Spring framework provides a similar feature for filters. We will demonstrate a suitably simple filter configuration that simply iterates through the inbound request’s request attributes and lists them. Here, we will use a collaborating object, of type CityServiceRequestAuditor, whose function it is to enumerate the request parameters (conceivably, such a filter could be used to send the data to syslog, to a monitoring agent like Splunk™ or through JMX). The source code for CityServiceRequestAuditor is as follows:   package com.apress.springrecipes.city;   import java.util.Map;   public class CityServiceRequestAuditor { public void log(Map attributes) { for (String k : attributes.keySet()) { System.out.println(String.format("%s=%s", k, attributes.get(k))); } }   In the servlet example, the HttpRequestHandlerServlet delegated to another object that implemented an interface—HttpRequestHandler—that was considerably simpler than that of a raw servlet. In the javax.servlet. Filter case, however, there is very little that can be done to simplify the interface, so we will instead delegate to an implementation of filter that’s been configured using Spring. Our filter implementation is as follows:   package com.apress.springrecipes.city.filter;   import com.apress.springrecipes.city.CityServiceRequestAuditor;   import javax.servlet.*; import java.io.IOException; import java.util.Map;    /** * This class is designed to intercept requests to the {@link com.apress.springrecipes.city.CityServiceImpl} and log them */ public class CityServiceRequestFilter implements Filter { private CityServiceRequestAuditor cityServiceRequestAuditor; www.it-ebooks.info Chapter 9 ■ Spring with Other Web Frameworks 410   @Override public void init(final FilterConfig filterConfig) throws ServletException { }   @Override public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { Map parameterMap = servletRequest.getParameterMap();   this.cityServiceRequestAuditor.log(parameterMap);   filterChain.doFilter(servletRequest, servletResponse); }   @Override public void destroy() { }   public void setCityServiceRequestAuditor( final CityServiceRequestAuditorcityServiceRequestAuditor) { this.cityServiceRequestAuditor = cityServiceRequestAuditor; } }   It has a dependency on a bean of type CityServiceRequestAuditor. We will inject it and configure this bean in our Spring application context, below the previous configuration.     ...     Now, all that remains is to setup the Spring org.springframework.web.filter.DelegatingFilterProxy instance in web.xml. This configuration maps the filter to the distance servlet we configured earlier.   www.it-ebooks.info Chapter 9 ■ Spring with Other Web Frameworks 411 package com.apress.springrecipes.city.servlet;   import com.apress.springrecipes.city.config.DistanceConfiguration; import org.springframework.web.WebApplicationInitializer; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.context.support.HttpRequestHandlerServlet; import org.springframework.web.filter.DelegatingFilterProxy;   import javax.servlet.FilterRegistration; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRegistration;   public class DistanceApplicationInitializer implements WebApplicationInitializer {   @Override public void onStartup(ServletContext ctx) throws ServletException { // Register the ContextLoaderListener AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext(); appContext.register(DistanceConfiguration.class);   ctx.addListener(new ContextLoaderListener(appContext));   // Register the Servlet ServletRegistration.Dynamic registration = ctx.addServlet("distance", new HttpRequestHandlerServlet()); registration.setLoadOnStartup(1); registration.addMapping("/distance");   //Register the Filter FilterRegistration.Dynamic filterReg = ctx.addFilter("cityServiceRequestFilter", new DelegatingFilterProxy()); filterReg.addMappingForServletNames(null, false, "distance");   } }   Again, note the use of engineered coincidence; the name of the filter, cityServiceRequestFilter , is used to determine which bean in the root Spring application context to look up and delegate to. You might notice a bit of redundancy here: the filter interface exposes two methods for life cycle management—init and destroy—and the Spring context also provides life cycle management for your beans. The default behavior of the www.it-ebooks.info Chapter 9 ■ Spring with Other Web Frameworks 412 DelegatingFilterProxy is to not delegate those life cycle methods to your bean, preferring instead to let the Spring life cycle machinery (InitializingBean, @PostConstruct, etc. for initialization and DisposableBean, @PreDestroy, etc. for destruction) work instead. If you’d like to have Spring invoke those life cycle methods for you, set the targetFilterLifecycle property to true on the filter:   public class DistanceApplicationInitializer implements WebApplicationInitializer {   @Override public void onStartup(ServletContext ctx) throws ServletException { ... DelegatingFilterProxy delegatingFilter = new DelegatingFilterProxy(); delegatingFilter.setTargetFilterLifecycle(true); FilterRegistration.Dynamic filterReg = ctx.addFilter("cityServiceRequestFilter", delegatingFilter); filterReg.addMappingForServletNames(null, false, "distance");   } } 9-3. Integrating Spring with JSF Problem You would like to access beans declared in the Spring IoC container in a web application developed with JSF. Solution A JSF application is able to access Spring’s application context just like a generic web application (i.e., by registering the servlet listener ContextLoaderListener and accessing it from the servlet context). However, due to the similarity between Spring’s and JSF’s bean models, it’s very easy to integrate them by registering the Spring-provided JSF variable resolver DelegatingVariableResolver (for JSF 1.1) or the SpringBeanFacesELResolver (for JSF 1.2 and greater), which can resolve JSF variables into Spring beans. Furthermore, you can even declare JSF managed beans in Spring’s bean configuration file to centralize them with your Spring beans. How It Works Suppose you are going to implement your web application for finding city distances using JSF. First, you create the following directory structure for your web application. www.it-ebooks.info Chapter 9 ■ Spring with Other Web Frameworks 413 Note■■ before you start developing a web application using JSF, you need a JSF implementation library. You can use the JSF Reference Implementation (JSF-RI) or a third-party implementation. If you are using Maven, add the following dependencies to your Maven project: javax.servlet javax.servlet-api 3.0.1 com.sun.faces jsf-api 2.2.7 com.sun.faces jsf-impl 2.2.7 org.apache.taglibs taglibs-standard-spec 1.2.1 org.apache.taglibs taglibs-standard-impl 1.2.1 As of JSF 2.1 the necessary JSF components are registered by default. The FacesServlet, needed to handle web requests, is configured by default and is mapped to the following patterns /faces/*, *.faces and *.jsf. It is however very common to map it to *.xhtml also. Using our DistanceApplicationInitializer we can add a mapping for this pattern. To load Spring’s application context at startup, you also have to register the servlet listener ContextLoaderListener.   package com.apress.springrecipes.city.servlet;   import com.apress.springrecipes.city.config.DistanceConfiguration; import org.springframework.web.WebApplicationInitializer; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;   www.it-ebooks.info Chapter 9 ■ Spring with Other Web Frameworks 414 import javax.servlet.ServletContext; import javax.servlet.ServletException;   public class DistanceApplicationInitializer implements WebApplicationInitializer {   @Override public void onStartup(ServletContext ctx) throws ServletException { // Register the ContextLoaderListener AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext(); appContext.register(DistanceConfiguration.class); ctx.addListener(new ContextLoaderListener(appContext));   // Configure facelets to use xhtml instead of jsp extension ctx.setInitParameter("javax.faces.DEFAULT_SUFFIX", ".xhtml"); // Map the FacesServlet to *.xhtml ctx.getServletRegistration("FacesServlet").addMapping("*.xhtml"); } }   Notice the init parameter when FacesServlet receives a request, it will map this request to a file with the same name with the configured suffix (default is .jsp). For example, if you request the URL /distance.faces, then FacesServlet will load /distance.jsp accordingly. Instead of .jsp you want to switch it to .xhtml, for that set the javax.faces.DEFAULT_SUFFIX init parameter to .xhtml. Next the default registered FacesServlet, named FacesServlet, is looked up and a mapping to *.xhtml is added. The basic idea of JSF is to separate presentation logic from UIs by encapsulating it in one or more JSF managed beans. For your distance-finding function, you can create the following DistanceBean class for a JSF managed bean:   package com.apress.springrecipes.city.jsf;   import com.apress.springrecipes.city.CityService;   import javax.faces.bean.ManagedBean; import javax.faces.bean.ManagedProperty; import javax.faces.bean.RequestScoped;   @ManagedBean @RequestScoped public class DistanceBean {   private String srcCity; private String destCity; private double distance;   @ManagedProperty("#{cityService}") private CityService cityService;   public String getSrcCity() { return srcCity; }   www.it-ebooks.info Chapter 9 ■ Spring with Other Web Frameworks 415 public String getDestCity() { return destCity; }   public double getDistance() { return distance; }   public void setSrcCity(String srcCity) { this.srcCity = srcCity; }   public void setDestCity(String destCity) { this.destCity = destCity; }   public void setCityService(CityService cityService) { this.cityService = cityService; }   public void find() { distance = cityService.findDistance(srcCity, destCity); } }   Due to the @RequestScope annotation the scope of DistanceBean is request, which means a new bean instance will be created on each request. There are four properties defined in this bean. As your page has to show the srcCity, destCity, and distance properties, you define a getter method for each of them. Users can only input the srcCity and destCity properties, so they require a setter method as well. The back-end CityService bean is injected via a setter method, which is mandated when using a @ManagedProperty. When the find() method is called on this bean, it will invoke the back-end service to find the distance between these two cities and then store it in the distance property for subsequent display. Then, you create distance.xhtml in the root of your web application context. You have to put it here because when FacesServlet receives a request, it will map this request to a file with the same name with the configured suffix (default is .jsp). For example, if you request the URL /distance.faces, then FacesServlet will load /distance.xhtml accordingly. (Remember that you configured the .xhtml suffix in the DistanceApplicationInitializer).   City Distance Source City www.it-ebooks.info Chapter 9 ■ Spring with Other Web Frameworks 416 Destination City Distance   This XHTML file contains an component for users to input a source city and a destination city. These two fields are defined using two components, whose values are bound to a JSF managed bean’s properties. The distance result is defined using an component because its value is read-only. Finally, you define an component whose action will be triggered on the server side when you click it. Resolving Spring Beans in JSF The JSF configuration file faces-config.xml, located in the root of WEB-INF, is where you configure your navigation rules and JSF managed beans. For this simple application with only one screen, there’s no navigation rule to configure. You can simply configure the preceding DistanceBean here. Here is the configuration file we might use for JSF 1.2 and up (note the sample uses JSF 2.2):   org.springframework.web.jsf.el.SpringBeanFacesELResolver   Note that by registering the variable resolver the SpringBeanFacesELResolver, you can easily refer to a bean declared in Spring’s application context as a JSF variable in the form of #{beanName}. This variable resolver will first attempt to resolve variables from the original JSF variable resolver. If a variable cannot be resolved, this variable resolver will look up Spring’s application context for a bean with the same name. Now, you can deploy this application to your web container and access it through the URL http://localhost:8080/city/distance.xhtml. Declaring JSF Managed Beans in Spring’s Bean Configuration File By registering DelegatingVariableResolver, you can refer to beans declared in Spring from JSF managed beans. However, they are managed by two different containers: JSF’s and Spring’s. A better solution is to centralize them under the management of Spring’s IoC container. Let’s remove the managed bean declaration from the JSF configuration file and add the following Spring bean declaration in applicationContext.xml:   www.it-ebooks.info Chapter 9 ■ Spring with Other Web Frameworks 417 package com.apress.springrecipes.city.config; ... import com.apress.springrecipes.city.jsf.DistanceBean; import org.springframework.context.annotation.Scope;    @Configuration public class DistanceConfiguration {   @Bean public CityService cityService() { ... }   @Bean @Scope("request") public DistanceBean distanceBean() { DistanceBean distanceBean = new DistanceBean(); distanceBean.setCityService(cityService()); return distanceBean; } }   To enable the request bean scope in Spring’s application context, you have to register RequestContextListener in the DistanceApplicationInitializer.   package com.apress.springrecipes.city.servlet; ... import org.springframework.web.context.request.RequestContextListener;   public class DistanceApplicationInitializer implements WebApplicationInitializer {   @Override public void onStartup(ServletContext ctx) throws ServletException { ... ctx.addListener(new RequestContextListener()); } } Summary In this chapter, you have learned how to integrate Spring into web applications developed with Servlet/JSP and popular web application frameworks like JSF. The integration mainly focuses on accessing beans declared in the Spring IoC container within these frameworks. In a generic web application, regardless of which framework it uses, you can register the Spring-provided servlet listener ContextLoaderListener to load Spring’s application context into the servlet context of this web application. Later, a servlet or any object that can access the servlet context will also be able to access Spring’s application context through a utility method. For JSF, you can register the variable resolver DelegatingVariableResolver to resolve JSF variables into Spring beans. Furthermore, you can even declare JSF managed beans in Spring’s bean configuration file to centralize them with Spring beans. www.it-ebooks.info 419 Chapter 10 Data Access In chapter, you will learn how Spring can simplify your database access tasks (Spring can also simplify your NoSQL and BigData tasks, which is covered in Chapter 13). Data access is a common requirement for most enterprise applications, which usually require accessing data stored in relational databases. As an essential part of Java SE, Java Database Connectivity (JDBC) defines a set of standard APIs for you to access relational databases in a vendor- independent fashion. The purpose of JDBC is to provide APIs through which you can execute SQL statements against a database. However, when using JDBC, you have to manage database-related resources by yourself and handle database exceptions explicitly. To make JDBC easier to use, Spring provides an abstraction framework for interfacing with JDBC. As the heart of the Spring JDBC framework, JDBC templates are designed to provide template methods for different types of JDBC operations. Each template method is responsible for controlling the overall process and allows you to override particular tasks of the process. If raw JDBC doesn’t satisfy your requirement or you feel your application would benefit from something slightly higher level, then Spring’s support for ORM solutions will interest you. In this chapter, you will also learn how to integrate object/relational mapping (ORM) frameworks into your Spring applications. Spring supports most of the popular ORM (or data mapper) frameworks, including Hibernate, JDO, iBATIS, and the Java Persistence API (JPA). Classic TopLink isn’t supported starting from Spring 3.0 (the JPA implementation’s still supported, of course). However, the JPA support is varied and has support for many implementations of JPA, including the Hibernate and TopLink-based versions. The focus of this chapter will be on Hibernate and JPA. However, Spring’s support for ORM frameworks is consistent, so you can easily apply the techniques in this chapter to other ORM frameworks as well. ORM is a modern technology for persisting objects into a relational database. An ORM framework persists your objects according to the mapping metadata you provide (XML- or annotation-based), such as the mappings between classes and tables, properties and columns, and so on. It generates SQL statements for object persistence at runtime, so you needn’t write database-specific SQL statements unless you want to take advantage of database- specific features or provide optimized SQL statements of your own. As a result, your application will be database independent, and it can be easily migrated to another database in the future. Compared to the direct use of JDBC, an ORM framework can significantly reduce the data access effort of your applications. Hibernate is a popular open-source and high-performance ORM framework in the Java community. Hibernate supports most JDBC-compliant databases and can use specific dialects to access particular databases. Beyond the basic ORM features, Hibernate supports more advanced features such as caching, cascading, and lazy loading. It also defines a querying language called Hibernate Query Language (HQL) for you to write simple but powerful object queries. JPA defines a set of standard annotations and APIs for object persistence in both the Java SE and Java EE platforms. JPA is defined as part of the EJB 3.0 specification in JSR-220. JPA is just a set of standard APIs that require a JPA-compliant engine to provide persistence services. You can compare JPA with the JDBC API and a JPA engine with a JDBC driver. Hibernate can be configured as a JPA-compliant engine through an extension module called Hibernate EntityManager. This chapter will mainly demonstrate JPA with Hibernate as the underlying engine. www.it-ebooks.info Chapter 10 ■ Data Access 420 Problems with Direct JDBC Suppose you are going to develop an application for vehicle registration, whose major functions are the basic create, read, update, and delete (CRUD) operations on vehicle records. These records will be stored in a relational database and accessed with JDBC. First, you design the following Vehicle class, which represents a vehicle in Java:   package com.apress.springrecipes.vehicle;   public class Vehicle {   private String vehicleNo; private String color; private int wheel; private int seat;   // Constructors, Getters and Setters ... }  Setting Up the Application Database Before developing your vehicle registration application, you have to set up the database for it. For the sake of low memory consumption and easy configuration, I have chosen Apache Derby (http://db.apache.org/derby/) as my database engine. Derby is an open-source relational database engine provided under the Apache License and implemented in pure Java. Derby can run in either the embedded mode or the client/server mode. For testing purposes, the client/server mode is more appropriate because it allows you to inspect and edit data with any visual database tools that support JDBC—for example, the Eclipse Data Tools Platform (DTP). Note■■   To start the Derby server in the client/server mode, just execute the startNetworkServer script for your platform (located in the bin directory of the Derby installation). After starting up the Derby network server on localhost, you can connect to it with the JDBC properties shown in Table 10-1. Table 10-1.  JDBC Properties for Connecting to the Application Database Property Value Driver class org.apache.derby.jdbc.ClientDriver URL jdbc:derby://localhost:1527/vehicle;create=true Username app Password app www.it-ebooks.info Chapter 10 ■ Data Access 421 Note■■   You require Derby’s client JDBC driver. If you are using Maven, add the following dependency to your project.   org.apache.derby derbyclient 10.10.1.0   The first time you connect to this database, the database instance vehicle will be created, if it did not exist before, because you specified create=true in the URL. Note that the specification of this parameter will not cause the re-creation of the database if it already exists. Follow these steps to connect to Derby: 1. Open a shell on your platform. 2. Type java –jar $DERBY_HOME/lib/derbyrun.jar ij on Unix variants or %DERBY_HOME%/ lib/derbyrun.jar ij on Windows. 3. Issue the command CONNECT 'jdbc:derby://localhost:1527/ vehicle;create=true';. You can provide any values for the username and password because Derby disables authentication by default. Next, you have to create the VEHICLE table for storing vehicle records with the following SQL statement. By default, this table will be created in the APP database sAPP database schema.   CREATE TABLE VEHICLE ( VEHICLE_NO VARCHAR(10) NOT NULL, COLOR VARCHAR(10), WHEEL INT, SEAT INT, PRIMARY KEY (VEHICLE_NO) ); Understanding the Data Access Object Design Pattern A typical design mistake made by inexperienced developers is to mix different types of logic (e.g., presentation logic, business logic, and data access logic) in a single large module. This reduces the module’s reusability and maintainability because of the tight coupling it introduces. The general purpose of the Data Access Object (DAO) pattern is to avoid these problems by separating data access logic from business logic and presentation logic. This pattern recommends that data access logic be encapsulated in independent modules called data access objects. For your vehicle registration application, you can abstract the data access operations to insert, update, delete, and query a vehicle. These operations should be declared in a DAO interface to allow for different DAO implementation technologies.   package com.apress.springrecipes.vehicle;   public interface VehicleDao {   public void insert(Vehicle vehicle); public void update(Vehicle vehicle); public void delete(Vehicle vehicle); public Vehicle findByVehicleNo(String vehicleNo); }  www.it-ebooks.info Chapter 10 ■ Data Access 422 Most parts of the JDBC APIs declare throwing java.sql.SQLException. But because this interface aims to abstract the data access operations only, it should not depend on the implementation technology. So, it’s unwise for this general interface to declare throwing the JDBC-specific SQLException. A common practice when implementing a DAO interface is to wrap this kind of exception with a runtime exception (either your own business Exception subclass or a generic one). Implementing the DAO with JDBC To access the database with JDBC, you create an implementation for this DAO interface (e.g., JdbcVehicleDao). Because your DAO implementation has to connect to the database to execute SQL statements, you may establish database connections by specifying the driver class name, database URL, username, and password. However, in JDBC 2.0 or higher, you can obtain database connections from a preconfigured javax.sql.DataSource object without knowing about the connection details.   package com.apress.springrecipes.vehicle;   import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException;   import javax.sql.DataSource;   public class JdbcVehicleDao implements VehicleDao {   private DataSource dataSource;   public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; }   public void insert(Vehicle vehicle) { String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) " + "VALUES (?, ?, ?, ?)"; Connection conn = null; try { conn = dataSource.getConnection(); PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, vehicle.getVehicleNo()); ps.setString(2, vehicle.getColor()); ps.setInt(3, vehicle.getWheel()); ps.setInt(4, vehicle.getSeat()); ps.executeUpdate(); ps.close(); } catch (SQLException e) { throw new RuntimeException(e); } finally { www.it-ebooks.info Chapter 10 ■ Data Access 423 if (conn != null) { try { conn.close(); } catch (SQLException e) {} } } }   public Vehicle findByVehicleNo(String vehicleNo) { String sql = "SELECT * FROM VEHICLE WHERE VEHICLE_NO = ?"; Connection conn = null; try { conn = dataSource.getConnection(); PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, vehicleNo);   Vehicle vehicle = null; ResultSet rs = ps.executeQuery(); if (rs.next()) { vehicle = new Vehicle(rs.getString("VEHICLE_NO"), rs.getString("COLOR"), rs.getInt("WHEEL"), rs.getInt("SEAT")); } rs.close(); ps.close(); return vehicle; } catch (SQLException e) { throw new RuntimeException(e); } finally { if (conn != null) { try { conn.close(); } catch (SQLException e) {} } } }   public void update(Vehicle vehicle) {/* ... */}   public void delete(Vehicle vehicle) {/* ... */} }   The vehicle insert operation is a typical JDBC update scenario. Each time this method is called, you obtain a connection from the data source and execute the SQL statement on this connection. Your DAO interface doesn’t declare throwing any checked exceptions, so if a SQLException occurs, you have to wrap it with an unchecked RuntimeException. (There is a detailed discussion on handling exceptions in your DAOs later in this chapter). Don’t forget to release the connection in the finally block. Failing to do so may cause your application to run out of connections. Here, the update and delete operations will be skipped, because they are much the same as the insert operation from a technical point of view. For the query operation, you have to extract the data from the returned result set to build a vehicle object in addition to executing the SQL statement. www.it-ebooks.info Chapter 10 ■ Data Access 424 Configuring a Data Source in Spring The javax.sql.DataSource interface is a standard interface defined by the JDBC specification that factories Connection instances. There are many data source implementations provided by different vendors and projects: C3PO and Apache Commons DBCP are popular open source options, and most application servers will provide their own implementation. It is very easy to switch between different data source implementations, because they implement the common DataSource interface. As a Java application framework, Spring also provides several convenient but less powerful data source implementations. The simplest one is DriverManagerDataSource, which opens a new connection every time one is requested.   package com.apress.springrecipes.vehicle.config;   import com.apress.springrecipes.vehicle.JdbcVehicleDao; import com.apress.springrecipes.vehicle.VehicleDao; import org.apache.derby.jdbc.ClientDriver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DriverManagerDataSource;   import javax.sql.DataSource;   @Configuration public class VehicleConfiguration {   @Bean public VehicleDao vehicleDao() { return new JdbcVehicleDao(dataSource()); }   @Bean public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(ClientDriver.class.getName()); dataSource.setUrl("jdbc:derby://localhost:1527/vehicle;create=true"); dataSource.setUsername("app"); dataSource.setPassword("app"); return dataSource;   } }   DriverManagerDataSource is not an efficient data source implementation because it opens a new connection for the client every time it’s requested. Another data source implementation provided by Spring is SingleConnectionDataSource (a DriverManagerDataSource subclass). As its name indicates, this maintains only a single connection that’s reused all the time and never closed. Obviously, it is not suitable in a multithreaded environment. Spring’s own data source implementations are mainly used for testing purposes. However, many production data source implementations support connection pooling. For example, the Database Connection Pooling Services (DBCP) module of the Apache Commons Library has several data source implementations that support connection pooling. Of these, BasicDataSource accepts the same connection properties as DriverManagerDataSource and allows you to specify the initial connection size and maximum active connections for the connection pool.   www.it-ebooks.info Chapter 10 ■ Data Access 425 @Bean public DataSource dataSource() { BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName(ClientDriver.class.getName()); dataSource.setUrl("jdbc:derby://localhost:1527/vehicle;create=true"); dataSource.setUsername("app"); dataSource.setPassword("app"); dataSource.setInitialSize(2); dataSource.setMaxTotal(5); return dataSource; }  Note■■   To use the data source implementations provided by DBCP, you have to add them to your CLASSPATH. If you are using Maven, add the following dependency to your project:   org.apache.commons commons-dbcp2 2.0   Many Java EE application servers build in data source implementations that you can configure from the server console or in configuration files. If you have a data source configured in an application server and exposed for JNDI lookup, you can use JndiDataSourceLookup to look it up.   @Bean public DataSource dataSource() { return new JndiDataSourceLookup().getDataSource("jdbc/VehicleDS"); }  Running the DAO The following Main class tests your DAO by using it to insert a new vehicle to the database. If it succeeds, you can query the vehicle from the database immediately.   package com.apress.springrecipes.vehicle;   import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;   public class Main {   public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(VehicleConfiguration.class);   VehicleDao vehicleDao = context.getBean(VehicleDao.class); Vehicle vehicle = new Vehicle("TEM0001", "Red", 4, 4); vehicleDao.insert(vehicle);   www.it-ebooks.info Chapter 10 ■ Data Access 426 vehicle = vehicleDao.findByVehicleNo("TEM0001"); System.out.println("Vehicle No: " + vehicle.getVehicleNo()); System.out.println("Color: " + vehicle.getColor()); System.out.println("Wheel: " + vehicle.getWheel()); System.out.println("Seat: " + vehicle.getSeat()); } }   Now you can implement a DAO using JDBC directly. However, as you can see from the preceding DAO implementation, most of the JDBC code is similar and needs to be repeated for each database operation. Such redundant code will make your DAO methods much longer and less readable. Taking It a Step Further An alternative approach is to use an ORM (an object/relational mapping) tool, which lets you code the logic specifically for mapping an entity in your domain model to a database table. The ORM will, in turn, figure out how to write the logic to usefully persist your class’s data to the database. This can be very liberating: you are suddenly beholden only to your business and domain model, not to whims of your database’s SQL parser. The flip side, of course, is that you are also divesting yourself from the complete control over the communication between your client and the database—you have to trust that the ORM layer will do the right thing. 10-1. Using a JDBC Template to Update a Database Problem Using JDBC is tedious and fraught with redundant API calls, many of which could be managed for you. To implement a JDBC update operation, you have to perform the following tasks, most of which are redundant: 1. Obtain a database connection from the data source. 2. Create a PreparedStatement object from the connection. 3. Bind the parameters to the PreparedStatement object. 4. Execute the PreparedStatement object. 5. Handle SQLException. 6. Clean up the statement object and connection. JDBC is a very low-level API, but with the JDBC template, the surface area of the API that you need to work with becomes more expressive (you spend less time in the weeds and more time working on your application logic) and is simpler to work with safely. Solution The org.springframework.jdbc.core.JdbcTemplate class declares a number of overloaded update() template methods to control the overall update process. Different versions of the update() method allow you to override different task subsets of the default process. The Spring JDBC framework predefines several callback interfaces to encapsulate different task subsets. You can implement one of these callback interfaces and pass its instance to the corresponding update() method to complete the process. www.it-ebooks.info Chapter 10 ■ Data Access 427 How It Works Updating a Database with a Statement Creator The first callback interface to introduce is PreparedStatementCreator. You implement this interface to override the statement creation task (task 2) and the parameter binding task (task 3) of the overall update process. To insert a vehicle into the database, you implement the PreparedStatementCreator interface as follows:   package com.apress.springrecipes.vehicle;   import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException;   import org.springframework.jdbc.core.PreparedStatementCreator;   public class InsertVehicleStatementCreator implements PreparedStatementCreator {   private Vehicle vehicle;   public InsertVehicleStatementCreator(Vehicle vehicle) { this.vehicle = vehicle; }   public PreparedStatement createPreparedStatement(Connection con) throws SQLException { String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) VALUES (?, ?, ?, ?)"; PreparedStatement ps = con.prepareStatement(sql); ps.setString(1, vehicle.getVehicleNo()); ps.setString(2, vehicle.getColor()); ps.setInt(3, vehicle.getWheel()); ps.setInt(4, vehicle.getSeat()); return ps; } }   When implementing the PreparedStatementCreator interface, you will get the database connection as the createPreparedStatement() method’s argument. All you have to do in this method is to create a PreparedStatement object on this connection and bind your parameters to this object. Finally, you have to return the PreparedStatement object as the method’s return value. Notice that the method signature declares throwing SQLException, which means that you don’t need to handle this kind of exception yourself. Now, you can use this statement creator to simplify the vehicle insert operation. First of all, you have to create an instance of the JdbcTemplate class and pass in the data source for this template to obtain a connection from it. Then, you just make a call to the update() method and pass in your statement creator for the template to complete the update process.   package com.apress.springrecipes.vehicle; ... import org.springframework.jdbc.core.JdbcTemplate;   public class JdbcVehicleDao implements VehicleDao { ... public void insert(Vehicle vehicle) { www.it-ebooks.info Chapter 10 ■ Data Access 428 JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.update(new InsertVehicleStatementCreator(vehicle)); } }   Typically, it is better to implement the PreparedStatementCreator interface and other callback interfaces as inner classes if they are used within one method only. This is because you can get access to the local variables and method arguments directly from the inner class, instead of passing them as constructor arguments. The only constraint on such variables and arguments is that they must be declared as final.   package com.apress.springrecipes.vehicle; ... import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.PreparedStatementCreator;   public class JdbcVehicleDao implements VehicleDao { ... public void insert(final Vehicle vehicle) { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);   jdbcTemplate.update(new PreparedStatementCreator() {   public PreparedStatement createPreparedStatement(Connection conn) throws SQLException { String sql = "INSERT INTO VEHICLE " + "(VEHICLE_NO, COLOR, WHEEL, SEAT) " + "VALUES (?, ?, ?, ?)"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, vehicle.getVehicleNo()); ps.setString(2, vehicle.getColor()); ps.setInt(3, vehicle.getWheel()); ps.setInt(4, vehicle.getSeat()); return ps; } }); } }   Now, you can delete the preceding InsertVehicleStatementCreator class, because it will not be used anymore. Updating a Database with a Statement Setter The second callback interface, PreparedStatementSetter, as its name indicates, performs only the parameter binding task (task 3) of the overall update process.   package com.apress.springrecipes.vehicle; ... import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.PreparedStatementSetter;   www.it-ebooks.info Chapter 10 ■ Data Access 429 public class JdbcVehicleDao implements VehicleDao { ... public void insert(final Vehicle vehicle) { String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) VALUES (?, ?, ?, ?)"; JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);   jdbcTemplate.update(sql, new PreparedStatementSetter() {   public void setValues(PreparedStatement ps) throws SQLException { ps.setString(1, vehicle.getVehicleNo()); ps.setString(2, vehicle.getColor()); ps.setInt(3, vehicle.getWheel()); ps.setInt(4, vehicle.getSeat()); } }); } }   Another version of the update() template method accepts a SQL statement and a PreparedStatementSetter object as arguments. This method will create a PreparedStatement object for you from your SQL statement. All you have to do with this interface is to bind your parameters to the PreparedStatement object. Updating a Database with a SQL Statement and Parameter Values Finally, the simplest version of the update() method accepts a SQL statement and an object array as statement parameters. It will create a PreparedStatement object from your SQL statement and bind the parameters for you. Therefore, you don’t have to override any of the tasks in the update process.   package com.apress.springrecipes.vehicle; ... import org.springframework.jdbc.core.JdbcTemplate;   public class JdbcVehicleDao implements VehicleDao { ... public void insert(final Vehicle vehicle) { String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) VALUES (?, ?, ?, ?)"; JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);   jdbcTemplate.update(sql, vehicle.getVehicleNo(),vehicle.getColor(),vehicle.getWheel(), vehicle.getSeat() ); } }   Of the three different versions of the update() method introduced, the last is the simplest because you don’t have to implement any callback interfaces. Additionally, we’ve managed to remove all setX (setInt, setString, etc.)–style methods for parameterizing the query. In contrast, the first is the most flexible because you can do any preprocessing of the PreparedStatement object before its execution. In practice, you should always choose the simplest version that meets all your needs. There are also other overloaded update() methods provided by the JdbcTemplate class. Please refer to Javadoc for details. www.it-ebooks.info Chapter 10 ■ Data Access 430 Batch Updating a Database Suppose you want to insert a batch of vehicles into the database. If you call the insert() method multiple times, the update will be very slow as the SQL statement will be compiled repeatedly. So, it would be better to add a new method to the DAO interface for inserting a batch of vehicles.   package com.apress.springrecipes.vehicle; ... public interface VehicleDao { ... public void insertBatch(List vehicles); }   The JdbcTemplate class also offers the batchUpdate() template method for batch update operations. It requires a SQL statement and a BatchPreparedStatementSetter object as arguments. In this method, the statement is compiled (prepared) only once and executed multiple times. If your database driver supports JDBC 2.0, this method automatically makes use of the batch update features to increase performance.   package com.apress.springrecipes.vehicle; ... import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.JdbcTemplate;   public class JdbcVehicleDao implements VehicleDao { ... public void insertBatch(final List vehicles) { String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) VALUES (?, ?, ?, ?)"; JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);   jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {   public int getBatchSize() { return vehicles.size(); }   public void setValues(PreparedStatement ps, int i) throws SQLException { Vehicle vehicle = vehicles.get(i); ps.setString(1, vehicle.getVehicleNo()); ps.setString(2, vehicle.getColor()); ps.setInt(3, vehicle.getWheel()); ps.setInt(4, vehicle.getSeat()); } }); } }   www.it-ebooks.info Chapter 10 ■ Data Access 431 You can test your batch insert operation with the following code snippet in the Main cMain class:   package com.apress.springrecipes.vehicle; ... public class Main {   public static void main(String[] args) { ... VehicleDao vehicleDao = (VehicleDao) context.getBean("vehicleDao"); Vehicle vehicle1 = new Vehicle("TEM0002", "Blue", 4, 4); Vehicle vehicle2 = new Vehicle("TEM0003", "Black", 4, 6); vehicleDao.insertBatch(Arrays.asList(vehicle1, vehicle2)); } } 10-2. Using a JDBC Template to Query a Database Problem To implement a JDBC query operation, you have to perform the following tasks, two of which (tasks 5 and 6) are additional as compared to an update operation: 1. Obtain a database connection from the data source. 2. Create a PreparedStatement object from the connection. 3. Bind the parameters to the PreparedStatement object. 4. Execute the PreparedStatement object. 5. Iterate the returned result set. 6. Extract data from the result set. 7. Handle SQLException. 8. Clean up the statement object and connection. The only steps relevant to your business logic, however, are the definition of the query and the extraction of the results from the result set! The rest is better handled by the JDBC template. Solution The JdbcTemplate class declares a number of overloaded query() template methods to control the overall query process. You can override the statement creation (task 2) and the parameter binding (task 3) by implementing the PreparedStatementCreator and PreparedStatementSetter interfaces, just as you did for the update operations. Moreover, the Spring JDBC framework supports multiple ways for you to override the data extraction (task 6). www.it-ebooks.info Chapter 10 ■ Data Access 432 How It Works Extracting Data with Row Callback Handler RowCallbackHandler is the is the primary interface that allows you to process the current row of the result set. One of the query() methods iterates the result set for you and calls your RowCallbackHandler for each row. So, the processRow() method will be called once for each row of the returned result set.   package com.apress.springrecipes.vehicle; ... import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowCallbackHandler;   public class JdbcVehicleDao implements VehicleDao { ... public Vehicle findByVehicleNo(String vehicleNo) { String sql = "SELECT * FROM VEHICLE WHERE VEHICLE_NO = ?"; JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);   final Vehicle vehicle = new Vehicle(); jdbcTemplate.query(sql, new RowCallbackHandler() { public void processRow(ResultSet rs) throws SQLException { vehicle.setVehicleNo(rs.getString("VEHICLE_NO")); vehicle.setColor(rs.getString("COLOR")); vehicle.setWheel(rs.getInt("WHEEL")); vehicle.setSeat(rs.getInt("SEAT")); } }, vehicleNo); return vehicle; } }   As there will be one row returned for the SQL query at maximum, you can create a vehicle object as a local variable and set its properties by extracting data from the result set. For a result set with more than one row, you should collect the objects as a list. Extracting Data with a Row Mapper The RowMapper interface is more general than RowCallbackHandler. Its purpose is to map a single row of the result set to a customized object, so it can be applied to a single-row result set as well as a multiple-row result set. From the viewpoint of reuse, it’s better to implement the RowMapper interface as a normal class than as an inner class. In the mapRow() method of this interface, you have to construct the object that represents a row and return it as the method’s return value.   package com.apress.springrecipes.vehicle;   import java.sql.ResultSet; import java.sql.SQLException;   import org.springframework.jdbc.core.RowMapper;   www.it-ebooks.info Chapter 10 ■ Data Access 433 public class VehicleRowMapper implements RowMapper {   public Vehicle mapRow(ResultSet rs, int rowNum) throws SQLException { Vehicle vehicle = new Vehicle(); vehicle.setVehicleNo(rs.getString("VEHICLE_NO")); vehicle.setColor(rs.getString("COLOR")); vehicle.setWheel(rs.getInt("WHEEL")); vehicle.setSeat(rs.getInt("SEAT")); return vehicle; } }   As mentioned, RowMapper can be used for either a single-row or multiple-row result set. When querying for a unique object like in findByVehicleNo(), you have to make a call to the queryForObject() method of JdbcTemplate.   package com.apress.springrecipes.vehicle; ... import org.springframework.jdbc.core.JdbcTemplate;   public class JdbcVehicleDao implements VehicleDao { ... public Vehicle findByVehicleNo(String vehicleNo) { String sql = "SELECT * FROM VEHICLE WHERE VEHICLE_NO = ?"; JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);   return jdbcTemplate.queryForObject(sql, new VehicleRowMapper(), vehicleNo); } }   Spring comes with a convenient RowMapper implementation, BeanPropertyRowMapper, which can automatically map a row to a new instance of the specified class. Note that the specified class must be a top-level class and must have a default or no-argument constructor. It first instantiates this class and then maps each column value to a property by matching their names. It supports matching a property name (e.g., vehicleNo) to the same column name or the column name with underscores (e.g., VEHICLE_NO).   package com.apress.springrecipes.vehicle; ... import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate;   public class JdbcVehicleDao implements VehicleDao {   ...   public Vehicle findByVehicleNo(String vehicleNo) { String sql = "SELECT * FROM VEHICLE WHERE VEHICLE_NO = ?"; JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);   return jdbcTemplate.queryForObject(sql, BeanPropertyRowMapper.newInstance(Vehicle.class), vehicleNo); } } www.it-ebooks.info Chapter 10 ■ Data Access 434 Querying for Multiple Rows Now, let’s look at how to query for a result set with multiple rows. For example, suppose that you need a findAll() method in the DAO interface to get all vehicles.   package com.apress.springrecipes.vehicle; ... public interface VehicleDao { ... public List findAll(); }   Without the help of RowMapper, you can still call the queryForList() method and pass in a SQL statement. The returned result will be a list of maps. Each map stores a row of the result set with the column names as the keys.   package com.apress.springrecipes.vehicle; ... import org.springframework.jdbc.core.JdbcTemplate;   public class JdbcVehicleDao implements VehicleDao { ... public List findAll() { String sql = "SELECT * FROM VEHICLE"; JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);   List vehicles = new ArrayList(); List>rows = jdbcTemplate.queryForList(sql); for (Map row : rows) { Vehicle vehicle = new Vehicle(); vehicle.setVehicleNo((String) row.get("VEHICLE_NO")); vehicle.setColor((String) row.get("COLOR")); vehicle.setWheel((Integer) row.get("WHEEL")); vehicle.setSeat((Integer) row.get("SEAT")); vehicles.add(vehicle); } return vehicles; } }   You can test your findAll() method with the following code snippet in the Main class:   package com.apress.springrecipes.vehicle; ... public class Main {   public static void main(String[] args) { ... VehicleDao vehicleDao = (VehicleDao) context.getBean("vehicleDao"); List vehicles = vehicleDao.findAll(); for (Vehicle vehicle : vehicles) { System.out.println("Vehicle No: " + vehicle.getVehicleNo()); System.out.println("Color: " + vehicle.getColor()); www.it-ebooks.info Chapter 10 ■ Data Access 435 System.out.println("Wheel: " + vehicle.getWheel()); System.out.println("Seat: " + vehicle.getSeat()); } } }   If you use a RowMapper object to map the rows in a result set, you will get a list of mapped objects from the query() method.   package com.apress.springrecipes.vehicle; ... import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate;   public class JdbcVehicleDao implements VehicleDao { ... public List findAll() { String sql = "SELECT * FROM VEHICLE"; JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); return jdbcTemplate.query (sql, BeanPropertyRowMapper.newInstance(Vehicle.class), vehicleNo); } } Querying for a Single Value Finally, let’s consider to query for a single-row and single-column result set. As an example, add the following operations to the DAO interface:   package com.apress.springrecipes.vehicle; ... public interface VehicleDao { ... public String getColor(String vehicleNo); public int countAll(); }   To query for a single string value, you can call the overloaded queryForObject() method, which requires an argument of java.lang.Class type. This method will help you to map the result value to the type you specified.   package com.apress.springrecipes.vehicle; ... import org.springframework.jdbc.core.JdbcTemplate;   public class JdbcVehicleDao implements VehicleDao { ... public String getColor(String vehicleNo) { String sql = "SELECT COLOR FROM VEHICLE WHERE VEHICLE_NO = ?"; JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);   return jdbcTemplate.queryForObject(sql, String.class, vehicleNo); }   www.it-ebooks.info Chapter 10 ■ Data Access 436 public int countAll() { String sql = "SELECT COUNT(*) FROM VEHICLE"; JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);   return jdbcTemplate.queryForObject(sql, Integer.class); } }  Note■■   Earlier versions of Spring provided the queryForInt and queryForLong methods, as of Spring 3.2 those methods are deprecated in favor of the queryForObject method. You can test these two methods with the following code snippet in the Main class:   package com.apress.springrecipes.vehicle; ... public class Main {   public static void main(String[] args) { ... VehicleDao vehicleDao = context.getBean(VehicleDao.class); int count = vehicleDao.countAll(); System.out.println("Vehicle Count: " + count); String color = vehicleDao.getColor("TEM0001"); System.out.println("Color for [TEM0001]: " + color); } } 10-3. Simplifying JDBC Template Creation Problem It’s not efficient to create a new instance of JdbcTemplate every time you use it, because you have to repeat the creation statement and incur the cost of creating a new object. Solution The JdbcTemplate class is designed to be thread-safe, so you can declare a single instance of it in the IoC container and inject this instance into all your DAO instances. Furthermore, the Spring JDBC framework offers a convenient class, org.springframework.jdbc.core.support.JdbcDaoSupport, to simplify your DAO implementation. This class declares a jdbcTemplate property, which can be injected from the IoC container or created automatically from a data source, for example, JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource). Your DAO can extend this class to have this property inherited. www.it-ebooks.info Chapter 10 ■ Data Access 437 How It Works Injecting a JDBC Template Until now, you have created a new instance of JdbcTemplate in each DAO method. Actually, you can have it injected at the class level and use this injected instance in all DAO methods. For simplicity’s sake, the following code shows only the change to the insert() method:   package com.apress.springrecipes.vehicle; ... import org.springframework.jdbc.core.JdbcTemplate;   public class JdbcVehicleDao implements VehicleDao {   private final JdbcTemplate jdbcTemplate;   public JdbcVehicleDao (JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; }   public void insert(final Vehicle vehicle) { String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) " + "VALUES (?, ?, ?, ?)";   jdbcTemplate.update(sql, new Object[] { vehicle.getVehicleNo(), vehicle.getColor(), vehicle.getWheel(), vehicle.getSeat() });   } ... }   A JDBC template requires a data source to be set. You can inject this property by either a setter method or a constructor argument. Then, you can inject this JDBC template into your DAO.   @Configuration public class VehicleConfiguration { ... @Bean public VehicleDao vehicleDao() { return new JdbcVehicleDao(jdbcTemplate()); }    @Bean public JdbcTemplate jdbcTemplate() { return new JdbcTemplate(dataSource()); } } www.it-ebooks.info Chapter 10 ■ Data Access 438 Extending the JdbcDaoSupport Class The org.springframework.jdbc.core.support.JdbcDaoSupport class has a setDataSource() method and a setJdbcTemplate() method. Your DAO class can extend this class to have these methods inherited. Then, you can either inject a JDBC template directly or inject a data source for it to create a JDBC template. The following code fragment is taken from Spring’s JdbcDaoSupport class:   package org.springframework.jdbc.core.support; ... public abstract class JdbcDaoSupport extends DaoSupport {   private JdbcTemplate jdbcTemplate;   public final void setDataSource(DataSource dataSource) { if( this.jdbcTemplate == null || dataSource != this.jdbcTemplate.getDataSource() ){ this.jdbcTemplate = createJdbcTemplate(dataSource); initTemplateConfig(); } } ... public final void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; initTemplateConfig(); }   public final JdbcTemplate getJdbcTemplate() { return this.jdbcTemplate; } ... }   In your DAO methods, you can simply call the getJdbcTemplate() method to retrieve the JDBC template. You also have to delete the dataSource and jdbcTemplate properties, as well as their setter methods, from your DAO class, because they have already been inherited. Again, for simplicity’s sake, only the change to the insert() method is shown.   package com.apress.springrecipes.vehicle; ... import org.springframework.jdbc.core.support.JdbcDaoSupport;   public class JdbcVehicleDao extends JdbcDaoSupport implements VehicleDao {   public void insert(final Vehicle vehicle) { String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) " + "VALUES (?, ?, ?, ?)";   getJdbcTemplate().update(sql, new Object[] { vehicle.getVehicleNo(), vehicle.getColor(), vehicle.getWheel(), vehicle.getSeat() }); } ... }   www.it-ebooks.info Chapter 10 ■ Data Access 439 By extending JdbcDaoSupport, your DAO class inherits the setDataSource() method. You can inject a data source into your DAO instance for it to create a JDBC template.   @Configuration public class VehicleConfiguration { ... @Bean public VehicleDao vehicleDao() { JdbcVehicleDao vehicleDao = new JdbcVehicleDao(); vehicleDao.setDataSource(dataSource()); return vehicleDao; } } 10-4. Using Named Parameters in a JDBC Template Problem In classic JDBC usage, SQL parameters are represented by the placeholder ? and are bound by position. The trouble with positional parameters is that whenever the parameter order is changed, you have to change the parameter bindings as well. For a SQL statement with many parameters, it is very cumbersome to match the parameters by position. Solution Another option when binding SQL parameters in the Spring JDBC framework is to use named parameters. As the term implies, named SQL parameters are specified by name (starting with a colon) rather than by position. Named parameters are easier to maintain and also improve readability. At runtime, the framework classes replace named parameters with placeholders. Named parameters are supported by the NamedParameterJdbcTemplate. How It Works When using named parameters in your SQL statement, you can provide the parameter values in a map with the parameter names as the keys.   package com.apress.springrecipes.vehicle; ... import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport;   public class JdbcVehicleDao extends NamedParameterJdbcDaoSupport implements VehicleDao {   public void insert(Vehicle vehicle) { String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) " + "VALUES (:vehicleNo, :color, :wheel, :seat)";   Map parameters = new HashMap(); parameters.put("vehicleNo", vehicle.getVehicleNo()); parameters.put("color", vehicle.getColor()); parameters.put("wheel", vehicle.getWheel()); parameters.put("seat", vehicle.getSeat());   www.it-ebooks.info Chapter 10 ■ Data Access 440 getNamedParameterJdbcTemplate().update(sql, parameters); } ... }   You can also provide a SQL parameter source, whose responsibility is to offer SQL parameter values for named SQL parameters. There are three implementations of the SqlParameterSource interface. The basic one is MapSqlParameterSource, which wraps a map as its parameter source. In this example, this is a net-loss compared to the previous example, as we’ve introduced one extra object—the SqlParameterSource:   package com.apress.springrecipes.vehicle; ... import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.SqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport;   public class JdbcVehicleDao extends NamedParameterJdbcDaoSupport implements VehicleDao {   public void insert(Vehicle vehicle) { String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) " + "VALUES (:vehicleNo, :color, :wheel, :seat)";   Map parameters = new HashMap(); ... SqlParameterSource parameterSource = new MapSqlParameterSource(parameters);   getNamedParameterJdbcTemplate().update(sql, parameterSource); } ... }   The power comes when we need an extra level of indirection between the parameters passed into the update method and the source of their values. For example, what if we want to get properties from a JavaBean? Here is where the SqlParameterSource intermediary starts to benefit us! SqlParameterSource is BeanPropertySqlParameterSource, which wraps a normal Java object as a SQL parameter source. For each of the named parameters, the property with the same name will be used as the parameter value.   package com.apress.springrecipes.vehicle; ... import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; import org.springframework.jdbc.core.namedparam.SqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport;   public class JdbcVehicleDao extends NamedParameterJdbcDaoSupport implements VehicleDao {   public void insert(Vehicle vehicle) { String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) " + "VALUES (:vehicleNo, :color, :wheel, :seat)";   www.it-ebooks.info Chapter 10 ■ Data Access 441 SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(vehicle);   getNamedParameterJdbcTemplate ().update(sql, parameterSource); } ... }   Named parameters can also be used in batch update. You can provide either a Map array or a SqlParameterSource array for the parameter values.   package com.apress.springrecipes.vehicle; ... import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; import org.springframework.jdbc.core.namedparam.SqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport;   public class JdbcVehicleDao extends NamedParameterJdbcDaoSupport implements VehicleDao { ... public void insertBatch(List vehicles) { String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) " + "VALUES (:vehicleNo, :color, :wheel, :seat)";   List parameters = new ArrayList(); for (Vehicle vehicle : vehicles) { parameters.add(new BeanPropertySqlParameterSource(vehicle)); }   getNamedParameterJdbcTemplate ().batchUpdate(sql, parameters.toArray(new SqlParameterSource[paramters.size()])); } } 10-5. Handling Exceptions in the Spring JDBC Framework Problem Many of the JDBC APIs declare throwing java.sql.SQLException, a checked exception that must be caught. It’s very troublesome to handle this kind of exception every time you perform a database operation. You often have to define your own policy to handle this kind of exception. Failure to do so may lead to inconsistent exception handling. Solution The Spring framework offers a consistent data access exception-handling mechanism for its data access module, including the JDBC framework. In general, all exceptions thrown by the Spring JDBC framework are subclasses of org.springframework.dao.DataAccessException, a type of RuntimeException that you are not forced to catch. It’s the root exception class for all exceptions in Spring’s data access module. Figure 10-1 shows only part of the DataAccessException hierarchy in Spring’s data access module. In total, there are more than 30 exception classes defined for different categories of data access exceptions. www.it-ebooks.info Chapter 10 ■ Data Access 442 How It Works Understanding Exception Handling in the Spring JDBC Framework Until now, you haven’t handled JDBC exceptions explicitly when using a JDBC template or JDBC operation objects. To help you understand the Spring JDBC framework’s exception-handling mechanism, let’s consider the following code fragment in the Main class, which inserts a vehicle. What happens if you insert a vehicle with a duplicate vehicle number?   package com.apress.springrecipes.vehicle; ... public class Main {   public static void main(String[] args) { ... VehicleDao vehicleDao = context.getBean(VehicleDao.class); Vehicle vehicle = new Vehicle("EX0001", "Green", 4, 4); vehicleDao.insert(vehicle); } }   If you run the method twice, or the vehicle has already been inserted into the database, it will throw a DuplicateKeyException, an indirect subclass of DataAccessException. In your DAO methods, you neither need to surround the code with a try/catch block nor declare throwing an exception in the method signature. This is because DataAccessException (and therefore its subclasses, including DuplicateKeyException) is an unchecked exception Figure 10-1.  Common exception classes in the DataAccessException hierarchy www.it-ebooks.info Chapter 10 ■ Data Access 443 that you are not forced to catch. The direct parent class of DataAccessException is NestedRuntimeException, a core Spring exception class that wraps another exception in a RuntimeException. When you use the classes of the Spring JDBC framework, they will catch SQLException for you and wrap it with one of the subclasses of DataAccessException. As this exception is a RuntimeException, you are not required to catch it. But how does the Spring JDBC framework know which concrete exception in the DataAccessException hierarchy should be thrown? It’s by looking at the errorCode and SQLState properties of the caught SQLException. As a DataAccessException wraps the underlying SQLException as the root cause, you can inspect the errorCode and SQLState properties with the following catch block:   package com.apress.springrecipes.vehicle; ... import java.sql.SQLException;   import org.springframework.dao.DataAccessException;   public class Main {   public static void main(String[] args) { ... VehicleDao vehicleDao = context.getBean(VehicleDao.class); Vehicle vehicle = new Vehicle("EX0001", "Green", 4, 4); try { vehicleDao.insert(vehicle); } catch (DataAccessException e) { SQLException sqle = (SQLException) e.getCause(); System.out.println("Error code: " + sqle.getErrorCode()); System.out.println("SQL state: " + sqle.getSQLState()); } } }   When you insert the duplicate vehicle again, notice that Apache Derby returns the following error code and SQL state:  Error code : -1 SQL state : 23505  If you refer to the Apache Derby reference manual, you will find the error code description shown in Table 10-2. Table 10-2.  Apache Derby’s Error Code Description SQL State Message Text 23505 The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by ‘’ defined on ‘ ’. www.it-ebooks.info Chapter 10 ■ Data Access 444 How does the Spring JDBC framework know that state 23505 should be mapped to DuplicateKeyException? The error code and SQL state are database specific, which means different database products may return different codes for the same kind of error. Moreover, some database products will specify the error in the errorCode property, while others (like Derby) will do so in the SQLState property. As an open Java application framework, Spring understands the error codes of most popular database products. Because of the large number of error codes, however, it can only maintain mappings for the most frequently encountered errors. The mapping is defined in the sql-error-codes.xml file, located in the org.springframework. jdbc.support package. The following snippet for Apache Derby is taken from this file:   ...   Apache Derby true 42802,42821,42X01,42X02,42X03,42X04,42X05,42X06,42X07,42X08 23505 22001,22005,23502,23503,23513,X0Y32 04501,08004,42Y07 40XL1 40001   Note that the databaseProductName property is used to match the database product name returned by Connection.getMetaData().getDatabaseProductName(). This enables Spring to know which type of database is currently connecting. The useSqlStateForTranslation property means that the SQLState property, rather than the errorCode property, should be used to match the error code. Finally, the SQLErrorCodes class defines several categories for you to map database error codes. The code 23505 lies in the dataIntegrityViolationCodes cdataIntegrityViolationCodes category. www.it-ebooks.info Chapter 10 ■ Data Access 445 Customizing Data Access Exception Handling The Spring JDBC framework only maps well-known error codes. Sometimes, you may wish to customize the mapping yourself. For example, you might decide to add more codes to an existing category or define a custom exception for particular error codes. In Table 10-2, the error code 23505 indicates a duplicate key error in Apache Derby. It is mapped by default to DataIntegrityViolationException. Suppose that you want to create a custom exception type, MyDuplicateKeyException, for this kind of error. It should extend DataIntegrityViolationException because it is also a kind of data integrity violation error. Remember that for an exception to be thrown by the Spring JDBC framework, it must be compatible with the root exception class DataAccessException.   package com.apress.springrecipes.vehicle;   import org.springframework.dao.DataIntegrityViolationException;   public class MyDuplicateKeyException extends DataIntegrityViolationException {   public MyDuplicateKeyException(String msg) { super(msg); }   public MyDuplicateKeyException(String msg, Throwable cause) { super(msg, cause); } }   By default, Spring will look up an exception from the sql-error-codes.xml file located in the org.springframework.jdbc.support package. However, you can override some of the mappings by providing a file with the same name in the root of the classpath. If Spring can find your custom file, it will look up an exception from your mapping first. However, if it does not find a suitable exception there, Spring will look up the default mapping. For example, suppose that you want to map your custom DuplicateKeyException type to error code 23505. You have to add the binding via a CustomSQLErrorCodesTranslation bean, and then add this bean to the customTranslations category.   Apache Derby true   www.it-ebooks.info Chapter 10 ■ Data Access 446 23505 com.apress.springrecipes.vehicle.MyDuplicateKeyException   Now, if you remove the try/catch block surrounding the vehicle insert operation and insert a duplicate vehicle, the Spring JDBC framework will throw a MyDuplicateKeyException instead. However, if you are not satisfied with the basic code-to-exception mapping strategy used by the SQLErrorCodes class, you may further implement the SQLExceptionTranslator interface and inject its instance into a JDBC template via the setExceptionTranslator() method. 10-6. Problems with Using ORM Frameworks Directly Problem You’ve decided to go to the next level—you have a sufficiently complex domain model, and manually writing all the code for each entity is getting tedious, so you begin to investigate a few alternatives, like Hibernate. You’re stunned to find that while they’re powerful, they can be anything but simple! Solution Let Spring lend a hand; it has facilities for dealing with ORM layers that rival those available for plain ol’ JDBC access. How It Works Suppose you are developing a course management system for a training center. The first class you create for this system is Course. This class is called an entity class or a persistent class because it represents a real-world entity and its instances will be persisted to a database. Remember that for each entity class to be persisted by an ORM framework, a default constructor with no argument is required.   package com.apress.springrecipes.course; ... public class Course {   private Long id; private String title; private Date beginDate; private Date endDate; private int fee;   // Constructors, Getters and Setters ... }   www.it-ebooks.info Chapter 10 ■ Data Access 447 For each entity class, you must define an identifier property to uniquely identify an entity. It’s a best practice to define an auto-generated identifier because this has no business meaning and thus won’t be changed under any circumstances. Moreover, this identifier will be used by the ORM framework to determine an entity’s state. If the identifier value is null, this entity will be treated as a new and unsaved entity. When this entity is persisted, an insert SQL statement will be issued; otherwise, an update statement will. To allow the identifier to be null, you should choose a primitive wrapper type like java.lang.Integer and java.lang.Long for the identifier. In your course management system, you need a DAO interface to encapsulate the data access logic. Let’s define the following operations in the CourseDao interface:   package com.apress.springrecipes.course; ... public interface CourseDao {   public void store(Course course); public void delete(Long courseId); public Course findById(Long courseId); public List findAll(); }   Usually, when using ORM for persisting objects, the insert and update operations are combined into a single operation (e.g., store). This is to let the ORM framework (not you) decide whether an object should be inserted or updated. In order for an ORM framework to persist your objects to a database, it must know the mapping metadata for the entity classes. You have to provide mapping metadata to it in its supported format. The native format for Hibernate is XML. However, because each ORM framework may have its own format for defining mapping metadata, JPA defines a set of persistent annotations for you to define mapping metadata in a standard format that is more likely to be reusable in other ORM frameworks. Hibernate also supports the use of JPA annotations to define mapping metadata, so there are essentially three different strategies for mapping and persisting your objects with Hibernate and JPA: Using the Hibernate API to persist objects with Hibernate XML mappings• Using the Hibernate API to persist objects with JPA annotations• Using JPA to persist objects with JPA annotations• The core programming elements of Hibernate, JPA, and other ORM frameworks resemble those of JDBC. They are summarized in Table 10-3. Table 10-3.  Core Programming Elements for Different Data Access Strategies Concept JDBC Hibernate JPA Resource Connection Session EntityManager Resource factory DataSource SessionFactory EntityManagerFactory Exception SQLException HibernateException PersistenceException www.it-ebooks.info Chapter 10 ■ Data Access 448 In Hibernate, the core interface for object persistence is Session, whose instances can be obtained from a SessionFactory instance. In JPA, the corresponding interface is EntityManager, whose instances can be obtained from an EntityManagerFactory instance. The exceptions thrown by Hibernate are of type HibernateException, while those thrown by JPA may be of type PersistenceException or other Java SE exceptions like IllegalArgumentException and IllegalStateException. Note that all these exceptions are subclasses of RuntimeException, which you are not forced to catch and handle. Persisting Objects Using the Hibernate API with Hibernate XML Mappings To map entity classes with Hibernate XML mappings, you can provide a single mapping file for each class or a large file for several classes. Practically, you should define one for each class by joining the class name with .hbm.xml as the file extension for ease of maintenance. The middle extension hbm stands for “Hibernate metadata.” The mapping file for the Course class should be named Course.hbm.xml and put in the same package as the entity class.       In the mapping file, you can specify a table name for this entity class and a table column for each simple property. You can also specify the column details such as column length, not-null constraints, and unique constraints. In addition, each entity must have an identifier defined, which can be generated automatically or assigned manually. In this example, the identifier will be generated using a table identity column. Each application that uses Hibernate requires a global configuration file to configure properties such as the database settings (either JDBC connection properties or a data source’s JNDI name), the database dialect, the mapping metadata’s locations, and so on. When using XML mapping files to define mapping metadata, you have to specify the locations of the XML files. By default, Hibernate will read the hibernate.cfg.xml file from the root of the classpath. The middle extension cfg stands for “configuration.” If there is a hibernate.properties file on the classpath, that file will be consulted first and overridden by hibernate.cfg.xml.     www.it-ebooks.info Chapter 10 ■ Data Access 449 org.apache.derby.jdbc.ClientDriver jdbc:derby://localhost:1527/course;create=true app app org.hibernate.dialect.DerbyTenSevenDialect true update   Before you can persist your objects, you have to create tables in a database schema to store the object data. When using an ORM framework like Hibernate, you usually needn’t design the tables by yourself. If you set the hbm2ddl.auto property to update, Hibernate can help you to update the database schema and create the tables when necessary. Naturally, you shouldn’t enable this in production, but it can be a great speed boost for development. Now, let’s implement the DAO interface in the hibernate subpackage using the plain Hibernate API. Before you call the Hibernate API for object persistence, you have to initialize a Hibernate session factory (e.g., in the constructor).   package com.apress.springrecipes.course.hibernate; ... import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration;   public class HibernateCourseDao implements CourseDao {   private SessionFactory sessionFactory;   public HibernateCourseDao() { Configuration configuration = new Configuration().configure(); sessionFactory = configuration.buildSessionFactory(); }   public void store(Course course) { Session session = sessionFactory.openSession(); Transaction tx = session.getTransaction(); try { tx.begin(); session.saveOrUpdate(course); tx.commit(); } catch (RuntimeException e) { tx.rollback(); www.it-ebooks.info Chapter 10 ■ Data Access 450 throw e; } finally { session.close(); } }   public void delete(Long courseId) { Session session = sessionFactory.openSession(); Transaction tx = session.getTransaction(); try { tx.begin(); Course course = (Course) session.get(Course.class, courseId); session.delete(course); tx.commit(); } catch (RuntimeException e) { tx.rollback(); throw e; } finally { session.close(); } }   public Course findById(Long courseId) { Session session = sessionFactory.openSession(); try { return (Course) session.get(Course.class, courseId); } finally { session.close(); } }   public List findAll() { Session session = sessionFactory.openSession(); try { Query query = session.createQuery("from Course"); return query.list(); } finally { session.close(); } } }   The first step in using Hibernate is to create a Configuration object and ask it to load the Hibernate configuration file. By default, it loads hibernate.cfg.xml from the classpath root when you call the configure() method. Then, you build a Hibernate session factory from this Configuration object. The purpose of a session factory is to produce sessions for you to persist your objects. In the preceding DAO methods, you first open a session from the session factory. For any operation that involves database update, such as saveOrUpdate() and delete(), you must start a Hibernate transaction on that session. If the operation completes successfully, you commit the transaction. Otherwise, you roll it back if any RuntimeException happens. For read-only operations such as get() and HQL queries, there’s no need to start a transaction. Finally, you must remember to close a session to release the resources held by this session. www.it-ebooks.info Chapter 10 ■ Data Access 451 You can create the following Main class to test run all the DAO methods. It also demonstrates an entity’s typical life cycle.   package com.apress.springrecipes.course; ... public class Main {   public static void main(String[] args) { CourseDao courseDao = new HibernateCourseDao();   Course course = new Course(); course.setTitle("Core Spring"); course.setBeginDate(new GregorianCalendar(2007, 8, 1).getTime()); course.setEndDate(new GregorianCalendar(2007, 9, 1).getTime()); course.setFee(1000); courseDao.store(course);   List courses = courseDao.findAll(); Long courseId = courses.get(0).getId();   course = courseDao.findById(courseId); System.out.println("Course Title: " + course.getTitle()); System.out.println("Begin Date: " + course.getBeginDate()); System.out.println("End Date: " + course.getEndDate()); System.out.println("Fee: " + course.getFee());   courseDao.delete(courseId); } } Persisting Objects Using the Hibernate API with JPA Annotations JPA annotations are standardized in the JSR-220 specification, so they’re supported by all JPA-compliant ORM frameworks, including Hibernate. Moreover, the use of annotations will be more convenient for you to edit mapping metadata in the same source file. The following Course class illustrates the use of JPA annotations to define mapping metadata:   package com.apress.springrecipes.course; ... import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table;   @Entity @Table(name = "COURSE") public class Course {   www.it-ebooks.info Chapter 10 ■ Data Access 452 @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "ID") private Long id;   @Column(name = "TITLE", length = 100, nullable = false) private String title;   @Column(name = "BEGIN_DATE") private Date beginDate;   @Column(name = "END_DATE") private Date endDate;   @Column(name = "FEE") private int fee;   // Constructors, Getters and Setters ... }   Each entity class must be annotated with the @Entity annotation. You can assign a table name for an entity class in this annotation. For each property, you can specify a column name and column details using the @Column annotation. Each entity class must have an identifier defined by the @Id annotation. You can choose a strategy for identifier generation using the @GeneratedValue annotation. Here, the identifier will be generated by a table identity column. Hibernate supports both native XML mapping files and JPA annotations as ways of defining mapping metadata. For JPA annotations, you have to specify the fully qualified names of the entity classes in hibernate.cfg.xml for Hibernate to read the annotations.   ... Persisting Objects Using JPA with Hibernate as the Engine In addition to persistent annotations, JPA defines a set of programming interfaces for object persistence. However, JPA is not a persistence implementation; you have to pick up a JPA-compliant engine to provide persistence services. Hibernate can be JPA-compliant through the Hibernate EntityManager extension module. With this extension, Hibernate can work as an underlying JPA engine to persist objects. This lets you retain both the valuable investment in Hibernate (perhaps it’s faster or handles certain operations more to your satisfaction) and write code that is JPA-compliant and portable among other JPA engines. This can also be a useful way to transition a code base to JPA. New code is written strictly against the JPA APIs, and older code is transitioned to the JPA interfaces. In a Java EE environment, you can configure the JPA engine in a Java EE container. But in a Java SE application, you have to set up the engine locally. The configuration of JPA is through the central XML file persistence.xml, located in the META-INF directory of the classpath root. In this file, you can set any vendor-specific properties for the underlying engine configuration. www.it-ebooks.info Chapter 10 ■ Data Access 453 Now, let’s create the JPA configuration file persistence.xml in the META-INF directory of the classpath root. Each JPA configuration file contains one or more elements. A persistence unit defines a set of persistent classes and how they should be persisted. Each persistence unit requires a name for identification. Here, you assign the name course to this persistence unit.     In this JPA configuration file, you configure Hibernate as your underlying JPA engine by referring to the Hibernate configuration file located in the classpath root. However, because Hibernate EntityManager will automatically detect XML mapping files and JPA annotations as mapping metadata, you have no need to specify them explicitly. Otherwise, you will encounter an org.hibernate.DuplicateMappingException.   ...   As an alternative to referring to the Hibernate configuration file, you can also centralize all the Hibernate configurations in persistence.xml.   '   www.it-ebooks.info Chapter 10 ■ Data Access 454 In a Java EE environment, a Java EE container is able to manage the entity manager for you and inject it into your EJB components directly. But when you use JPA outside of a Java EE container (e.g., in a Java SE application), you have to create and maintain the entity manager by yourself. Note■■   To use Hibernate as the underlying JPA engine, you have to include the Hibernate Entity Manager libraries to your CLASSPATH. If you’re using Maven, add the following dependency to your project:   org.hibernate hibernate-entitymanager ${hibernate.version} Now, let’s implement the CourseDao interface using JPA in a Java SE application. Before you call JPA for object persistence, you have to initialize an entity manager factory. The purpose of an entity manager factory is to produce entity managers for you to persist your objects.   package com.apress.springrecipes.course; ... import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; import javax.persistence.Query;   public class JpaCourseDao implements CourseDao {   private EntityManagerFactory entityManagerFactory;   public JpaCourseDao() { entityManagerFactory = Persistence.createEntityManagerFactory("course"); }   public void store(Course course) { EntityManager manager = entityManagerFactory.createEntityManager(); EntityTransaction tx = manager.getTransaction(); try { tx.begin(); manager.merge(course); tx.commit(); } catch (RuntimeException e) { tx.rollback(); throw e; } finally { manager.close(); } }   www.it-ebooks.info Chapter 10 ■ Data Access 455 public void delete(Long courseId) { EntityManager manager = entityManagerFactory.createEntityManager(); EntityTransaction tx = manager.getTransaction(); try { tx.begin(); Course course = manager.find(Course.class, courseId); manager.remove(course); tx.commit(); } catch (RuntimeException e) { tx.rollback(); throw e; } finally { manager.close(); } }   public Course findById(Long courseId) { EntityManager manager = entityManagerFactory.createEntityManager(); try { return manager.find(Course.class, courseId); } finally { manager.close(); } }   public List findAll() { EntityManager manager = entityManagerFactory.createEntityManager(); try { Query query = manager.createQuery("select course from Course course"); return query.getResultList(); } finally { manager.close(); } } }   The entity manager factory is built by the static method createEntityManagerFactory() of the javax.persistence.Persistence class. You have to pass in a persistence unit name defined in persistence.xml for an entity manager factory. In the preceding DAO methods, you first create an entity manager from the entity manager factory. For any operation that involves database update, such as merge() and remove(), you must start a JPA transaction on the entity manager. For read-only operations such as find() and JPA queries, there’s no need to start a transaction. Finally, you must close an entity manager to release the resources. You can test this DAO with the similar Main class, but this time, you instantiate the JPA DAO implementation instead.   www.it-ebooks.info Chapter 10 ■ Data Access 456 package com.apress.springrecipes.course; ... public class Main {   public static void main(String[] args) { CourseDao courseDao = new JpaCourseDao(); ... } }   In the preceding DAO implementations for both Hibernate and JPA, there are only one or two lines that are different for each DAO method. The rest of the lines are boilerplate routine tasks that you have to repeat. Moreover, each ORM framework has its own API for local transaction management. 10-7. Configuring ORM Resource Factories in Spring Problem When using an ORM framework on its own, you have to configure its resource factory with its API. For Hibernate and JPA, you have to build a session factory and an entity manager factory from the native Hibernate API and JPA. You have no choice but to manage these objects manually, without Spring’s support. Solution Spring provides several factory beans for you to create a Hibernate session factory or a JPA entity manager factory as a singleton bean in the IoC container. These factories can be shared between multiple beans via dependency injection. Moreover, this allows the session factory and the entity manager factory to integrate with other Spring data access facilities, such as data sources and transaction managers. How It Works Configuring a Hibernate Session Factory in Spring First of all, let’s modify HibernateCourseDao to accept a session factory via dependency injection, instead of creating it directly with the native Hibernate API in the constructor.   package com.apress.springrecipes.course.hibernate; ... import org.hibernate.SessionFactory;   public class HibernateCourseDao implements CourseDao {   private SessionFactory sessionFactory;   public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } ... }   www.it-ebooks.info Chapter 10 ■ Data Access 457 Now, let’s look at how to declare a session factory that uses XML mapping files in Spring. For this purpose, you have to enable the XML mapping file definition in hibernate.cfg.xml again.   ...   Then, you create a bean configuration file for using Hibernate as the ORM framework (e.g., beans-hibernate.xml in the classpath root). You can declare a session factory that uses XML mapping files with the factory bean LocalSessionFactoryBean. You can also declare a HibernateCourseDao instance under Spring’s management.   package com.apress.springrecipes.course.config;   import com.apress.springrecipes.course.CourseDao; import com.apress.springrecipes.course.HibernateCourseDao; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.orm.hibernate4.LocalSessionFactoryBean;   @Configuration public class CourseConfiguration {   @Bean public CourseDao courseDao() { return new HibernateCourseDao(sessionfactory().getObject()); }   @Bean public LocalSessionFactoryBean sessionfactory() { LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean(); sessionFactoryBean.setConfigLocation(new ClassPathResource("hibernate.cfg.xml")); return sessionFactoryBean; } }   Note that you can specify the configLocation property for this factory bean to load the Hibernate configuration file. The configLocation property is of type Resource, and we want to load the file from the classpath, hence we construct a ClassPathResource with the name of the configuration file. The preceding factory bean loads the configuration file from the root of the classpath. Now, you can modify the Main class to retrieve the HibernateCourseDao instance from the Spring IoC container.   package com.apress.springrecipes.course; ... import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;   www.it-ebooks.info Chapter 10 ■ Data Access 458 public class Main {   public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(CourseConfiguration.class); CourseDao courseDao = context.getBean(CourseDao.class); ... } }   The preceding factory bean creates a session factory by loading the Hibernate configuration file, which includes the database settings (either JDBC connection properties or a data source’s JNDI name). Now, suppose you have a data source defined in the Spring IoC container. If you want to use this data source for your session factory, you can inject it into the dataSource property of LocalSessionFactoryBean. The data source specified in this property will override the database settings in the Hibernate configuration file. If this is set, the Hibernate settings should not define a connection provider to avoid meaningless double configuration.   @Configuration public class CourseConfiguration { ... @Bean public LocalSessionFactoryBean sessionfactory() { LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean(); sessionFactoryBean.setDataSource(dataSource()); sessionFactoryBean.setConfigLocation(new ClassPathResource("hibernate.cfg.xml")); return sessionFactoryBean; }   @Bean public DataSource dataSource() { SimpleDriverDataSource dataSource = new SimpleDriverDataSource(); dataSource.setDriverClass(ClientDriver.class); dataSource.setUrl("jdbc:derby://localhost:1527/course;create=true"); dataSource.setUsername("app"); dataSource.setPassword("app"); return dataSource; } }   Or you can even ignore the Hibernate configuration file by merging all the configurations into LocalSessionFactoryBean. For example, you can specify the packages containing the JPA annotated classes in the packagesToScan property and other Hibernate properties such as the database dialect in the hibernateProperties property.   @Configuration public class CourseConfiguration { ... @Bean public LocalSessionFactoryBean sessionfactory() { LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean(); sessionFactoryBean.setDataSource(dataSource()); sessionFactoryBean.setPackagesToScan("com.apress.springrecipes.course"); sessionFactoryBean.setHibernateProperties(hibernateProperties()); return sessionFactoryBean; }   www.it-ebooks.info Chapter 10 ■ Data Access 459 private Properties hibernateProperties() { Properties properties = new Properties(); properties.put("hibernate.dialect", org.hibernate.dialect.DerbyTenSevenDialect.class.getName()); properties.put("hibernate.show_sql", true); properties.put("hibernate.hbm2dll.auto", "update"); return properties; } }   If you are in a project that still uses Hibernate mapping files you can use the mappingLocations property to specify the mapping files. LocalSessionFactoryBean also allows you take advantage of Spring’s resource-loading support to load mapping files from various types of locations. You can specify the resource paths of the mapping files in the mappingLocations property, whose type is Resource[].   @Bean public LocalSessionFactoryBean sessionfactory() { LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean(); sessionFactoryBean.setDataSource(dataSource()); sessionFactoryBean.setMappingLocations( new ClassPathResource("com/apress/springrecipes/course/Course.hbm.xml ")); sessionFactoryBean.setHibernateProperties(hibernateProperties()); return sessionFactoryBean; }   With Spring’s resource-loading support, you can also use wildcards in a resource path to match multiple mapping files so that you don’t need to configure their locations every time you add a new entity class. For this to work we need a ResourcePatternResolver in our configuration class. We can get this by using the ResourcePatternUtils and the ResourceLoaderAware interface. We implement the latter and use the getResourcePatternResolver method to get a ResourcePatternResolver based on the ResourceLoader.   @Configuration public class CourseConfiguration implements ResourceLoaderAware {   private ResourcePatternResolver resourcePatternResolver; ... @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader); } }   Now we can use the ResourecePatternResolver to resolve resource patterns to resources.   @Bean public LocalSessionFactoryBean sessionfactory() throws IOException { LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean(); Resource[] mappingResources = resourcePatternResolver.getResources("classpath:com/apress/springrecipes/course/*.hbm.xml"); sessionFactoryBean.setMappingLocations(mappingResources); ... return sessionFactoryBean; }   www.it-ebooks.info Chapter 10 ■ Data Access 460 You can now delete the Hibernate configuration file (i.e., hibernate.cfg.xml) because its configurations have been ported to Spring. Configuring a JPA Entity Manager Factory in Spring First of all, let’s modify JpaCourseDao to accept an entity manager factory via dependency injection, instead of creating it directly in the constructor.   package com.apress.springrecipes.course; ... import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence;   public class JpaCourseDao implements CourseDao {   private final EntityManagerFactory entityManagerFactory;   public JpaCourseDao (EntityManagerFactory entityManagerFactory) { this.entityManagerFactory = entityManagerFactory; } ... }   The JPA specification defines how you should obtain an entity manager factory in Java SE and Java EE environments. In a Java SE environment, an entity manager factory is created manually by calling the createEntityManagerFactory() static method of the Persistence class. Let’s create a bean configuration file for using JPA. Spring provides a factory bean, LocalEntityManagerFactoryBean, for you to create an entity manager factory in the IoC container. You must specify the persistence unit name defined in the JPA configuration file. You can also declare a JpaCourseDao instance under Spring’s management.   package com.apress.springrecipes.course.config;   import com.apress.springrecipes.course.CourseDao; import com.apress.springrecipes.course.JpaCourseDao; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;   @Configuration public class CourseConfiguration {   @Bean public CourseDao courseDao() { return new JpaCourseDao(entityManagerFactory().getObject()); }   www.it-ebooks.info Chapter 10 ■ Data Access 461 @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean(); emf.setPersistenceUnitName("course"); return emf; } }   Now, you can test this JpaCourseDao instance with the Main class by retrieving it from the Spring IoC container.   package com.apress.springrecipes.course; ... import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;   public class Main {   public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(CourseConfiguration.class); CourseDao courseDao = context.getBean(CourseDao.class); ... } }   In a Java EE environment, you can look up an entity manager factory from a Java EE container with JNDI. In Spring, you can perform a JNDI lookup by using the JndiLocatorDelegate object (which is simpler than constructing a JndiObjectFactoryBean which would also work).   @Bean public EntityManagerFactory entityManagerFactory() throws NamingException { return JndiLocatorDelegate.createDefaultResourceRefLocator() .lookup("jpa/coursePU", EntityManagerFactory.class); }   LocalEntityManagerFactoryBean creates an entity manager factory by loading the JPA configuration file (i.e., persistence.xml). Spring supports a more flexible way to create an entity manager factory by another factory bean, LocalContainerEntityManagerFactoryBean. It allows you to override some of the configurations in the JPA configuration file, such as the data source and database dialect. So, you can take advantage of Spring’s data access facilities to configure the entity manager factory.   @Configuration public class CourseConfiguration { ... @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean(); emf.setPersistenceUnitName("course"); emf.setDataSource(dataSource()); emf.setJpaVendorAdapter(jpaVendorAdapter()); return emf; }   www.it-ebooks.info Chapter 10 ■ Data Access 462 private JpaVendorAdapter jpaVendorAdapter() { HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter(); jpaVendorAdapter.setShowSql(true); jpaVendorAdapter.setGenerateDdl(true); jpaVendorAdapter.setDatabasePlatform(DerbyTenSevenDialect.class.getName()); return jpaVendorAdapter; }   @Bean public DataSource dataSource() { SimpleDriverDataSource dataSource = new SimpleDriverDataSource(); dataSource.setDriverClass(ClientDriver.class); dataSource.setUrl("jdbc:derby://localhost:1527/course;create=true"); dataSource.setUsername("app"); dataSource.setPassword("app"); return dataSource; } }   In the preceding bean configurations, you inject a data source into this entity manager factory. It will override the database settings in the JPA configuration file. You can set a JPA vendor adapter to LocalContainerEntityManagerFactoryBean to specify JPA engine–specific properties. With Hibernate as the underlying JPA engine, you should choose HibernateJpaVendorAdapter. Other properties that are not supported by this adapter can be specified in the jpaProperties property. Now your JPA configuration file (i.e., persistence.xml) can be simplified as follows because its configurations have been ported to Spring:     Spring also makes it possible to configure the JPA EntityManagerFactory without a persistence.xml if we want we can fully configure it in a Spring configuration file. Instead of a persistenceUnitName we need to specify the packagesToScan property. After this we can remove the persistence.xml completely.   @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean(); emf.setDataSource(dataSource()); emf.setPackagesToScan("com.apress.springrecipes.course"); emf.setJpaVendorAdapter(jpaVendorAdapter()); return emf; }  www.it-ebooks.info Chapter 10 ■ Data Access 463 10-8. Persisting Objects with Spring’s ORM Templates Problem When using an ORM framework on its own, you have to repeat certain routine tasks for each DAO operation. For example, in a DAO operation implemented with Hibernate or JPA, you have to open and close a session or an entity manager, and begin, commit, and roll back a transaction with the native API. Solution Spring’s approach to simplifying an ORM framework’s usage is the same as JDBC’s—by defining template classes and DAO support classes. Also, Spring defines an abstract layer on top of different transaction management APIs. For different ORM frameworks, you only have to pick up a corresponding transaction manager implementation. Then, you can manage transactions for them in a similar way. In Spring’s data access module, the support for different data access strategies is consistent. Table 10-4 compares the support classes for JDBC, Hibernate, and JPA. Table 10-4.  Spring’s Support Classes for Different Data Access Strategies Support Class JDBC Hibernate JPA Template class JdbcTemplate HibernateTemplate - DAO support JdbcDaoSupport HibernateDaoSupport - Transaction DataSourceTransaction HibernateTransaction JpaTransactionManager Note■■   Before Spring 4.0 there were also JpaTemplate and JpaDaoSupport classes; however they have been removed in favor of using plain JPA (see Recipe 10-10). Spring defines the HibernateTemplate class (for Hibernate 3 and 4) to provide template methods for different versions of Hibernate minimize the effort involved in using them. The template methods in HibernateTemplate ensure that Hibernate will be opened and closed properly. They will also have native Hibernate transactions participate in Spring-managed transactions. As a result, you will be able to manage transactions declaratively for your Hibernate and JDBC DAOs without any boilerplate transaction code. Note■■   The template approach should be considered deprecated in favor of using either a plain SessionFactory or EntityManager (see respectively Recipes 10-9 and 10-10). www.it-ebooks.info Chapter 10 ■ Data Access 464 How It Works Using a Hibernate Template First, the HibernateCourseDao class can be simplified as follows with the help of Spring’s HibernateTemplate:   package com.apress.springrecipes.course; ... import org.springframework.orm.hibernate4.HibernateTemplate; import org.springframework.transaction.annotation.Transactional;   public class HibernateCourseDao implements CourseDao {   private HibernateTemplate hibernateTemplate;   public void setHibernateTemplate(HibernateTemplate hibernateTemplate) { this.hibernateTemplate = hibernateTemplate; }   @Transactional public void store(Course course) { hibernateTemplate.saveOrUpdate(course); }   @Transactional public void delete(Long courseId) { Course course = (Course) hibernateTemplate.get(Course.class, courseId); hibernateTemplate.delete(course); }   @Transactional(readOnly = true) public Course findById(Long courseId) { return (Course) hibernateTemplate.get(Course.class, courseId); }   @Transactional(readOnly = true) public List findAll() { return hibernateTemplate.find("from Course"); } }   In this DAO implementation, you declare all the DAO methods to be transactional with the @Transactional annotation. Among these methods, findById() and findAll() are read-only. The template methods in HibernateTemplate are responsible for managing the sessions and transactions. If there are multiple Hibernate operations in a transactional DAO method, the template methods will ensure that they will run within the same session and transaction. As a result, you have no need to deal with the Hibernate API for session and transaction management. The HibernateTemplate class is thread-safe, so you can declare a single instance of it in the bean configuration file for Hibernate and inject this instance into all Hibernate DAOs. A HibernateTemplate instance requires the sessionFactory property to be set. You can inject this property by either setter method or constructor argument.   @Configuration @EnableTransactionManagement public class CourseConfiguration {   www.it-ebooks.info Chapter 10 ■ Data Access 465 @Bean public CourseDao courseDao() { return new HibernateCourseDao(hibernateTemplate()); }   @Bean public HibernateTemplate hibernateTemplate() { return new HibernateTemplate(sessionfactory().getObject()); }   @Bean public LocalSessionFactoryBean sessionfactory() { ... }   } @Bean public PlatformTransactionManager transactionManager() { return new HibernateTransactionManager(sessionfactory().getObject()); }   To enable declarative transaction management for the methods annotated with @Transactional, you have to add the @EnableTransactionManagement annotation to your configuration class. By default, it will look for a transaction manager with the name transactionManager, so you have to declare a HibernateTransactionManager instance with that name. HibernateTransactionManager requires the session factory property to be set. It will manage transactions for sessions opened through this session factory. Another advantage of HibernateTemplate is that they will translate native Hibernate exceptions into exceptions in Spring’s DataAccessException hierarchy. This allows consistent exception handling for all the data access strategies in Spring. For instance, if a database constraint is violated when persisting an object, Hibernate will throw an org.hibernate.exception.ConstraintViolationException, while JPA will throw a javax.persistence.EntityExistsException. These exceptions will be translated by HibernateTemplate into a DataIntegrityViolationException, which is a subclass of Spring’s DataAccessException. If you want to get access to the underlying Hibernate session in HibernateTemplate in order to perform native Hibernate operations, you can implement the HibernateCallback interface and pass its instance to the execute() method of the template. This will give you a chance to use any implementation-specific features directly if there’s not sufficient support already available from the template implementations.   hibernateTemplate.execute(new HibernateCallback() { public Object doInHibernate(Session session) throws HibernateException, SQLException { // ... anything you can imagine doing can be done here. // Cache invalidation, for example... } };  Extending the Hibernate DAO Support Classes Your Hibernate DAO can extend HibernateDaoSupport to have the setSessionFactory() and setHibernateTemplate() methods inherited. Then, in your DAO methods, you can simply call the getHibernateTemplate() method to retrieve the template instance.   package com.apress.springrecipes.course; ... import org.springframework.orm.hibernate4.support.HibernateDaoSupport; import org.springframework.transaction.annotation.Transactional;   www.it-ebooks.info Chapter 10 ■ Data Access 466 public class HibernateCourseDao extends HibernateDaoSupport implements CourseDao {   @Transactional public void store(Course course) { getHibernateTemplate().saveOrUpdate(course); }   @Transactional public void delete(Long courseId) { Course course = getHibernateTemplate().get(Course.class,courseId); return getHibernateTemplate().delete(Course.class,courseId); }   @Transactional(readOnly = true) public Course findById(Long courseId) { return getHibernateTemplate().get(Course.class, courseId); }   @Transactional(readOnly = true) public List findAll() { return getHibernateTemplate().find("from Course"); } }   Because HibernateCourseDao inherits the setSessionFactory() and setHibernateTemplate() methods, you can inject either of them into your DAO so that you can retrieve the HibernateTemplate instance. If you inject a session factory, you will be able to delete the HibernateTemplate declaration.   @Bean public CourseDao courseDao() { HibernateCourseDao courseDao = new HibernateCourseDao(); courseDao.setSessionFactory(sessionfactory().getObject()); return courseDao; } 10-9. Persisting Objects with Hibernate’s Contextual Sessions Problem Spring’s HibernateTemplate can simplify your DAO implementation by managing sessions and transactions for you. However, using HibernateTemplate means your DAO has to depend on Spring’s API. Tip■■   This is the recommended approach instead of the template-based approach! www.it-ebooks.info Chapter 10 ■ Data Access 467 Solution An alternative to Spring’s HibernateTemplate is to use Hibernate’s contextual sessions. In Hibernate 3, a session factory can manage contextual sessions for you and allows you to retrieve them by the getCurrentSession() method on org.hibernate.SessionFactory. Within a single transaction, you will get the same session for each getCurrentSession() method call. This ensures that there will be only one Hibernate session per transaction, so it works nicely with Spring’s transaction management support. How It Works To use the contextual session approach, your DAO methods require access to the session factory, which can be injected via a setter method or a constructor argument. Then, in each DAO method, you get the contextual session from the session factory and use it for object persistence.   package com.apress.springrecipes.course; ... import org.hibernate.Query; import org.hibernate.SessionFactory; import org.springframework.transaction.annotation.Transactional;   public class HibernateCourseDao implements CourseDao {   private SessionFactory sessionFactory;   public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; }   @Transactional public void store(Course course) { sessionFactory.getCurrentSession().saveOrUpdate(course); }   @Transactional public void delete(Long courseId) { Course course = (Course) sessionFactory.getCurrentSession().get(Course.class, courseId); sessionFactory.getCurrentSession().delete(course); }   @Transactional(readOnly = true) public Course findById(Long courseId) { return (Course) sessionFactory.getCurrentSession().get(Course.class, courseId); }   @Transactional(readOnly = true) public List findAll() { Query query = sessionFactory.getCurrentSession().createQuery("from Course"); return query.list(); } }   www.it-ebooks.info Chapter 10 ■ Data Access 468 Note that all your DAO methods must be made transactional. This is required because Spring integrates with Hibernate through Hibernate’s Contextual Session support. Spring has its own implementation of the CurrentSessionContext interface from Hibernate. It will attempt to find a transaction and then fail, complaining that no Hibernate session’s been bound to the thread. You can achieve this by annotating each method or the entire class with @Transactional. This ensures that the persistence operations within a DAO method will be executed in the same transaction and hence by the same Session. Moreover, if a service layer component’s method calls multiple DAO methods, and it propagates its own transaction to these methods, then all these DAO methods will run within the same session as well. Caution■■   When configuring Hibernate with Spring make sure not to set the hibernate.current_session_ context_class property, as that will interfere with Springs’ ability to properly manage the transactions. You should only set this property when you are in need of JTA transactions. In the bean configuration file, you have to declare a HibernateTransactionManager instance for this application and enable declarative transaction management via @EnableTransactionManagement.   @Configuration @EnableTransactionManagement public class CourseConfiguration {   @Bean public CourseDao courseDao() { return new HibernateCourseDao(sessionfactory().getObject()); } @Bean public PlatformTransactionManager transactionManager() { return new HibernateTransactionManager(sessionfactory().getObject()); } }   Remember that HibernateTemplate will translate the native Hibernate exceptions into exceptions in Spring’s DataAccessException hierarchy. This allows consistent exception handling for different data access strategies in Spring. However, when calling the native methods on a Hibernate session, the exceptions thrown will be of native type HibernateException. If you want the Hibernate exceptions to be translated into Spring’s DataAccessException for consistent exception handling, you have to apply the @Repository annotation to your DAO class that requires exception translation.   package com.apress.springrecipes.course.hibernate; ... import org.springframework.stereotype.Repository;   @Repository public class HibernateCourseDao implements CourseDao { ... }   www.it-ebooks.info Chapter 10 ■ Data Access 469 A PersistenceExceptionTranslationPostProcessor takes care of translating the native Hibernate exceptions into data access exceptions in Spring’s DataAccessException hierarchy. This bean post processor will only translate exceptions for beans annotated with @Repository. When using Java based configuration this bean is automatically registered in the AnnotationConfigApplicationContext, hence there is no need to explicitly declare a bean for it. In Spring, @Repository is a stereotype annotation. By annotating it, a component class can be auto-detected through component scanning. You can assign a component name in this annotation and have the session factory auto-wired by the Spring IoC container with @Autowired.   package com.apress.springrecipes.course.hibernate; ... import org.hibernate.SessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository;   @Repository("courseDao") public class HibernateCourseDao implements CourseDao {   private SessionFactory sessionFactory;   @Autowired public HibernateCourseDao (SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } ... }   Then, you can simply add the @ComponentScan annotation and delete the original HibernateCourseDao bean declaration.   @Configuration @EnableTransactionManagement @ComponentScan("com.apress.springrecipes.course") public class CourseConfiguration { ... } 10-10. Persisting Objects with JPA’s Context Injection Problem In a Java EE environment, a Java EE container can manage entity managers for you and inject them into your EJB components directly. An EJB component can simply perform persistence operations on an injected entity manager without caring much about the entity manager creation and transaction management. Solution Originally, the @PersistenceContext annotation is used for entity manager injection in EJB components. Spring can also interpret this annotation by means of a bean post processor. It will inject an entity manager into a property with this annotation. Spring ensures that all your persistence operations within a single transaction will be handled by the same entity manager. www.it-ebooks.info Chapter 10 ■ Data Access 470 How It Works To use the context injection approach, you can declare an entity manager field in your DAO and annotate it with the @PersistenceContext annotation. Spring will inject an entity manager into this field for you to persist your objects.   package com.apress.springrecipes.course; ... import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query;   import org.springframework.transaction.annotation.Transactional;   public class JpaCourseDao implements CourseDao {   @PersistenceContext private EntityManager entityManager;   @Transactional public void store(Course course) { entityManager.merge(course); }   @Transactional public void delete(Long courseId) { Course course = entityManager.find(Course.class, courseId); entityManager.remove(course); }   @Transactional(readOnly = true) public Course findById(Long courseId) { return entityManager.find(Course.class, courseId); }   @Transactional(readOnly = true) public List findAll() { TypedQuery query = entityManager.createQuery("from Course", Course.class); return query.getResultList(); } }   You can annotate each DAO method or the entire DAO class with @Transactional to make all these methods transactional. It ensures that the persistence operations within a DAO method will be executed in the same transaction and hence by the same entity manager. In the bean configuration file, you have to declare a JpaTransactionManager instance and enable declarative transaction management via@EnableTransactionManagement. A PersistenceAnnotationBeanPostProcessor instance is registered automatically when using Java based config, to inject entity managers into properties annotated with @PersistenceContext.   www.it-ebooks.info Chapter 10 ■ Data Access 471 package com.apress.springrecipes.course.config;   import com.apress.springrecipes.course.CourseDao; import com.apress.springrecipes.course.JpaCourseDao; import org.apache.derby.jdbc.ClientDriver; import org.hibernate.dialect.DerbyTenSevenDialect; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.SimpleDriverDataSource; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.JpaVendorAdapter; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement;   import javax.sql.DataSource;   @Configuration @EnableTransactionManagement public class CourseConfiguration {   @Bean public CourseDao courseDao() { return new JpaCourseDao(); }   @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean(); emf.setDataSource(dataSource()); emf.setJpaVendorAdapter(jpaVendorAdapter()); return emf; }   private JpaVendorAdapter jpaVendorAdapter() { HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter(); jpaVendorAdapter.setShowSql(true); jpaVendorAdapter.setGenerateDdl(true); jpaVendorAdapter.setDatabasePlatform(DerbyTenSevenDialect.class.getName()); return jpaVendorAdapter; }   @Bean public PlatformTransactionManager transactionManager() { return new JpaTransactionManager(entityManagerFactory().getObject()); }   @Bean public DataSource dataSource() { ... }   }   www.it-ebooks.info Chapter 10 ■ Data Access 472 The PersistenceAnnotationBeanPostProcessor can also inject the entity manager factory into a property with the @PersistenceUnit annotation. This allows you to create entity managers and manage transactions by yourself. It’s no different from injecting the entity manager factory via a setter method.   package com.apress.springrecipes.course; ... import javax.persistence.EntityManagerFactory; import javax.persistence.PersistenceUnit;   public class JpaCourseDao implements CourseDao { @PersistenceContext private EntityManager entityManager;   @PersistenceUnit private EntityManagerFactory entityManagerFactory; ... }   When calling native methods on a JPA entity manager, the exceptions thrown will be of native type PersistenceException, or other Java SE exceptions like IllegalArgumentException and IllegalStateException. If you want JPA exceptions to be translated into Spring’s DataAccessException, you have to apply the @Repository annotation to your DAO class.   package com.apress.springrecipes.course; ... import org.springframework.stereotype.Repository;   @Repository("courseDao") public class JpaCourseDao implements CourseDao { ... }   A PersistenceExceptionTranslationPostProcessor instance will translate the native JPA exceptions into exceptions in Spring’s DataAccessException hierarchy. When using Java based configuration this bean is automatically registered in the AnnotationConfigApplicationContext, hence there is no need to explicitly declare a bean for it. 10-11. Simplify JPA with Spring Data JPA Problem Writing data access code, even with JPA, can be a tedious and repetitive task. You often need access to the EntityManager or EntityManagerFactory and have to create queries. Not to mention the fact that when one has a lot of dao’s the repetitive declaration of findById, findAll methods for all different entities. Solution Spring Data JPA allows you, just as Spring itself, to focus on the parts that are important and not on the boilerplate needed to accomplish this. It also provides default implementations for the most commonly used data access methods (i.e., findAll, delete, save etc.). www.it-ebooks.info Chapter 10 ■ Data Access 473 How It Works To use Spring Data JPA we have to extend one of its interfaces, these interfaces are detected and a default implementation of that repository is generated at runtime. In most cases it is enough to extend the CrudRepository interface.   package com.apress.springrecipes.course;   import com.apress.springrecipes.course.Course; import org.springframework.data.repository.CrudRepository;   public interface CourseRepository extends CrudRepository{}   This is enough to be able to do all necessary CRUD actions for the Course entity. When extending the Spring Data interfaces we have to specify the type, Course, and the type of the primary key, Long, this information is needed to generate the repository at runtime. Note■■   You could also extend JpaRepository which adds some JPA specific methods (flush, saveAndFlush) and provides query methods with paging/sorting capabilities. Next we need to enable detection of the Spring Data enabled repositories for this we can use the @EnableJpaRepositories annotation provided by Spring Data JPA.   @Configuration @EnableTransactionManagement @EnableJpaRepositories("com.apress.springrecipes.course") public class CourseConfiguration { ... }   This will bootstrap Spring Data JPA and will construct a usable repository. By default all repository methods are marked with @Transactional so no additional annotations are needed. Now, you can test this CourseRepository instance with the Main class by retrieving it from the Spring IoC container.   package com.apress.springrecipes.course.datajpa; ... import org.springframework.context.Applic