Red Hat JBoss Web Framework Kit 2.7

Seam Guide

for use with Red Hat JBoss Enterprise Application Platform

Red Hat Customer Content Services

Legal Notice

Copyright © 2015 Red Hat, Inc.
This document is licensed by Red Hat under the Creative Commons Attribution-ShareAlike 3.0 Unported License. If you distribute this document, or a modified version of it, you must provide attribution to Red Hat, Inc. and provide a link to the original. If the document is modified, all Red Hat trademarks must be removed.
Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law.
Red Hat, Red Hat Enterprise Linux, the Shadowman logo, JBoss, MetaMatrix, Fedora, the Infinity Logo, and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries.
Linux® is the registered trademark of Linus Torvalds in the United States and other countries.
Java® is a registered trademark of Oracle and/or its affiliates.
XFS® is a trademark of Silicon Graphics International Corp. or its subsidiaries in the United States and/or other countries.
MySQL® is a registered trademark of MySQL AB in the United States, the European Union and other countries.
Node.js® is an official trademark of Joyent. Red Hat Software Collections is not formally related to or endorsed by the official Joyent Node.js open source or commercial project.
The OpenStack® Word Mark and OpenStack Logo are either registered trademarks/service marks or trademarks/service marks of the OpenStack Foundation, in the United States and other countries and are used with the OpenStack Foundation's permission. We are not affiliated with, endorsed or sponsored by the OpenStack Foundation, or the OpenStack community.
All other trademarks are the property of their respective owners.

Abstract

This book is a reference guide for the Seam framework shipped with Red Hat JBoss Web Framework Kit, and its patch releases.
1. Seam Deprecation
2. Introduction to Red Hat JBoss Web Framework Kit
2.1. About Red Hat JBoss Web Framework Kit
2.2. About the JBoss Web Framework Kit Tiers
2.3. About the JBoss Web Framework Kit Distribution
3. Seam Tutorial
3.1. Using the Seam examples
3.1.1. Running the examples on Red Hat JBoss Enterprise Application Platform
3.1.2. Running the example tests
3.2. Your first Seam application: the registration example
3.2.1. Understanding the code
3.2.2. How it works
3.3. Clickable lists in Seam: the messages example
3.3.1. Understanding the code
3.3.2. How it works
3.4. A complete Seam application: the Hotel Booking example
3.4.1. Introduction
3.4.2. Overview of the booking example
3.4.3. Understanding Seam conversations
3.4.4. The Seam Debug Page
3.5. Nested conversations: extending the Hotel Booking example
3.5.1. Introduction
3.5.2. Understanding Nested Conversations
3.6. Bookmarkable URLs with the Blog example
3.6.1. Using "pull"-style MVC
3.6.2. Bookmarkable search results page
3.6.3. Using "push"-style MVC in a RESTful application
4. Migration from 2.2 to 2.3
4.1. Migration of XML Schemas
4.1.1. Seam schema migration
4.1.2. Java EE 6 schema changes
4.2. Java EE 6 upgrade
4.2.1. Using Bean Validation standard instead of Hibernate Validator
4.2.2. Migration of JSF 1 to JSF 2 Facelets templates
4.2.3. Using s:validate and s:validateAll in JSF 2
4.2.4. Migration to JPA 2.0
4.2.5. Using compatible JNDI for resources
4.3. Red Hat JBoss Enterprise Application Platform 6 deployment
4.3.1. Deployment changes
4.3.2. Datasource migration
4.4. Changes in the testing framework
4.5. Moving from JBoss Cache to Infinispan Tree
4.6. Dependency changes when using Maven
4.6.1. Seam Bill of Materials
5. Getting started with seam-gen
5.1. Before you start
5.2. Setting up a new project
5.3. Creating a new action
5.4. Creating a form with an action
5.5. Generating an application from an existing database
5.6. Generating an application from existing JPA/EJB3 entities
5.7. Deploying the application as an EAR
5.8. Seam and incremental hot deployment
6. The contextual component model
6.1. Seam contexts
6.1.1. Stateless context
6.1.2. Event context
6.1.3. Page context
6.1.4. Conversation context
6.1.5. Session context
6.1.6. Application context
6.1.7. Context variables
6.1.8. Context search priority
6.1.9. Concurrency model
6.2. Seam components
6.2.1. Stateless session beans
6.2.2. Stateful session beans
6.2.3. Entity beans
6.2.4. JavaBeans
6.2.5. Message-driven beans
6.2.6. Interception
6.2.7. Component names
6.2.8. Defining the component scope
6.2.9. Components with multiple roles
6.2.10. Built-in components
6.3. Bijection
6.4. Life cycle methods
6.5. Conditional installation
6.6. Logging
6.7. The Mutable interface and @ReadOnly
6.8. Factory and manager components
7. Configuring Seam components
7.1. Configuring components through property settings
7.2. Configuring components via components.xml
7.3. Fine-grained configuration files
7.4. Configurable property types
7.5. Using XML Namespaces
8. Events, interceptors and exception handling
8.1. Seam events
8.2. Page actions
8.3. Page parameters
8.3.1. Mapping request parameters to the model
8.4. Propagating request parameters
8.5. URL rewriting with page parameters
8.6. Conversion and Validation
8.7. Navigation
8.8. Fine-grained files for defining navigation, page actions and parameters
8.9. Component-driven events
8.10. Contextual events
8.11. Seam interceptors
8.12. Managing exceptions
8.12.1. Exceptions and transactions
8.12.2. Enabling Seam exception handling
8.12.3. Using annotations for exception handling
8.12.4. Using XML for exception handling
8.12.5. Some common exceptions
9. Conversations and workspace management
9.1. Seam's conversation model
9.2. Nested conversations
9.3. Starting conversations with GET requests
9.4. Requiring a long-running conversation
9.5. Using <s:link> and <s:button>
9.6. Success messages
9.7. Natural conversation IDs
9.8. Creating a natural conversation
9.9. Redirecting to a natural conversation
9.10. Workspace management
9.10.1. Workspace management and JSF navigation
9.10.2. The conversation switcher
9.10.3. The conversation list
9.10.4. Breadcrumbs
9.11. Conversational components and JSF component bindings
9.12. Concurrent calls to conversational components
9.12.1. How should we design our conversational AJAX application?
9.12.2. Dealing with errors
9.12.3. RichFaces (Ajax4jsf)
10. Seam and Object/Relational Mapping
10.1. Introduction
10.2. Seam managed transactions
10.2.1. Disabling Seam-managed transactions
10.2.2. Configuring a Seam transaction manager
10.2.3. Transaction synchronization
10.3. Seam-managed persistence contexts
10.3.1. Using a Seam-managed persistence context with JPA
10.3.2. Using a Seam-managed Hibernate session
10.3.3. Seam-managed persistence contexts and atomic conversations
10.4. Using the JPA "delegate"
10.5. Using EL in EJB-QL/HQL
10.6. Using Hibernate filters
11. JSF form validation in Seam
12. Groovy integration
12.1. Groovy introduction
12.2. Writing Seam applications in Groovy
12.2.1. Writing Groovy components
12.2.2. Seam component
12.2.3. seam-gen
12.3. Deployment
12.3.1. Deploying Groovy code
12.3.2. Native .groovy file deployment at development time
12.3.3. seam-gen
13. The Seam Application Framework
13.1. Introduction
13.2. Home objects
13.3. Query objects
13.4. Controller objects
14. Seam and JBoss Rules
14.1. Installing rules
14.2. Using rules from a Seam component
15. Security
15.1. Overview
15.2. Disabling Security
15.3. Authentication
15.3.1. Configuring an Authenticator component
15.3.2. Writing an authentication method
15.3.3. Writing a login form
15.3.4. Configuration Summary
15.3.5. Remember Me
15.3.6. Handling Security Exceptions
15.3.7. Login Redirection
15.3.8. HTTP Authentication
15.3.9. Advanced Authentication Features
15.4. Identity Management
15.4.1. Configuring IdentityManager
15.4.2. JpaIdentityStore
15.4.3. LdapIdentityStore
15.4.4. Writing your own IdentityStore
15.4.5. Authentication with Identity Management
15.4.6. Using IdentityManager
15.5. Error Messages
15.6. Authorization
15.6.1. Core concepts
15.6.2. Securing components
15.6.3. Security in the user interface
15.6.4. Securing pages
15.6.5. Securing Entities
15.6.6. Typesafe Permission Annotations
15.6.7. Typesafe Role Annotations
15.6.8. The Permission Authorization Model
15.6.9. RuleBasedPermissionResolver
15.6.10. PersistentPermissionResolver
15.7. Permission Management
15.7.1. PermissionManager
15.7.2. Permission checks for PermissionManager operations
15.8. SSL Security
15.8.1. Overriding the default ports
15.9. CAPTCHA
15.9.1. Configuring the CAPTCHA Servlet
15.9.2. Adding a CAPTCHA to a form
15.9.3. Customizing the CAPTCHA algorithm
15.10. Security Events
15.11. Run As
15.12. Extending the Identity component
15.13. OpenID
15.13.1. Configuring OpenID
15.13.2. Presenting an OpenIdLogin form
15.13.3. Logging in immediately
15.13.4. Deferring log in
15.13.5. Logging out
16. Internationalization, localization and themes
16.1. Internationalizing your application
16.1.1. Application server configuration
16.1.2. Translated application strings
16.1.3. Other encoding settings
16.2. Locales
16.3. Labels
16.3.1. Defining labels
16.3.2. Displaying labels
16.3.3. Faces messages
16.4. Timezones
16.5. Themes
16.6. Persisting locale and theme preferences via cookies
17. Seam Text
17.1. Basic formatting
17.2. Entering code and text with special characters
17.3. Links
17.4. Entering HTML
17.5. Using the SeamTextParser
18. iText PDF generation
18.1. Using PDF Support
18.1.1. Creating a document
18.1.2. Basic Text Elements
18.1.3. Headers and Footers
18.1.4. Chapters and Sections
18.1.5. Lists
18.1.6. Tables
18.1.7. Document Constants
18.2. Charting
18.3. Bar codes
18.4. Fill-in-forms
18.5. Rendering Swing/AWT components
18.6. Configuring iText
18.7. Further documentation
19. The Microsoft® Excel® spreadsheet application
19.1. Microsoft Excel support
19.2. Creating a simple workbook
19.3. Workbooks
19.4. Worksheets
19.5. Columns
19.6. Cells
19.6.1. Validation
19.6.2. Format masks
19.7. Formulas
19.8. Images
19.9. Hyperlinks
19.10. Headers and footers
19.11. Print areas and titles
19.12. Worksheet Commands
19.12.1. Grouping
19.12.2. Page breaks
19.12.3. Merging
19.13. Datatable exporter
19.14. Fonts and layout
19.14.1. Stylesheet links
19.14.2. Fonts
19.14.3. Borders
19.14.4. Background
19.14.5. Column settings
19.14.6. Cell settings
19.14.7. The datatable exporter
19.14.8. Limitations
19.15. Internationalization
19.16. Links and further documentation
20. Email
20.1. Creating a message
20.1.1. Attachments
20.1.2. HTML/Text alternative part
20.1.3. Multiple recipients
20.1.4. Multiple messages
20.1.5. Templating
20.1.6. Internationalization
20.1.7. Other Headers
20.2. Configuration
20.2.1. mailSession
20.3. Tags
21. Asynchronicity and messaging
21.1. Asynchronicity
21.1.1. Asynchronous methods
21.1.2. Asynchronous methods with the Quartz Dispatcher
21.1.3. Asynchronous events
21.1.4. Handling exceptions from asynchronous calls
21.2. Messaging in Seam
21.2.1. Configuration
21.2.2. Sending messages
21.2.3. Receiving messages using a message-driven bean
21.2.4. Receiving messages in the client
22. Caching
22.1. Using Caching in Seam
22.2. Page fragment caching
23. Web Services
23.1. Configuration and Packaging
23.2. Conversational Web Services
23.2.1. A Recommended Strategy
23.3. An example web service
23.4. RESTful HTTP web services with RESTEasy
23.4.1. RESTEasy configuration and request serving
23.4.2. Resources and providers as Seam components
23.4.3. Securing resources
23.4.4. Mapping exceptions to HTTP responses
23.4.5. Exposing entities via RESTful API
24. Remoting
24.1. Configuration
24.2. The Seam object
24.2.1. A Hello World example
24.2.2. Seam.Component
24.2.3. Seam.Remoting
24.3. Evaluating EL Expressions
24.4. Client Interfaces
24.5. The Context
24.5.1. Setting and reading the Conversation ID
24.5.2. Remote calls within the current conversation scope
24.6. Batch Requests
24.7. Working with Data types
24.7.1. Primitives / Basic Types
24.7.2. JavaBeans
24.7.3. Dates and Times
24.7.4. Enums
24.7.5. Collections
24.8. Debugging
24.9. Handling Exceptions
24.10. The Loading Message
24.10.1. Changing the message
24.10.2. Hiding the loading message
24.10.3. A Custom Loading Indicator
24.11. Controlling what data is returned
24.11.1. Constraining normal fields
24.11.2. Constraining Maps and Collections
24.11.3. Constraining objects of a specific type
24.11.4. Combining Constraints
24.12. Transactional Requests
24.13. JMS Messaging
24.13.1. Configuration
24.13.2. Subscribing to a JMS Topic
24.13.3. Unsubscribing from a Topic
24.13.4. Tuning the Polling Process
25. Seam and the Google Web Toolkit
25.1. Configuration
25.2. Preparing your component
25.3. Hooking up a GWT widget to the Seam component
25.4. GWT Ant Targets
25.5. GWT Maven plugin
26. Spring Framework integration
26.1. Injecting Seam components into Spring beans
26.2. Injecting Spring beans into Seam components
26.3. Making a Spring bean into a Seam component
26.4. Seam-scoped Spring beans
26.5. Using Spring PlatformTransactionManagement
26.6. Using a Seam-Managed Persistence Context in Spring
26.7. Using a Seam-Managed Hibernate Session in Spring
26.8. Spring Application Context as a Seam Component
26.9. Using a Spring TaskExecutor for @Asynchronous
27. Hibernate Search
27.1. Introduction
27.2. Configuration
27.3. Usage
28. Configuring Seam and packaging Seam applications
28.1. Basic Seam configuration
28.1.1. Integrating Seam with JSF and your servlet container
28.1.2. Seam Resource Servlet
28.1.3. Seam Servlet filters
28.1.4. Integrating Seam with your EJB container
28.1.5. Remember
28.2. Using Alternate JPA Providers
28.3. Configuring Seam in Java EE 6
28.3.1. Packaging
28.4. Configuring Seam without EJB
28.4.1. Boostrapping Hibernate in Seam
28.4.2. Boostrapping JPA in Seam
28.4.3. Packaging
28.5. Configuring Seam in Java SE
28.6. Deployment in Red Hat JBoss Enterprise Application Platform 6
28.7. Configuring SFSB and Session Timeouts in Red Hat JBoss Enterprise Application Platform 6
28.8. Running Seam in a Portlet
28.9. Deploying custom resources
29. Seam annotations
29.1. Annotations for component definition
29.2. Annotations for bijection
29.3. Annotations for component life cycle methods
29.4. Annotations for context demarcation
29.5. Annotations for use with Seam JavaBean components in a J2EE environment
29.6. Annotations for exceptions
29.7. Annotations for Seam Remoting
29.8. Annotations for Seam interceptors
29.9. Annotations for asynchronicity
29.10. Annotations for use with JSF
29.10.1. Annotations for use with dataTable
29.11. Meta-annotations for databinding
29.12. Annotations for packaging
29.13. Annotations for integrating with the Servlet container
30. Built-in Seam components
30.1. Context injection components
30.2. JSF-related components
30.3. Utility components
30.4. Components for internationalization and themes
30.5. Components for controlling conversations
30.6. Security-related components
30.7. JMS-related components
30.8. Mail-related components
30.9. Infrastructural components
30.10. Miscellaneous components
30.11. Special components
31. Seam JSF controls
31.1. Tags
31.1.1. Navigation Controls
31.1.2. Converters and Validators
31.1.3. Formatting
31.1.4. Seam Text
31.1.5. Form support
31.1.6. Other
31.2. Annotations
32. JBoss EL
32.1. Parameterized Expressions
32.1.1. Usage
32.1.2. Limitations and Hints
32.2. Projection
33. Performance Tuning
33.1. Bypassing Interceptors
34. Testing Seam applications
34.1. Unit testing Seam components
34.2. Integration testing Seam components
34.2.1. Configuration
34.2.2. Using JUnitSeamTest with Arquillian
34.2.3. Integration testing Seam application user interactions
35. Dependencies
35.1. JDK Dependencies
35.1.1. Oracle's JDK 6 Considerations
35.2. Project Dependencies
35.2.1. Core
35.2.2. RichFaces
35.2.3. Seam PDF
35.2.4. Seam Microsoft®Excel®
35.2.5. Drools
35.2.6. GWT
35.2.7. Groovy
35.3. Dependency Management using Maven
36. Migration from Seam 2 to Java EE and Alternatives
36.1. Initialization and life cycle callbacks
36.2. Scopes and contexts
36.3. @Install
36.4. Entities
36.5. @Startup
36.6. Special injection
36.6.1. @RequestParameter
36.6.2. Logger
36.6.3. @DataModel
36.7. XML Configuration
36.7.1. Other methods of configuration
36.8. Events
36.9. Interceptors
36.10. Seam Navigation migration to JSF
36.11. Seam 2 UI controls migration to JSF
36.12. Seam 2 components
36.12.1. Asynchronicity
36.12.2. Bijection
36.13. Security
36.13.1. Authentication
36.13.2. Authorization
36.13.3. Identity Management
36.13.4. Captcha support
36.13.5. Migrating Seam 2 Security annotations
A. Revision History

Chapter 1. Seam Deprecation

Warning

Seam 2.3 is deprecated from the release of JBoss Web Framework Kit 2.6.0. This means that no new features will be introduced to the Seam 2.3 framework in JBoss Web Framework Kit 2.6.0 or later. As a tier 1 framework, Red Hat continues to support Seam 2.3 and provide fixes under our standard support terms and conditions. For more information about the Red Hat support policy, see https://access.redhat.com/support/policy/updates/jboss_notes/.

Chapter 2. Introduction to Red Hat JBoss Web Framework Kit

2.1. About Red Hat JBoss Web Framework Kit

Red Hat JBoss Web Framework Kit is a set of enterprise-ready versions of popular open source frameworks. Together, these frameworks provide a solution for developing light and rich Java-based web applications.
JBoss Web Framework Kit comprises enterprise distributions of JBoss community web application frameworks and tested third party frameworks. These leading frameworks support fast and easy client-side and server-side application development and testing, with frameworks including RichFaces, jQuery, jQuery Mobile, Hibernate Search, Spring, and Arquillian.
The breadth of JBoss Web Framework Kit frameworks provides choice for Java application development. Each framework has been tested and certified for use in applications deployed to Red Hat JBoss Enterprise Application Platform, Red Hat JBoss Enterprise Web Server, or Red Hat OpenShift JBoss EAP cartridge. Inclusion in JBoss Web Framework Kit ensures stable versions of frameworks are available over long-term enterprise product life cycles, with regular releases for fixes and nonintrusive feature updates. Further, Red Hat JBoss Developer Studio (an Eclipse-based development environment) provides integrated project templates, quickstarts and tooling for many of the JBoss Web Framework Kit frameworks.
For the complete list of the frameworks composing JBoss Web Framework Kit and the certified platform and framework configurations, see https://access.redhat.com/site/articles/112543 on the Red Hat Customer Portal.

2.2. About the JBoss Web Framework Kit Tiers

The frameworks composing JBoss Web Framework Kit are categorized in four distinct tiers. A description of each tier and the associated Red Hat support is detailed here.
Tier 1 - Included Components
These are components that are based wholly or partly on open source technologies that support broad collaboration and where Red Hat maintains a leadership role; as such Red Hat is able to support these components and provide upgrades and fixes under our standard support terms and conditions.
Tier 2 - Tested Frameworks
These are third party frameworks where Red Hat does not have sufficient influence and does not provide upgrades and fixes under our standard support terms and conditions. Commercially reasonable support is provided by Red Hat Global Support Services for these frameworks.
Tier 3 - Frameworks in Tested Examples
These are third party frameworks where Red Hat does not have sufficient influence and does not provide upgrades and fixes under our standard support terms and conditions. Red Hat supports the examples these frameworks are used in and the generic use cases that these examples intend to demonstrate.
Tier 4 - Confirmed Frameworks
These are third party frameworks that do not receive any support from Red Hat, but Red Hat verifies that the frameworks run successfully on Red Hat JBoss Enterprise Application Platform. Frameworks and versions not listed here have not been explicitly tested and certified, and thus may be subject to support limitations.
For a list of JBoss Web Framework Kit frameworks by tier, see https://access.redhat.com/site/articles/112543 on the Red Hat Customer Portal.

2.3. About the JBoss Web Framework Kit Distribution

The frameworks composing JBoss Web Framework Kit are distributed from a range of sources and in a variety of formats:
  • The component frameworks are available from the Red Hat Customer Portal. They are distributed in two alternative formats: a binary for each framework or together as one Maven repository. In addition, the source code for each framework is provided for inspection.
  • The third party frameworks are not distributed by Red Hat and each must be obtained from its own source.
A number of defined Maven JBoss stacks are provided as part of the JBoss Web Framework Kit distribution. All of the BOMs defining the JBoss stacks are available in the Maven repository .zip file available to download from the Red Hat Customer Portal or from http://www.jboss.org/developer-materials/ on the JBoss Developer Framework website.
An extensive set of examples are also provided as part of the JBoss Web Framework Kit distribution:
  • TicketMonster is a moderately complex application demonstrating a number of the JBoss Web Framework Kit frameworks working together.
  • Quickstarts and Maven archeypes illustrate subsets of the JBoss Web Framework Kit frameworks used to create simple applications.
  • RichFaces, Snowdrop and Seam demonstrations showcase the power of each framework in web application development.
All of these examples are available from the Red Hat Customer Portal, with TicketMonster, the quickstarts, and the Maven archetypes also available from http://www.jboss.org/developer-materials/ on the JBoss Developer Framework website.

Chapter 3. Seam Tutorial

3.1. Using the Seam examples

Seam provides a number of example applications that demonstrate the use of a variety of Seam's features. This tutorial guides you through a few examples to help you start learning Seam. The Seam examples are located in the examples subdirectory of the Seam Demo file distributed with Red Hat JBoss Web Framework Kit. The first example, on registration, is in the examples/registration directory.
All examples have similar directory structure based on Maven project structure defaults:
  • The <example>-ear directory contains enterprise application submodule files such as aggregator for web application files, and EJB project.
  • The <example>-web directory contains web application submodule view-related files such as web page templates, images, and stylesheets.
  • The <example>-ejb directory contains Enterprise Java Beans components and integration tests.
  • The <example>-ftest directory contains functional tests.
  • The <example>-web/src/main/webapp directory contains view-related files such as web page templates, images, and stylesheets.
  • The <example>-[ear|ejb]/src/main/resources directory contains deployment descriptors, and other configuration files.
  • The <example>-ejb/src/main/java directory contains the application source code.

Note

Seam examples are built and run from the Maven pom.xml file, so you must have at least version 3.x of Maven installed before you get started.

3.1.1. Running the examples on Red Hat JBoss Enterprise Application Platform

Seam examples are configured for use on JBoss Enterprise Application Platform.
Set JBOSS_HOME as the environment variable. Start the application server and build examples by typing mvn install in the example's root directory. To deploy an example, change the directory to *-ear or *-web and use the command mvn jboss-as:deploy. The example gets deployed to a URL like /seam-example, where example is the name of the example folder. For instance, if JBoss Enterprise Application Platform is running on port 8080, the URL for the Registration example is http://localhost:8080/seam-registration/.
If, on the other hand, the example is packaged as a WAR, then it deploys to a URL like /${name}-web.

Note

Examples like groovybooking, hibernate, jpa, and spring can only be deployed as a WAR.

3.1.2. Running the example tests

Most examples come with a suite of JUnit and Arquillian integration tests. The easiest way to run the tests is to run the command mvn verify -Darquillian=jbossas-managed-7.

3.2. Your first Seam application: the registration example

The registration example is a simple application that allows a new user to store user name, real name, and password in the database. This example uses only basic functions to demonstrate the use of an EJB3 session bean as a JSF action listener, and the basic configuration of Seam.
The start page displays a basic form with three input fields. If a user fills these fields and submits the form a user object is saved in the database.

3.2.1. Understanding the code

The example is implemented with two Facelet templates: entity bean, and stateless session bean. This section explains the code in detail, starting from the base level.

3.2.1.1. The entity bean: User.java

You need an EJB entity bean for user data. This class defines persistence and validation declaratively through annotations. It also requires some extra annotations to define the class as a Seam component.

Example 3.1. User.java

@Entity                                                                  1
@Name("user")                                                            2
@Scope(SESSION)                                                          3
@Table(name="users")                                                     4
public class User implements Serializable
{
   private static final long serialVersionUID = 1881413500711441951L;
   
   private String username;
   private String password;                                              5
   private String name;
   
   public User(String name, String password, String username)
   {
      this.name = name;
      this.password = password;
      this.username = username;
   }
   
   public User() {}
   
   @NotNull @Size(min=5, max=15)
   public String getPassword()                                           6
   {
      return password;                                                   7
   }

   public void setPassword(String password)
   {
      this.password = password;
   }
   
   @NotNull
   public String getName()
   {
      return name;
   }

   public void setName(String name)
   {
      this.name = name;
   }
   
   @Id @NotNull @Size(min=5, max=15)
   public String getUsername()
   {
      return username;                                                   8
   }

   public void setUsername(String username)
   {
      this.username = username;
   }

}

1

The EJB3 standard @Entity annotation indicates that the User class is an entity bean.

2

A Seam component needs a component name specified by the @Name annotation. This name must be unique within the Seam application. When JSF asks Seam to resolve a context variable with a name that is the same as a Seam component name, and the context variable is currently undefined (null), Seam instantiates the component, and binds the new instance to the context variable. In this case, Seam instantiates a User the first time JSF encounters a variable named user.

3

Whenever Seam instantiates a component, it binds the new instance to a context variable in the component's default context. The default context is specified using the @Scope annotation. The User bean is a session scoped component.

4

The EJB standard @Table annotation indicates that the User class is mapped to the users table.

5

name, password, and username are persistent attributes of the entity bean. All persistent attributes define accessor methods. These attributes are required when a component is used by JSF in the render response, and update model values phase.

6

An empty constructor is required by both; EJB specification, and Seam.

7

The @NotNull and @Size annotations are part of the Hibernate Validator framework. Seam integrates Hibernate Validator and allows you use it for data validation (even if you are not using Hibernate for persistence).

8

The EJB standard @Id annotation indicates the primary key attribute of the entity bean.
In the above example, @Name and @Scope annotations are very important as these annotations establish the class as a Seam component.
The next section shows that the properties of the User class are bound directly to JSF components and populated by JSF during the update model values phase. There is no glue code to copy data back and forth between the JSP pages and the entity bean domain model.
However, entity beans do not perform transaction management or database access. So, the JSF component is not used as a JSF action listener. In this situation, use a session bean.

3.2.1.2. The stateless session bean class: RegisterAction.java

Most Seam applications use session beans as JSF action listeners, though you may also use JavaBeans.
This example application has exactly one JSF action and one session bean method attached to it. It uses a stateless session bean as the state associated with the action is retained by the User bean.
The relevant code is shown below:

Example 3.2. RegisterAction.java

@Stateless                                                                               1
@Name("register")
public class RegisterAction implements Register
{
    @In                                                                                  2
    private User user;
            
    @PersistenceContext                                                                  3
    private EntityManager em;
            
    @Logger                                                                              4
    private Log log;
            
    public String register()                                                             5
    {
        List existing = em.createQuery("select username " +
                                       "from User " +
                                       "where username = #{user.username}")              6
            .getResultList();
            
        if (existing.size()==0)
            {
                em.persist(user);
                log.info("Registered new user #{user.username}");                        7
                return "/registered.xhtml";                                              8
            }
        else
            {
                FacesMessages.instance().add("User #{user.username} already exists");    9
                return null;
            }
    }

}

1

The EJB @Stateless annotation marks this class as a stateless session bean.

2

The @In annotation marks an attribute of the bean as injected by Seam. In this case, the attribute is injected from a context variable named user (the instance variable name).

3

The EJB standard @PersistenceContext annotation is used to inject the EJB3 entity manager.

4

The Seam @Logger annotation is used to inject the component's Log instance.

5

The action listener method uses the standard EJB3 EntityManager API to interact with the database, and returns the JSF outcome. Note that, as this is a session bean, a transaction automatically starts when the register() method is called, and committed when the register() method completes.

6

Seam allows you to use a JSF EL expression inside EJB-QL. This results in an ordinary JPA setParameter() call on the standard JPA Query object.

7

The Log API allows you to easily display templated log messages that can also use JSF EL expressions.

8

JSF action listener methods return a string-valued outcome that determines the page that is displayed next. A null outcome (or a void action listener method) redisplays the previous page. In plain JSF, it is normal to always use a JSF navigation rule to determine the JSF view id from the outcome. For complex applications this indirection is useful and a good practice. However, for very simple examples like this one, Seam allows you to use the JSF view id as the outcome, eliminating the requirement for a navigation rule. Note that when you use a view id as an outcome, Seam always performs a browser redirect.

9

Seam provides a number of built-in components to help solve common problems. The FacesMessages component makes it easy to display templated error or success messages. (As of Seam 2.1, you can use StatusMessages instead to remove the semantic dependency on JSF). Built-in Seam components may be obtained by injection, or by calling the instance() method on the class of the built-in component.
Note that we did not explicitly specify a @Scope this time. Each Seam component type has a default scope, which is used if a scope is not explicitly specified. For stateless session beans, the default scope is the stateless context.
The session bean action listener performs the business and persistence logic for a mini-application. In a more complex application, a separate service layer might be necessary, but Seam allows you to implement your own strategies for application layering. You can make any application as simple, or as complex, as you want.

Note

This application is more complex than necessary for the sake of clear example code. All the application code could have been eliminated by using Seam's application framework controllers.

3.2.1.3. The session bean local interface: Register.java

The session bean requires a local interface.

Example 3.3. Register.java

@Local
public interface Register
{
     public String register();
}
That is the end of the Java code. The next level to examine is the view.

3.2.1.4. The view: register.xhtml and registered.xhtml

The view pages for a Seam application can be implemented using any technology that supports JSF. The following example is written with Facelets.

Example 3.4. register.xhtml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:s="http://jboss.org/schema/seam/taglib"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core">

<h:head>
  <title>Register New User</title>
</h:head>
<h:body>
  <f:view>
    <h:form>
      <s:validateAll>
        <h:panelGrid columns="2">
          Username: <h:inputText value="#{user.username}" required="true"/>
          Real Name: <h:inputText value="#{user.name}" required="true"/>
          Password: <h:inputSecret value="#{user.password}" required="true"/>
        </h:panelGrid>
      </s:validateAll>
      <h:messages/>
        <s:button value="Register" action="#{register.register}"/>
    </h:form>
  </f:view>
</h:body>
</html>
The only Seam-specific tag in the above example is <s:validateAll>. This JSF component tells JSF to validate all the contained input fields against the Hibernate Validator annotations specified on the entity bean.

Example 3.5. registered.xhtml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:f="http://java.sun.com/jsf/core">

  <head>
    <title>Successfully Registered New User</title>
  </head>
  <body>
    <f:view>
      Welcome, #{user.name}, you are successfully 
      registered as #{user.username}.
    </f:view>
  </body>

</html>
The above is a simple Facelets page, created with inline EL — it contains nothing specific to Seam.

3.2.1.5. The Seam component deployment descriptor: components.xml

Seam strongly values minimal configuration. The configuration files are created when you create a Seam application, and they are rarely required to be altered.
Unlike Java frameworks, Seam does not require application components to be accompanied by XML files. Most Seam applications require very few XML files, which do not tend to increase in size as the project expands.
However, it is useful to provide external configuration of some components, particularly the components that are built into Seam. The most flexible option, here, is to provide this configuration in a file called components.xml, located in the WEB-INF directory. The components.xml file can be used to tell Seam the method of finding EJB components in JNDI.

Example 3.6. components.xml

<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://jboss.org/schema/seam/components"
    xmlns:core="http://jboss.org/schema/seam/core"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://jboss.org/schema/seam/core
        http://jboss.org/schema/seam/core-2.3.xsd 
        http://jboss.org/schema/seam/components
        http://jboss.org/schema/seam/components-2.3.xsd">
            
    <core:init jndi-pattern="${jndiPattern}"/>
     
</components>
The above code configures jndiPattern property of the built-in Seam component org.jboss.seam.core.init. More information about the working of this process is available at: Section 7.2, “Configuring components via components.xml.

3.2.1.6. The web deployment description: web.xml

The presentation layer of a mini-application is deployed in a WAR, so a web deployment descriptor is required.

Example 3.7. web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://java.sun.com/xml/ns/javaee
        http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">

    <listener>
        <listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
    </listener>
    
    <context-param>
        <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
        <param-value>.xhtml</param-value>
    </context-param>
              
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.seam</url-pattern>
    </servlet-mapping>
              
    <session-config>
        <session-timeout>10</session-timeout>
    </session-config>

</web-app>
The above web.xml file configures both Seam and JSF. This configuration changes very little between Seam applications.

3.2.1.7. The JSF configuration: faces-config.xml

Most Seam applications use JSF views as the presentation layer, so faces-config.xml is usually a requirement.

Example 3.8. faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation=
              "http://java.sun.com/xml/ns/javaee
               http://java.sun.com/xml/ns/javaee/web-facesconfig_2_1.xsd"
              version="2.1">
</faces-config>
Note that JSF managed bean declarations are unnecessary because the managed beans are annotated Seam components. In Seam applications, faces-config.xml file is used less often than in plain JSF.
After setting the basic descriptors, define the orchestration: navigation rules to add functionality to the Seam application. Seam operates on the principle that process flow and configuration data are all that truly belongs in XML.
The above example does not require a navigation rule, as the view ID is embedded in the action code.

3.2.1.8. The EJB deployment descriptor: ejb-jar.xml

The ejb-jar.xml file integrates Seam with EJB3 by attaching the SeamInterceptor to all the session beans in the archive.

Example 3.9. ejb-jar.xml

<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation=
         "http://java.sun.com/xml/ns/javaee
          http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"
         version="3.0">
  
  <interceptors>
    <interceptor>
      <interceptor-class>
        org.jboss.seam.ejb.SeamInterceptor
      </interceptor-class>
    </interceptor>
  </interceptors>
  
  <assembly-descriptor>
    <interceptor-binding>
      <ejb-name>*</ejb-name>
      <interceptor-class>
        org.jboss.seam.ejb.SeamInterceptor
      </interceptor-class>
    </interceptor-binding>
  </assembly-descriptor>
  
</ejb-jar>

3.2.1.9. The JPA persistence deployment descriptor: persistence.xml

The persistence.xml file contains the location of the datasource that is required by the JPA persistence provider, and also contains some vendor-specific settings. In this case, the persistence.xml file also enables automatic schema export at startup.

Example 3.10. persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://java.sun.com/xml/ns/persistence
        http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" 
    version="2.0">

    <persistence-unit name="userDatabase">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
        <properties>
            <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
        </properties>
    </persistence-unit>
    
</persistence>

3.2.1.10. The EAR deployment descriptor: application.xml

Applications deployed as an EAR require a deployment descriptor. The descriptor file can be generated by Maven EAR plug-in if the registration application has it set up in the registration-ear/pom.xml file.

Example 3.11. registration application

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://java.sun.com/xml/ns/javaee
        http://java.sun.com/xml/ns/javaee/application_6.xsd"
    version="6">
    <display-name>registration-ear</display-name>
    <module>
        <web>
            <web-uri>registration-web.war</web-uri>
            <context-root>/seam-registration</context-root>
        </web>
    </module>
    <module>
        <ejb>registration-ejb.jar</ejb>
    </module>
    <module>
        <ejb>jboss-seam.jar</ejb>
    </module>
</application>
This deployment descriptor links modules in the enterprise archive and binds the web application to the context root /seam-registration.
You have now seen all the files in the application.

3.2.2. How it works

When the form is submitted, JSF requests Seam to resolve the user variable. The uservariable is not assigned a value yet (in any Seam context). So, Seam instantiates the user component, and returns the resulting User entity bean instance to JSF after storing the instance in the Seam session context.
The form input values are validated against the Hibernate Validator constraints specified on the User entity. If the constraints are violated, JSF redisplays the page. Otherwise, JSF binds the form input values to the properties of the User entity bean.
JSF requests Seam to resolve the register variable. Seam uses the JNDI pattern to locate the stateless session bean, wraps the bean as a Seam component, and returns it. Seam then presents this Seam component to JSF, and JSF invokes the register() action listener method.
Seam then intercepts the method call and injects the User entity from the Seam session context, before allowing the invocation to continue.
The register() method checks if a user already exists with the entered username. If so, an error message is queued with the FacesMessages component, and a null outcome is returned, causing a page redisplay. The FacesMessages component interpolates the JSF expression embedded in the message string and adds a JSF FacesMessage to the view.
If no user exists with the entered username, the "/registered.xhtml" outcome triggers a browser redirect to the registered.xhtml page. When JSF comes to render the page, it asks Seam to resolve the variable named user and uses property values of the returned User entity from Seam's session scope.

3.3. Clickable lists in Seam: the messages example

Clickable lists of database search results are a vital part of an online application. Seam provides special functionality on top of JSF to make it easier to query data with EJB-QL or HQL, and display the result as a clickable list using a JSF <h:dataTable>. The messages example demonstrates this functionality.

3.3.1. Understanding the code

The message list example has one entity bean (Message), one session bean (MessageListBean), and one JSF.

3.3.1.1. The entity bean: Message.java

The Message entity defines the title, text, date, and time of a message, and a flag indicating whether the message has been read.

Example 3.12. Message.java

@Entity
@Name("message")
@Scope(EVENT)
public class Message implements Serializable
{
   private Long id;
   private String title;
   private String text;
   private boolean read;
   private Date datetime;
   
   @Id @GeneratedValue
   public Long getId()
   {
      return id;
   }
   public void setId(Long id)
   {
      this.id = id;
   }
   
   @NotNull @Size(max=100)
   public String getTitle()
   {
      return title;
   }
   public void setTitle(String title)
   {
      this.title = title;
   }
   
   @NotNull @Lob
   public String getText()
   {
      return text;
   }
   public void setText(String text)
   {
      this.text = text;
   }
   
   @NotNull
   public boolean isRead()
   {
      return read;
   }
   public void setRead(boolean read)
   {
      this.read = read;
   }
   
   @NotNull 
   @Basic @Temporal(TemporalType.TIMESTAMP)
   public Date getDatetime()
   {
      return datetime;
   }
   public void setDatetime(Date datetime)
   {
      this.datetime = datetime;
   }
   
}

3.3.1.2. The stateful session bean: MessageManagerBean.java

This example contains a session bean (MessageManagerBean) that defines the action listener methods for both of the buttons on the form. One buttons selects a message from the list and displays the message; the other button deletes a message.
However, MessageManagerBean is also responsible for fetching the list of messages the first time you navigate to the message list page. There are various ways to navigate to the message list page, all the ways are not preceded by a JSF action. (For example, navigating to the page from your favorites will not necessarily call the JSF action.). Therefore, fetching the message list must take place in a Seam factory method, instead of in an action listener method.
To cache the list of messages in memory between server requests, make this a stateful session bean.

Example 3.13. MessageManagerBean.java

@Stateful
@Scope(SESSION)
@Name("messageManager")
public class MessageManagerBean 
    implements Serializable, MessageManager
{
    @DataModel                                                        1
    private List<Message> messageList;
        
    @DataModelSelection                                               2
    @Out(required=false)                                              3
    private Message message;
        
    @PersistenceContext(type=EXTENDED)                                4
    private EntityManager em;
        
    @Factory("messageList")                                           5
    public void findMessages()
    {
        messageList = em.createQuery("select msg " + 
                                     "from Message msg" + 
                                     "order by msg.datetime desc")
                         .getResultList();
    }
        
    public void select()                                              6
    {
        message.setRead(true);
    }
        
    public void delete()                                              7
    {
        messageList.remove(message);
        em.remove(message);
        message=null;
    }
        
    @Remove                                                           8
    public void destroy() {}
        
}

1

The @DataModel annotation exposes an attributes of type java.util.List to the JSF page as an instance of javax.faces.model.DataModel. This allows you to use the list in a JSF <h:dataTable> with clickable links for each row. In this case, the DataModel is made available in the messageList session context variable.

2

The @DataModelSelection annotation tells Seam to inject the List element that corresponds to the clicked link.

3

The @Out annotation exposes the selected value directly to the page. So every time a row of the clickable list is selected, the Message is injected to the attribute of the stateful bean, and subsequently outjected to the message event context variable.

4

This stateful bean has an EJB3 extended persistence context. The messages retrieved in the query remain in the managed state as long as the bean exists. So subsequent method calls to the stateful bean can update the messages without making an explicit call to the EntityManager.

5

The first time you navigate to the JSF page, there is no value in the messageList context variable. The @Factory annotation requests Seam to create an instance of MessageManagerBean and invoke the findMessages() method to initialize the value. findMessages()i called a factory method for messages.

6

The select() action listener method marks the selected Message as read, and updates it in the database.

7

The delete() action listener method removes the selected Message from the database.

8

All stateful session bean Seam components must define a parameterless method marked @Remove. Seam uses this method to remove stateful beans when Seam context ends, and clean up server-side states.

Note

This is a session-scoped Seam component. It is associated with the user log in the session, and all requests from a log in the session share the same instance of the component. Session-scoped components are used sparingly in Seam applications.

3.3.1.3. The session bean local interface: MessageManager.java

All session beans have a business interface.

Example 3.14. MessageManager.java

@Local
public interface MessageManager {
    public void findMessages();
    public void select();
    public void delete();
    public void destroy();
}
From this point, local interfaces are not shown in code examples. Components.xml, persistence.xml, web.xml, ejb-jar.xml, faces-config.xml, and application.xml files operate in a similar fashion as in the previous example, and go directly to the JSF.

3.3.1.4. The view: messages.xhtml

The JSF page is a straightforward use of the JSF <h:dataTable> component.

Example 3.15. messages.xhtml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:s="http://jboss.org/schema/seam/taglib"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
 <h:head>
  <title>Messages</title>
 </h:head>
 <h:body>
  <f:view>
     <h2>Message List</h2>
     <h:outputText id="noMessages" value="No messages to display" rendered="#{messageList.rowCount==0}"/>
     <h:dataTable id="messages" var="msg" value="#{messageList}" rendered="#{messageList.rowCount>0}">
        <h:column>
           <f:facet name="header">
              <h:outputText value="Read"/>
           </f:facet>
           <h:selectBooleanCheckbox id="read" value="#{msg.read}" disabled="true"/>
        </h:column>
        <h:column>
           <f:facet name="header">
              <h:outputText value="Title"/>
           </f:facet>
           <s:link id="link" value="#{msg.title}" action="#{messageManager.select}"/>
        </h:column>
        <h:column>
           <f:facet name="header">
              <h:outputText value="Date/Time"/>
           </f:facet>
           <h:outputText id="date" value="#{msg.datetime}">
              <f:convertDateTime type="both" dateStyle="medium" timeStyle="short"/>
           </h:outputText>
        </h:column>
        <h:column>
           <s:button id="delete" value="Delete" action="#{messageManager.delete}"/>
        </h:column>
     </h:dataTable>
     <h3><h:outputText id="title" value="#{message.title}"/></h3>
     <div><h:outputText id="text" value="#{message.text}"/></div>
  </f:view>
 </h:body>
</html>

3.3.2. How it works

The first time you navigate to the messages.xhtml page, the page tries to resolve the messageList context variable. As the context variable is not initialized, Seam calls the findMessages()factory method, which performs a query against the database and results in a DataModel being outjected. This DataModel provides the row data needed for rendering <h:dataTable>.
When you click <s:link>, JSF calls the select() action listener. Seam intercepts the call and injects the selected row data into the message attribute of the messageManager component. The action listener fires, marking the selected Message as read. At the end of the call, Seam outjects the selected Message to the message context variable. The EJB container commits the transaction, and the change to the Message is flushed to the database. The page is re-rendered, redisplaying the message list, and displaying the selected message below it.
If you click <s:button>, JSF calls the delete() action listener. Seam intercepts the call and injects the selected row data into the message attribute of the messageManager component. The action listener fires, removing the selected Message from the list, and calling remove() on the EntityManager. At the end of the call, Seam refreshes the messageList context variable and clears the message context variable. The EJB container commits the transaction, and deletes the Message from the database. The page is re-rendered, redisplaying the message list.

3.4. A complete Seam application: the Hotel Booking example

3.4.1. Introduction

The booking application is a complete hotel room reservation system incorporating the following features:
  • User registration
  • Login
  • Logout
  • Set password
  • Hotel search
  • Hotel selection
  • Room reservation
  • Reservation confirmation
  • Existing reservation list
Booking example
The booking application uses JSF, EJB 3.1, and Seam, along with Facelets for the view. There is also a port of this application to JSF, Facelets, Seam, JavaBeans, and Hibernate 4.
This application is extremely robust. You can open multiple windows, use the back, browser, and refresh buttons, and enter nonsensical data, but it is difficult to crash the application. Seam is designed to build straightforward robust web applications. Robustness that was previously hand-coded now comes naturally and automatically with Seam.
In the source code of the example application you can see how declarative state management and integrated validation are used to achieve robustness.

3.4.2. Overview of the booking example

The project structure is identical to that of the previous project. To install and deploy this application, refer to Section 3.1, “Using the Seam examples”. Once you have successfully started the application, you can access the application by pointing the browser to http://localhost:8080/seam-booking/
The application uses six session beans to implement the business logic for the following features:
  • AuthenticatorAction provides the log in authentication logic.
  • BookingListAction retrieves existing bookings for the currently logged in user.
  • ChangePasswordAction updates the password of the currently logged in user.
  • HotelBookingAction implements booking and confirmation functionality. This functionality is implemented as a conversation, so HotelBookingAction class is an important class in the application.
  • HotelSearchingAction implements the hotel search functionality.
  • RegisterAction registers a new system user.
Three entity beans implement the application's persistent domain model:
  • Hotel is an entity bean that represents a hotel.
  • Booking is an entity bean that represents an existing booking.
  • User is an entity bean that represents a user who can make hotel bookings.

3.4.3. Understanding Seam conversations

This tutorial concentrates on one particular piece of functionality: placing a hotel reservation. From the user's perspective, hotel search, selection, booking, and confirmation are one continuous unit of work — a conversation. However, from the application perspective, it is important that searching remains separate. This will help users to select multiple hotels from the same search results page, and open distinct conversations in separate browser tabs.
Most web application architectures do not have first class constructs to represent conversations, which makes managing conversational state problematic. Java web applications generally use a combination of several techniques. Some state is transferred in the URL, but that cannot be transferred is either added to the HttpSession or recorded to the database at the beginning and end of each request.
As the database is the least-scalable tier, it drastically reduces scalability. The extra traffic to and from the database also increases latency. In order to reduce redundant traffic, Java applications introduce a data cache to store commonly-accessed data between requests. However, invalidation is based upon an LRU policy, rather than whether the user has finished using the data. Therefore, this data cache is inefficient. This data cache is shared between concurrent transactions, which introduces issues associated with keeping the cached state consistent with that of the database.
State held in the HttpSession suffers similar issues. The HttpSession can be used to store true session data; data common to all requests between user and application. However, for data related to individual request series, HttpSession is not effective. Conversations stored in HttpSession break down quickly when dealing with multiple windows or the back button. Without careful programming, data in HttpSession can grow large, and make the session difficult to cluster. Developing mechanisms to deal with the problems these methods present (by isolating session state associated with distinct concurrent conversations, and incorporating failsafes to ensure conversation state is destroyed when a conversation is aborted) can be complicated.
Seam improves conditions by introducing conversation context as a first class construct. Conversation state is stored safely in the conversation context, with a well-defined life cycle. Also, there is no need to push data continually between the application server and the database; the conversation context is a natural cache for currently-used data.
In the following application, the conversation context is used to store stateful session beans. These beans are sometimes regarded as detrimental to scalability. However, modern application servers have sophisticated mechanisms for stateful session bean replication. JBoss Enterprise Application Platform performs fine-grained replication, replicating only altered bean attribute values. If used correctly, stateful session beans pose no scalability problems. However, if you are uncomfortable or unfamiliar with the use of stateful session beans, Seam also allows the use of POJOs.
The booking example shows one way in which stateful components with different scopes can collaborate to achieve complex behaviors. The main page of the booking application allows you to search for hotels. Search results are stored in the Seam session scope. When you navigate to a hotel, a conversation begins, and a conversation scoped component retrieves the selected hotel from the session scoped component.
The booking example also demonstrates the use of RichFaces Ajax to implement rich client behavior without handwritten JavaScript.
The search function is implemented with a session-scoped stateful session bean, similar to the one used in the message list example.

Example 3.16. HotelSearchingAction.java

@Stateful                                                                                     1
@Name("hotelSearch")
@Scope(ScopeType.SESSION)
@Restrict("#{identity.loggedIn}")                                                             2
public class HotelSearchingAction implements HotelSearching
{
   
    @PersistenceContext
    private EntityManager em;
   
    private String searchString;
    private int pageSize = 10;
    private int page;
   
    @DataModel                                                                                3
    private List<Hotel> hotels;
   
    public void find()
    {
        page = 0;
        queryHotels();
    }
    public void nextPage()
    {
        page++;
        queryHotels();
    }
      
    private void queryHotels()
    {
        hotels = 
            em.createQuery("select h from Hotel h where lower(h.name) like #{pattern} " + 
                           "or lower(h.city) like #{pattern} " + 
                           "or lower(h.zip) like #{pattern} " +
                           "or lower(h.address) like #{pattern}")
              .setMaxResults(pageSize)
              .setFirstResult( page * pageSize )
              .getResultList();
    }
   
    public boolean isNextPageAvailable()
    {
        return hotels!=null && hotels.size()==pageSize;
    }
   
    public int getPageSize() {
        return pageSize;
    }
   
    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }
   
    @Factory(value="pattern", scope=ScopeType.EVENT)
    public String getSearchPattern()
    {
        return searchString==null ? 
            "%" : '%' + searchString.toLowerCase().replace('*', '%') + '%';
    }
   
    public String getSearchString()
    {
        return searchString;
    }
   
    public void setSearchString(String searchString)
    {
        this.searchString = searchString;
    }
   
    @Remove                                                                                   4
    public void destroy() {}
}

1

The EJB standard @Stateful annotation identifies HotelSearchingAction.java class as a stateful session bean. Stateful session beans are scoped to the conversation context by default.

2

The @Restrict annotation applies a security restriction to the component. It restricts access to the component allowing only logged-in users. The security chapter explains more about security in Seam.

3

The @DataModel annotation exposes a List as a JSF ListDataModel. This makes it easy to implement clickable lists for search screens. In this case, the list of hotels is exposed to the page as a ListDataModel in the hotels conversation variable.

4

The EJB standard @Remove annotation specifies that a stateful session bean should be removed and its state should be destroyed after invocation of the annotated method. In Seam, all stateful session beans must define a parameterless method marked @Remove. This method is called when Seam destroys the session context.
The main page of the application is a Facelets page. The fragment that relates to hotel searching is shown below:

Example 3.17. main.xhtml

<div class="section">
  
    <span class="errors">
       <h:messages id="messages" globalOnly="true"/>
    </span>
    
    <h1>Search Hotels</h1>

    <h:form id="searchCriteria">
    <fieldset>
       <h:inputText id="searchString" value="#{hotelSearch.searchString}" style="width: 165px;">
        <a:ajax event="keyup" render="searchResults" listener="#{hotelSearch.find}"/>
       </h:inputText>                                                                                                                   1
       &#160;
       <a:commandButton id="findHotels" value="Find Hotels" actionListener="#{hotelSearch.find}"  render="searchResults"/>
       &#160;
       <a:status id="status">
          <f:facet id="StartStatus" name="start">
             <h:graphicImage id="SpinnerGif" value="/img/spinner.gif"/>
          </f:facet>                                                                                                                    2
       </a:status>
       <br/>
       <h:outputLabel id="MaximumResultsLabel" for="pageSize">Maximum results:</h:outputLabel>&#160;
       <h:selectOneMenu id="pageSize" value="#{hotelSearch.pageSize}">
          <f:selectItem id="PageSize5" itemLabel="5" itemValue="5"/>
          <f:selectItem id="PageSize10" itemLabel="10" itemValue="10"/>
          <f:selectItem id="PageSize20" itemLabel="20" itemValue="20"/>
       </h:selectOneMenu>
    </fieldset>
    </h:form>
    
</div>

<a:outputPanel id="searchResults">
  <div class="section">
    <h:outputText id="NoHotelsFoundMessage" value="No Hotels Found" rendered="#{hotels != null and hotels.rowCount==0}"/>
    <h:dataTable id="hotels" value="#{hotels}" var="hot" rendered="#{hotels.rowCount>0}">                                               3
        <h:column id="column1">
            <f:facet id="NameFacet" name="header">Name</f:facet>
            #{hot.name}
        </h:column>
        <h:column id="column2">
            <f:facet id="AddressFacet" name="header">Address</f:facet>
            #{hot.address}
        </h:column>
        <h:column id="column3">
            <f:facet id="CityStateFacet" name="header">City, State</f:facet>
            #{hot.city}, #{hot.state}, #{hot.country}
        </h:column> 
        <h:column id="column4">
            <f:facet id="ZipFacet" name="header">Zip</f:facet>
            #{hot.zip}
        </h:column>
        <h:column id="column5">
            <f:facet id="ActionFacet" name="header">Action</f:facet>
            <s:link id="viewHotel" value="View Hotel" action="#{hotelBooking.selectHotel(hot)}"/>
        </h:column>
    </h:dataTable>
    <s:link id="MoreResultsLink" value="More results" action="#{hotelSearch.nextPage}" rendered="#{hotelSearch.nextPageAvailable}"/>
  </div>
</a:outputPanel>                                                                                                                        4

1

The RichFaces Ajax <a:ajax> tag allows a JSF action event listener to be called by asynchronous XMLHttpRequest when a JavaScript event like keyup occurs. Also, the render attribute allows you to render a fragment of the JSF page and perform a partial page update when the asynchronous response is received.

2

The RichFaces Ajax <a:status> tag allows you to display an animated image while you wait for asynchronous requests to return.

3

The RichFaces Ajax <a:outputPanel> tag defines a region of the page that can be re-rendered by an asynchronous request.

4

The Seam <s:link> tag allows you to attach a JSF action listener to an ordinary (non-JavaScript) HTML link. The advantage of this over the standard JSF <s:link> is that it preserves the operation of "open in new window" and "open in new tab". Also notice the use of a method binding with a parameter: #{hotelBooking.selectHotel(hot)}. This is not possible in the standard Unified EL, but Seam provides an extension to the EL that allows you to use parameters on any method binding expression.
All the navigation rules are in WEB-INF/pages.xml file, and discussed in Section 8.7, “Navigation”.
The main page of the application displays search results dynamically as you type and pass a selected hotel to the selectHotel() method of HotelBookingAction.
The following code shows how the booking example application uses a conversation-scoped stateful session bean to achieve a natural cache of persistent data related to the conversation. Think of the code as a list of scripted actions that implement the various steps of the conversation.

Example 3.18. HotelBookingAction.java

@Stateful
@Name("hotelBooking")
@Restrict("#{identity.loggedIn}")
public class HotelBookingAction implements HotelBooking
{
   
    @PersistenceContext(type=EXTENDED)                                                1
    private EntityManager em;
   
    @In 
        private User user;
   
    @In(required=false) @Out
    private Hotel hotel;
   
    @In(required=false) 
    @Out(required=false)                                                              2
    private Booking booking;
     
    @In
    private FacesMessages facesMessages;
      
    @In
    private Events events;
   
    @Logger 
        private Log log;
   
    private boolean bookingValid;
   
    @Begin                                                                            3
    public void selectHotel(Hotel selectedHotel)
    {
        hotel = em.merge(selectedHotel);
    }
   
    public void bookHotel()
    {      
        booking = new Booking(hotel, user);
        Calendar calendar = Calendar.getInstance();
        booking.setCheckinDate( calendar.getTime() );
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        booking.setCheckoutDate( calendar.getTime() );
    }
   
    public void setBookingDetails()
    {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DAY_OF_MONTH, -1);
        if ( booking.getCheckinDate().before( calendar.getTime() ) )
            {
                facesMessages.addToControl("checkinDate", 
                                           "Check in date must be a future date");
                bookingValid=false;
            }
        else if ( !booking.getCheckinDate().before( booking.getCheckoutDate() ) )
            {
                facesMessages.addToControl("checkoutDate", 
                                           "Check out date must be later " + 
                                           "than check in date");
                bookingValid=false;
            }
        else
            {
                bookingValid=true;
            }
    }
   
    public boolean isBookingValid()
    {
        return bookingValid;
    }
   
    @End                                                                              4
    public void confirm()
    {
        em.persist(booking);
        facesMessages.add("Thank you, #{user.name}, your confimation number " + 
                          " for #{hotel.name} is #{booki g.id}");
        log.info("New booking: #{booking.id} for #{user.username}");
        events.raiseTransactionSuccessEvent("bookingConfirmed");
    }
   
    @End
    public void cancel() {}
   
    @Remove                                                                           5
    public void destroy() {}
}

1

A conversation-scoped stateful session bean uses an EJB3 extended persistence context, to manage entity instances for the entire life cycle of the stateful session bean.

2

The @Out annotation declares that an attribute value is outjected to a context variable after method invocations. In this case, the hotel context variable is set to the value of the hotel instance variable after every action listener invocation completes.

3

The @Begin annotation specifies that the annotated method begins a long-running conversation, so the current conversation context is not destroyed at the end of the request. Instead, the current conversation context is re-associated with every request from the current window, and destroyed either by timeout due to conversation inactivity or invocation of a matching @End method.

4

The @End annotation specifies that the annotated method ends the current long-running conversation, so the current conversation context is destroyed at the end of the request.

5

This EJB remove method is called when Seam destroys the conversation context. Do not forget to define this method!
HotelBookingAction contains all the action listener methods that implement selection, booking, and booking confirmation; and holds the state related to this work in its instance variables. This code is much cleaner and simpler than getting and setting HttpSession attributes.
Also, you can have multiple isolated conversations per log in session. Log in, run a search, and navigate to different hotel pages in multiple browser tabs. You can work on creating two different hotel reservations at the same time. If you leave one conversation inactive for a long time, Seam will eventually time out that conversation and destroy its state. If, after ending a conversation, you backtrack to a page of that conversation and try to perform an action, Seam will detect that the conversation was already ended, and redirect you to the search page.

3.4.4. The Seam Debug Page

The WAR also includes seam-debug.jar. To make the Seam debug page available, deploy seam-debug.jar in WEB-INF/lib alongside Facelets, and set the debug property of the init component as shown here:
<core:init jndi-pattern="${jndiPattern}" debug="true"/>
The debug page allows you to browse and inspect the Seam components in Seam contexts associated with your current log in session. Just point your browser at http://localhost:8080/seam-booking/debug.seam .

3.5. Nested conversations: extending the Hotel Booking example

3.5.1. Introduction

Long-running conversations allow you to easily maintain state consistency in an application, even in the face of multi-window operation and back-buttoning. However, simply beginning and ending a long-running conversation is not always enough. Depending on the requirements of the application, there can be inconsistency between user expectations and the application state.
The nested booking application extends the features of the hotel booking application to incorporate room selection. Each hotel has a list of available rooms from which you can select a room. This requires the addition of a room selection page in the hotel reservation flow.
You can now select an available room to be included in the booking. If room selection is left in the same conversation context, it can lead to issues with state consistency — if a conversation variable changes, it affects all windows operating within the same conversation context.
For example, you clone the room selection screen in a new window. You then select the Wonderful Room and proceed to the confirmation screen. To check the cost of a more expensive room, you return to the original window, select the Fantastic Suite for booking, and again proceed to confirmation. After reviewing the total cost, you return to the window showing Wonderful Room to confirm.
In this scenario, if all the states are stored in the conversation, flexibility for multi-window operation within the same conversation are limited. Nested conversations allow you to achieve correct behavior even when contexts vary within the same conversation.

3.5.2. Understanding Nested Conversations

The following code shows the behavior of the hotel booking application with intended behavior for nested conversations. Think of the code as a set of steps to be read in a sequence.

Example 3.19. RoomPreferenceAction.java

@Stateful
@Name("roomPreference")
@Restrict("#{identity.loggedIn}")
public class RoomPreferenceAction implements RoomPreference 
{

    @Logger 
        private Log log;

    @In private Hotel hotel;                                                                  1
   
    @In private Booking booking;

    @DataModel(value="availableRooms")
    private List<Room> availableRooms;

    @DataModelSelection(value="availableRooms")
    private Room roomSelection;
    
    @In(required=false, value="roomSelection")
    @Out(required=false, value="roomSelection")
    private Room room;

    @Factory("availableRooms")
    public void loadAvailableRooms()
    {
        availableRooms = hotel.getAvailableRooms(booking.getCheckinDate(), 
                                                 booking.getCheckoutDate());
        log.info("Retrieved #0 available rooms", availableRooms.size());
    }

    public BigDecimal getExpectedPrice()
    {
        log.info("Retrieving price for room #0", roomSelection.getName());
      
        return booking.getTotal(roomSelection);
    }

    @Begin(nested=true)                                                                       2
    public String selectPreference()
    {
        log.info("Room selected");
      
        this.room = this.roomSelection;                                                       3
      
        return "payment";
    }

    public String requestConfirmation()
    {
        // all validations are performed through the s:validateAll, so checks are
        // already performed
        log.info("Request confirmation from user");
      
        return "confirm";
    }

    @End(beforeRedirect=true)                                                                 4
    public String cancel()
    {
        log.info("ending conversation");

        return "cancel";
    }

    @Destroy @Remove                                                                      
        public void destroy() {}    
}

1

The hotel instance is injected from the conversation context. The hotel is loaded through an extended persistence context so that the entity remains managed throughout the conversation. This allows you to load the availableRooms through a @Factory method by simply walking the association.

2

When @Begin(nested=true) is encountered, a nested conversation is pushed onto the conversation stack. When executing within a nested conversation, components still have access to all outer conversation states. However, setting values in the nested conversation’s state container does not affect the outer conversation. In addition, nested conversations can exist concurrently stacked on the same outer conversation, allowing independent state for each.

3

The roomSelection is outjected to the conversation based on the @DataModelSelection. Note that because nested conversation has an independent context, the roomSelection is only set into the new nested conversation. If you select a different preference in another window or tab a new nested conversation is started.

4

The @End annotation pops the conversation stack and resumes the outer conversation. The roomSelection is destroyed along with the conversation context.
When you begin a nested conversation, it is pushed onto the conversation stack. In the nestedbooking example, the conversation stack consists of the external long-running conversation (the booking) and each of the nested conversations (room selections).

Example 3.20. rooms.xhtml

<div class="section">
  <h1>Room Preference</h1>
</div>

<div class="section">
  <h:form id="room_selections_form">
    <div class="section">
      <h:outputText styleClass="output" 
         value="No rooms available for the dates selected: " 
         rendered="#{availableRooms != null and availableRooms.rowCount == 0}"/>
      <h:outputText styleClass="output" 
         value="Rooms available for the dates selected: " 
         rendered="#{availableRooms != null and availableRooms.rowCount > 0}"/>
      
      <h:outputText styleClass="output" value="#{booking.checkinDate}"/>
      <h:outputText styleClass="output" value="#{booking.checkoutDate}"/>
      
      <br/><br/>
      
      <h:dataTable value="#{availableRooms}" var="room"                             1
         rendered="#{availableRooms.rowCount > 0}">
        <h:column>
          <f:facet name="header">Name</f:facet>
          #{room.name}
        </h:column>
        <h:column>
          <f:facet name="header">Description</f:facet>
          #{room.description}
        </h:column>
        <h:column>
          <f:facet name="header">Per Night</f:facet>
          <h:outputText value="#{room.price}">
            <f:convertNumber type="currency" currencySymbol="$"/>
          </h:outputText>
        </h:column>
        <h:column>
          <f:facet name="header">Action</f:facet>
          <s:link id="selectRoomPreference" 
            action="#{roomPreference.selectPreference}">Select</s:link>             2
        </h:column>
      </h:dataTable>   
    </div>
    <div class="entry">
      <div class="label">&#160;</div>
      <div class="input">
        <s:button id="cancel" value="Revise Dates" view="/book.xhtml"/>             3
      </div>
    </div>    
  </h:form>
</div>

1

When requested from EL, the #{availableRooms} are loaded by the @Factory method defined in RoomPreferenceAction. The @Factory method is executed only once to load the values into the current context as a @DataModel instance.

2

Invoking the #{roomPreference.selectPreference} action results in the row being selected and set into the @DataModelSelection. This value is outjected to the nested conversation context.

3

Revising the dates simply return to the /book.xhtml. Note that you have not yet nested a conversation (no room preference has been selected), so the current conversation can be resumed. The <s:button> component propagates the current conversation when displaying the /book.xhtml view.
The following code shows how you can confirm the booking of a selected room by extending the behavior of the HotelBookingAction.

Example 3.21. HotelBookingAction.java

@Stateful
@Name("hotelBooking")
@Restrict("#{identity.loggedIn}")
public class HotelBookingAction implements HotelBooking
{
   
   @PersistenceContext(type=EXTENDED)
   private EntityManager em;
   
   @In 
   private User user;
   
   @In(required=false) @Out
   private Hotel hotel;
   
   @In(required=false) 
   @Out(required=false)
   private Booking booking;
   
   @In(required=false)
   private Room roomSelection;
   
   @In
   private FacesMessages facesMessages;
      
   @In
   private Events events;
   
   @Logger 
   private Log log;
   
   @Begin
   public void selectHotel(Hotel selectedHotel)
   {
      log.info("Selected hotel #0", selectedHotel.getName());
      hotel = em.merge(selectedHotel);
   }
   
   public String setBookingDates()
   {
      // the result will indicate whether or not to begin the nested conversation
      // as well as the navigation.  if a null result is returned, the nested
      // conversation will not begin, and the user will be returned to the current
      // page to fix validation issues
      String result = null;

      Calendar calendar = Calendar.getInstance();
      calendar.add(Calendar.DAY_OF_MONTH, -1);

      // validate what we have received from the user so far
      if ( booking.getCheckinDate().before( calendar.getTime() ) )
      {
         facesMessages.addToControl("checkinDate", 
                                    "Check in date must be a future date");
      }
      else if ( !booking.getCheckinDate().before( booking.getCheckoutDate() ) )
      {
         facesMessages.addToControl("checkoutDate", 
                                    "Check out date must be later than check in date");
      }
      else
      {
         result = "rooms";
      }

      return result;
   }
   
   public void bookHotel()
   {      
      booking = new Booking(hotel, user);
      Calendar calendar = Calendar.getInstance();
      booking.setCheckinDate( calendar.getTime() );
      calendar.add(Calendar.DAY_OF_MONTH, 1);
      booking.setCheckoutDate( calendar.getTime() );
   }
   
   @End(root=true)                                                                          1
   public void confirm()
   {
      // on confirmation we set the room preference in the booking.  the room preference
      // will be injected based on the nested conversation we are in.
      booking.setRoomPreference(roomSelection);                                             2

      em.persist(booking);
      facesMessages.add("Thank you, #{user.name}, your confimation number" +
                        " for #{hotel.name} is #{booking.id}");
      log.info("New booking: #{booking.id} for #{user.username}");
      events.raiseTransactionSuccessEvent("bookingConfirmed");
   }
   
   @End(root=true, beforeRedirect=true)                                                     3
   public void cancel() {}
   
   @Destroy @Remove
   public void destroy() {}
}

1

Annotating an action with @End(root=true) ends the root conversation which effectively destroys the entire conversation stack. When a conversation is ended, its nested conversations are also ended. As root is the conversation that started the nested conversation, this is a simple way to destroy and release all the states associated with a workspace once the booking is confirmed.

2

The roomSelection is associated with the booking only on user confirmation. Outjecting values to the nested conversation context does not impact the outer conversation, and objects injected from the outer conversation are injected by reference. Therefore, any change in these objects is reflected in the parent conversation and other concurrent nested conversations.

3

By annotating the cancellation action with @End(root=true, beforeRedirect=true)you can destroy and release all the states associated with the workspace, before redirecting the user back to the hotel selection view.
Confirming a booking will always result in the correct hotel and room preference with the nested conversation model.

3.6. Bookmarkable URLs with the Blog example

Seam makes it easy to implement applications that keep the state on the server side. However, server-side state is not always appropriate, particularly for functionality that serves up content. For this, application state is often stored as part of the URL, so that a page can be accessed through a bookmark at any time. The blog example shows how to implement an application that supports bookmarking throughout, even on the search results page. This example demonstrates Seam's management of application state in the URL.
The blog example demonstrates the use of "pull"-style model view control (MVC). In this, the view pulls data from components as it is being rendered rather than using action listener methods to retrieve and prepare data for the view.

3.6.1. Using "pull"-style MVC

The following snippet from the index.xhtml facelets page, displays a list of recent blog entries:
<h:dataTable value="#{blog.recentBlogEntries}" var="blogEntry" rows="3">
  <h:column>
    <div class="blogEntry">
      <h3>#{blogEntry.title}</h3>
      <div>
        <s:formattedText value="#{blogEntry.excerpt==null ? 
                                blogEntry.body : blogEntry.excerpt}"/>
      </div>
      <p>
        <s:link view="/entry.xhtml" rendered="#{blogEntry.excerpt!=null}" 
                propagation="none" value="Read more...">
          <f:param name="blogEntryId" value="#{blogEntry.id}"/>
        </s:link>
      </p>
      <p>
        [Posted on&#160;
        <h:outputText value="#{blogEntry.date}">
          <f:convertDateTime timeZone="#{blog.timeZone}" 
                             locale="#{blog.locale}" type="both"/>
          </h:outputText>]
          &#160;
          <s:link view="/entry.xhtml" propagation="none" value="[Link]">
            <f:param name="blogEntryId" value="#{blogEntry.id}"/>
          </s:link>
      </p>
    </div>
  </h:column>
</h:dataTable>
If you navigate to this page from a bookmark, the #{blog.recentBlogEntries} data used by the <h:dataTable> is retrieved — "pulled" — when required, by a Seam component named blog. This flow of control is the reverse of that used in traditional action-based web frameworks like Struts.

Example 3.22. 

 @Name("blog")
@Scope(ScopeType.STATELESS)
@AutoCreate
public class BlogService 
{
   @In EntityManager entityManager;                                                                             1

   @Unwrap                                                                                                      2
   public Blog getBlog()
   {
      return (Blog) entityManager.createQuery("select distinct b from Blog b left join fetch b.blogEntries")
         .setHint("org.hibernate.cacheable", true)
         .getSingleResult();
   }
}

1

The blog component uses a seam-managed persistence context. Unlike the other examples, this persistence context is managed by Seam, instead of the EJB3 container. The persistence context spans the entire web request, allowing you to avoid exceptions that occur when accessing unfetched associations in the view.

2

The @Unwrap annotation requests Seam for the return value of the Blog method, instead of the actual BlogService component to clients. This is the Seam manager component pattern.
This method stores the basic view content. To bookmark form submission results, like a search results page, there are several other definitions are required.

3.6.2. Bookmarkable search results page

The blog example has a small form at the top right of each page that allows you to search for blog entries. This form is defined in the menu.xhtml file, which is included in the Facelets template template.xhtml:
<div id="search"> 
  <h:form> 
    <h:inputText value="#{searchAction.searchPattern}"/> 
    <s:button value="Search" action="/search.xhtml"/> 
  </h:form> 
</div>
To implement a bookmarkable search results page, perform a browser redirect after the search form submission is processed. As the JSF view ID is used as the action outcome, Seam automatically redirects to the view ID when the form is submitted. You can also define a navigation rule as follows:
<navigation-rule> 
  <navigation-case> 
    <from-outcome>searchResults</from-outcome> 
    <to-view-id>/search.xhtml</to-view-id> 
    <redirect/> 
  </navigation-case> 
</navigation-rule>
If you use a navigation rule, the form will look as follows:
<div id="search"> 
  <h:form> 
    <h:inputText value="#{searchAction.searchPattern}"/> 
    <s:button value="Search" action="searchResults"/> 
  </h:form> 
</div>
However, to get a bookmarkable URL like http://localhost:8080/seam-blog/search/, the values submitted with the form must be included in the URL. There is no easy way to do this with JSF, but with Seam, only two features are required: page parameters and URL rewriting. These features are defined in WEB-INF/pages.xml:
 <pages>
  <page view-id="/search.xhtml">
    <rewrite pattern="/search/{searchPattern}"/> 
    <rewrite pattern="/search"/>

    <param name="searchPattern" value="#{searchService.searchPattern}"/>

  </page>
  ...
</pages>
			

The page parameter instructs Seam to link the searchPattern request parameter to the value held by #{searchService.searchPattern}, whenever the search page is requested, and whenever a link to the search page is generated. Seam takes responsibility for maintaining the link between URL state and application state.
The URL for a search on the term book is ordinarily http://localhost:8080/seam-blog/seam/search.xhtml?searchPattern=book. Seam can simplify this URL by using a rewrite rule. The first rewrite rule, for the pattern /search/{searchPattern}, states that whenever a URL for search.xhtml contains a searchPattern request parameter, that URL can be compressed into a simplified URL. So, the earlier URL (http://localhost:8080/seam-blog/seam/search.xhtml?searchPattern= book) can be written as http://localhost:8080/seam-blog/search/book.
As with page parameters, rewriting URLs is bidirectional. This means that Seam forwards requests for the simplified URL to the correct view, and it automatically generates the simplified view; users need not construct URLs. The entire process is handled transparently. The only requirement for rewriting URLs is to enable the rewrite filter in components.xml:
<web:rewrite-filter view-mapping="/seam/*" />
The redirect takes you to the search.xhtml page:
<h:dataTable value="#{searchResults}" var="blogEntry">
 <h:column>
   <div>
      <s:link view="/entry.xhtml" propagation="none" 
              value="#{blogEntry.title}">
        <f:param name="blogEntryId" value="#{blogEntry.id}"/>
      </s:link>
      posted on 
      <h:outputText value="#{blogEntry.date}">
        <f:convertDateTime timeZone="#{blog.timeZone}" 
                           locale="#{blog.locale}" type="both"/>
      </h:outputText>
    </div>
  </h:column>
</h:dataTable>
Again, this uses "pull"-style MVC to retrieve the search results with Hibernate Search.
@Name("searchService")
public class SearchService {
   
  @In
  private FullTextEntityManager entityManager;
   
  private String searchPattern;
   
  @Factory("searchResults")
  public List<BlogEntry> getSearchResults() {
  if (searchPattern==null || "".equals(searchPattern) )
    {
      searchPattern = null;
      return entityManager.createQuery(
                "select be from BlogEntry be order by date desc"
              ).getResultList();
    }
      else
        {
          Map<String,Float> boostPerField = new HashMap<String,Float>();
          boostPerField.put( "title", 4f );
          boostPerField.put( "body", 1f );
          String[] productFields = {"title", "body"};
          QueryParser parser = new MultiFieldQueryParser(productFields, 
                               new StandardAnalyzer(), boostPerField);
          parser.setAllowLeadingWildcard(true);
          org.apache.lucene.search.Query luceneQuery;
          try
            {
              luceneQuery = parser.parse(searchPattern);
            }
          catch (ParseException e)
            {
              return null;
            }

          return entityManager
            .createFullTextQuery(luceneQuery, BlogEntry.class)
            .setMaxResults(100)
            .getResultList();
        }
    }

    public String getSearchPattern()
    {
      return searchPattern;
    }

    public void setSearchPattern(String searchPattern)
    {
      this.searchPattern = searchPattern;
    }

}

3.6.3. Using "push"-style MVC in a RESTful application

Push-style MVC is sometimes used to process RESTful pages, so Seam provides the notion of a page action. The blog example uses a page action for the blog entry page, entry.xhtml.

Note

We use push-style for the sake of an example, but this particular function will be simpler to implement with pull-style MVC.
The entryAction component works much like an action class in a traditional push-MVC action-oriented framework like Struts.
@Name("entryAction")
@Scope(STATELESS)
public class EntryAction
{
    @In Blog blog;
  
    @Out BlogEntry blogEntry;
  
    public void loadBlogEntry(String id) throws EntryNotFoundException {
        blogEntry = blog.getBlogEntry(id);
        if (blogEntry==null) throw new EntryNotFoundException(id);
    }
   
}
Page actions are also declared in pages.xml:
<pages>
  ...

  <page view-id="/entry.xhtml"> 
    <rewrite pattern="/entry/{blogEntryId}" />
    <rewrite pattern="/entry" />
    
    <param name="blogEntryId" 
           value="#{blogEntry.id}"/>
    
    <action execute="#{entryAction.loadBlogEntry(blogEntry.id)}"/>
  </page>
  
  <page view-id="/post.xhtml" login-required="true">
    <rewrite pattern="/post" />
    
    <action execute="#{postAction.post}"
            if="#{validation.succeeded}"/>
    
    <action execute="#{postAction.invalid}"
            if="#{validation.failed}"/>
    
    <navigation from-action="#{postAction.post}">
      <redirect view-id="/index.xhtml"/>
    </navigation>
  </page>

  <page view-id="*">
    <action execute="#{blog.hitCount.hit}"/>
  </page>

</pages>

Note

The example uses page actions for post validation and pageview counter. Also a parameter is used in the page action method binding. This is not a standard JSF EL feature, but Seam allows it both here and in JSF method bindings.
When the entry.xhtml page is requested, Seam first binds the blogEntryId page parameter to the model.

Note

Because of URL rewriting, the blogEntryId parameter name does not appear in the URL.
Seam then runs the page action, which retrieves the required data — the blogEntry — and places it in the Seam event context. Finally, it renders the following:
<div class="blogEntry">
  <h3>#{blogEntry.title}</h3>
  <div>
    <s:formattedText value="#{blogEntry.body}"/>
  </div>
  <p>
  [Posted on&#160;
  <h:outputText value="#{blogEntry.date}">
     <f:convertDateTime timeZone="#{blog.timeZone}" locale="#{blog.locale}" type="both"/>
  </h:outputText>]
  </p>
</div>
If a blog entry is not found in the database, the EntryNotFoundException exception is raised. This exception should result in a 404 error, and not a 500 error, so annotate the exception class as:
@ApplicationException(rollback=true)
@HttpError(errorCode=HttpServletResponse.SC_NOT_FOUND)
public class EntryNotFoundException extends Exception {
    EntryNotFoundException(String id) {
        super("entry not found: " + id);
    }
}
An alternative implementation of the example does not use the parameter in the method binding:
@Name("entryAction")
@Scope(STATELESS)
public class EntryAction {
    @In(create=true) 
        private Blog blog;
  
    @In @Out
    private BlogEntry blogEntry;
  
    public void loadBlogEntry() throws EntryNotFoundException {
        blogEntry = blog.getBlogEntry( blogEntry.getId() );
        if (blogEntry==null) throw new EntryNotFoundException(id);
    }
}
<pages> 
  ... 
  <page view-id="/entry.xhtml" action="#{entryAction.loadBlogEntry}"> 
  <param name="blogEntryId" value="#{blogEntry.id}"/> 
</page> 
  ... 
</pages>
The implementation used depends entirely upon personal preference.
The blog example also demonstrates simple password authentication, blog posting, page fragment caching, and atom feed generation.

Chapter 4. Migration from 2.2 to 2.3

This migration guide assumes that you are migrating from Seam 2.2 to Seam 2.3. If you are migrating from Seam 1.2 or 2.0, see the jboss-seam-x.y.z.Final/seam2migration.txt and jboss-seam-x.y.z.Final/seam21migration.txt guides also.

Important

Seam 2.3 has been re-architected to support features of Java EE6 on Red Hat JBoss Enterprise Application Platform 6. jBPM3 is deemed unsuitable for EE6 and JBoss Enterprise Application Platform 6. Features depending on jBPM3 (pageflows and business processes) are removed from Seam 2.3.

4.1. Migration of XML Schemas

4.1.1. Seam schema migration

Update the XML schemas for validation files that use Seam 2.2 XSDs to refer to 2.3 XSDs; notice the version change. The current namespace pattern is www.jboss.org/schema/seam/* and the schemaLocation URL is www.jboss.org/schema/seam/*_-2.3.xsd, where * is a Seam module.

Warning

The old XML namespace http://jboss.com/products/seam/* is invalid now. You must update the components.xml file with the new namespace.
The following snippet is an example of component declaration for Seam 2.2:

Example 4.1. Before migration of Seam components.xml


<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://jboss.com/products/seam/components"
     xmlns:core="http://jboss.com/products/seam/core" xmlns:persistence="http://jboss.com/products/seam/persistence"
     xmlns:security="http://jboss.com/products/seam/security"
     xmlns:theme="http://jboss.com/products/seam/theme"
     xmlns:cache="http://jboss.com/products/seam/cache"
     xmlns:web="http://jboss.com/products/seam/web"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.2.xsd
     http://jboss.com/products/seam/persistence http://jboss.com/products/seam/persistence-2.2.xsd
     http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.2.xsd
     http://jboss.com/products/seam/theme http://jboss.com/products/seam/theme-2.2.xsd
     http://jboss.com/products/seam/cache http://jboss.com/products/seam/cache-2.2.xsd
     http://jboss.com/products/seam/web http://jboss.com/products/seam/web-2.2.xsd
     http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.2.xsd">
Migrated declaration of components.xml for version 2.3:

Example 4.2. Migrated components.xml


<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://jboss.org/schema/seam/components"
     xmlns:core="http://jboss.org/schema/seam/core"
     xmlns:persistence="http://jboss.org/schema/seam/persistence"
     xmlns:security="http://jboss.org/schema/seam/security"
     xmlns:theme="http://jboss.org/schema/seam/theme"
     xmlns:cache="http://jboss.org/schema/seam/cache"
     xmlns:web="http://jboss.org/schema/seam/web"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://jboss.org/schema/seam/core http://jboss.org/schema/seam/core-2.3.xsd
     http://jboss.org/schema/seam/persistence http://jboss.org/schema/seam/persistence-2.3.xsd
     http://jboss.org/schema/seam/security http://jboss.org/schema/seam/security-2.3.xsd
     http://jboss.org/schema/seam/theme http://jboss.org/schema/seam/theme-2.3.xsd
     http://jboss.org/schema/seam/cache http://jboss.org/schema/seam/cache-2.3.xsd
     http://jboss.org/schema/seam/web http://jboss.org/schema/seam/web-2.3.xsd
     http://jboss.org/schema/seam/components http://jboss.org/schema/seam/components-2.3.xsd">
Update pages.xml files and schemas.

Example 4.3. Before migration of Seam pages.xml


<?xml version="1.0" encoding="UTF-8"?>
<pages xmlns="http://jboss.com/products/seam/pages"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.2.xsd">
       ...
</pages>

Example 4.4. After migration of Seam pages.xml


<?xml version="1.0" encoding="UTF-8"?>
<pages xmlns="http://jboss.org/schema/seam/pages"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://jboss.org/schema/seam/pages http://jboss.org/schema/seam/pages-2.3.xsd">
       ...
</pages>

4.1.2. Java EE 6 schema changes

Seam 2.3 technology upgrade includes an update to Java EE 6. Update the following descriptors:
  • persistence.xml for using JPA 2.
  • web.xml for using Servlet 3.0 and Web application.
  • application.xml for using Enterprise Java 6 application.
  • faces-config.xml to specify advanced configuration for JSF 2 (this descriptor file is optional).
Following are examples of changed headers with correct versions:

Example 4.5. persistence.xml


<persistence xmlns="http://java.sun.com/xml/ns/persistence" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"version="2.0">

Example 4.6. application.xml


<application xmlns="http://java.sun.com/xml/ns/javaee" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_6.xsd" version="6">

Example 4.7. web.xml


<web-app xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">

Example 4.8. faces-config.xml


<?xml version="1.0" encoding="UTF-8"?>
 <faces-config version="2.1"
        xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_1.xsd">

4.2. Java EE 6 upgrade

Seam 2.3 can integrate with the major upgrades in Java EE 6. You can use persistence with JPA 2, EJB 3.1, and Bean Validation. EE 6 technology upgrades require a change in the XML schema declaration. For details, see Section 4.1.2, “Java EE 6 schema changes”.

4.2.1. Using Bean Validation standard instead of Hibernate Validator

Bean Validation is a standard included in Java EE 6 as a new technology. Seam uses Hibernate Validator, which is a reference implementation.
You need to migrate from using org.hibernate.validator.* Hibernate validator annotations to javax.validation.constraint.*. Seam examples use many of the following annotations (Using Bean Validation):
  • org.hibernate.validator.Length to javax.validation.constraint.Size.
  • org.hibernate.validator.NotNull to javax.validation.constraint.NotNull.
  • org.hibernate.validator.Pattern to javax.validation.constraint.Pattern.

4.2.2. Migration of JSF 1 to JSF 2 Facelets templates

For using JSF 2 in a simple application, migrate only web.xml file. The configuration file faces-config.xml is not required. However, for information on migrating faces-config.xml file, see Example 4.8, “faces-config.xml”.
All JSF templates in your application must use facelets technology only, as JSP is deprecated.
In the facelet templates, convert <head>/<body> tags to ><h:head>/<h:body>, respectively.
The upgrade from JSF 1.x to JSF 2.x differs depending on the JSF components used (for example, Richfaces or Icefaces). You may need to upgrade libraries entirely. See a component framework documentation for details of these changes. Migration of these independent components is not covered here.

4.2.3. Using s:validate and s:validateAll in JSF 2

In Seam 2.3, JSF 2 adds bean validators to all the fields by default. The s:validateAll tag ignores the fields to which JSF 2 has added bean validators.
To use the s:validate tag or s:validateAll tag in Seam 2.3, disable the JSF default bean validator view settings. Set javax.faces.validator.DISABLE_DEFAULT_BEAN_VALIDATOR context parameter to true in the application's web.xmlfile.

4.2.4. Migration to JPA 2.0

To use JPA 2, change the version in persistence.xml file to 2.0, see Example 4.5, “persistence.xml”. The version in application.xml file should be 6 if you are using EAR (Example 4.6, “application.xml”), or version 3.0 in web.xml file if you use only WAR archives (Example 4.7, “web.xml”).

Note

Most applications can use WAR with EJB 3.1 and not require to be packaged as an EAR.
JPA 2.0 is backward compatible with JPA 1.0, so do not migrate JPA annotations or classes.

4.2.5. Using compatible JNDI for resources

Java EE 6 brings a new set of standardized global rules for creating portable JNDI syntax. Change all JNDI strings from _your_application_/#{ejbName}/local to java:app/_application-module-name_/#{ejbName}. For example, WEB-INF/components.xml jndiPattern changes from:
seam-mail/#{ejbName}/local
to:
java:app/seam-mail-ejb/#{ejbName}

4.3. Red Hat JBoss Enterprise Application Platform 6 deployment

4.3.1. Deployment changes

The next level of migration is the migration of the target runtime. Seam 2.3 uses JBoss Enterprise Application Platform 6 as its default target runtime.
Change the JNDI datasource in the persistence.xml file from java:/DefaultDS to java:jboss/datasources/ExampleDS.
Utilize refactored classloading. Classloading of bundled or provided libraries can be managed in jboss-deployment-structure.xml file or in META-INF/MANIFEST.MF file, in the Dependencies section. We recommend you to use the jboss-deployment-structure.xml file, which should be placed in the META-INF directory of your WAR or EAR application, based on your application type.
For EAR projects, the jboss-deployment-structure.xml file is located in the _your_ear_/META-INF directory.
For Web (non-ear) projects, the jboss-deployment-structure.xml file is located in the _your_war_/WEB-INF directory.
The minimal content for Seam 2.3 based applications is:

<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.0">
  <deployment>
    <dependencies>
      <module name="org.dom4j" export="true"/>
      <module name="org.apache.commons.collections" export="true"/>
      <module name="javax.faces.api" export="true"/>
      <!-- keep there only if you use JSF as view technology -->
    </dependencies>
  </deployment>  
</jboss-deployment-structure>
For further information, see the JBoss Enterprise Application Server 6 Development Guide on Class Loading and Modules.

4.3.2. Datasource migration

You can include a database descriptor (*-ds.xml) file in your project in the META-INF directory. The data source is deployed automatically when you deploy the application. The structure of the database descriptor (*-ds.xml) file is changed to be based on IronJacamar (IronJacamar). Iron-Jacamar is the JBoss JCA (Java Connector Architecture) project. Example 4.9, “Sample Seam 2.2 Datasource Descriptor File” is the former datasource for JBoss. Example 4.10, “Ironjacamar Datasource Descriptor File” is the conversion of the datasource to IronJacamar using the same driver, url, and credentials.

Example 4.9. Sample Seam 2.2 Datasource Descriptor File


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE datasources PUBLIC "-//JBoss//DTD JBOSS JCA Config 1.5//EN""http://www.jboss.org/j2ee/dtd/jboss-ds_1_5.dtd">
<datasources>
  <local-tx-datasource>
    <jndi-name>seamdiscsDatasource</jndi-name>
    <connection-url>jdbc:hsqldb:.</connection-url>
    <driver-class>org.hsqldb.jdbcDriver</driver-class>
    <user-name>sa</user-name>
    <password></password>
  </local-tx-datasource>
</datasources>

Example 4.10. Ironjacamar Datasource Descriptor File


<?xml version="1.0" encoding="UTF-8"?>
<datasources xmlns="http://www.jboss.org/ironjacamar/schema">
  <datasource jndi-name="java:/jboss/seamdiscsDatasource" enabled="true" use-java-context="true" pool-name="seamdiscs">
    <connection-url>jdbc:hsqldb:.</connection-url>
    <driver>org.hsqldb.jdbcDriver</driver>
    <security>
      <user-name>sa</user-name>
      <password></password>
    </security>
  </datasource>
</datasources>

4.4. Changes in the testing framework

Seam does not support SeamTest and JBoss Embedded legacy components (as supported in Seam 2.2).
Arquillian is the replacement for JBoss Embedded. Extend org.jboss.seam.mock.JUnitSeamTest instead of org.jboss.seam.mock.SeamTest. DBUnit testing is provided by org.jboss.seam.mock.DBJUnitSeamTest instead of org.jboss.seam.mock.DBUnitSeamTest. Assertion issues mean JUnit is the preferred testing framework over TestNG. Complete the migration to Junit and Arquillian as follows:
  1. Add the following annotation to your test class:
    @RunWith(Arquillian.class)
  2. In your test class, extend org.jboss.seam.mock.JUnitSeamTest instead of org.jboss.seam.mock.SeamTest.
  3. Add a method for creating ShrinkWrap deployment; Seam examples and integration testsuite uses helper class for this purpose. For example, see the Booking sample test modules
    jboss-seam-x.y.z.Final/examples/booking/booking-ejb/src/test/java/org/jboss/seam/example/booking/test/Deployments.java.
    
    package org.jboss.seam.example.booking.test;
     
    import java.io.File;
    import org.jboss.seam.example.booking.Booking;
    import org.jboss.shrinkwrap.api.ShrinkWrap;
    import org.jboss.shrinkwrap.api.spec.WebArchive;
    import org.jboss.shrinkwrap.resolver.api.maven.Maven;
     
    public class Deployments 
    {
      public static WebArchive bookingDeployment() 
      {
        File[] libs = Maven.resolver().loadPomFromFile("pom.xml")
          .importCompileAndRuntimeDependencies()
          .resolve("org.jboss.seam:jboss-seam")
          .withTransitivity().asFile();
        return ShrinkWrap.create(WebArchive.class, "seam-booking.war")
          .addPackage(Booking.class.getPackage())
          .addAsWebInfResource("META-INF/ejb-jar.xml", "ejb-jar.xml")
          .addAsResource("import.sql")
          .addAsResource("persistence.xml", "META-INF/persistence.xml")
          .addAsWebInfResource("components.xml")
          .addAsWebInfResource("jboss-deployment-structure.xml")
          .addAsResource("seam.properties")
          .addAsWebInfResource("web.xml")
          .addAsLibraries(libs);
      }
    }
    
  4. Add a method for creating test deployment archive as:
    
    @Deployment(name="_your_test_name_")
    @OverProtocol("Servlet 3.0")
    public static org.jboss.shrinkwrap.api.Archive<?> createDeployment(){}
    The following example is taken from the Booking example test suite:
    
    @Deployment(name="BookingTest")
    @OverProtocol("Servlet 3.0") 
    public static Archive<?> createDeployment()
    {
      return Deployments.bookingDeployment();
    }
    In the Booking example, call the already created bookingDeployment() method from the Deployment class.
  5. Add the arquillian.xml file in the root of your classpath to run Arquillian tests. The file content should specify the path to a remote or managed container and some specific options for JVM or Arquillian. An example of an arquillian file is available at
    jboss-seam-x.y.z.Final/examples/booking/booking-ejb/src/test/resources/arquillian.xml.
    
    <?xml version="1.0" encoding="UTF-8"?>
    <arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xmlns="http://jboss.org/schema/arquillian"
                xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
      <engine>
        <property name="deploymentExportPath">target/</property>
      </engine>
    
      <container qualifier="jboss" default="true">
        <configuration>
          <property name="javaVmArguments">-Xmx1024m -XX:MaxPermSize=1024m ${jacoco.agent}</property>
          <property name="serverConfig">standalone.xml</property>
        </configuration>
      </container>
    </arquillian>

4.5. Moving from JBoss Cache to Infinispan Tree

In the Seam components.xml file, change all occurrences of<cache:jboss-cache3-provider/> to <cache:infinispan-cache-provider/>.
Migrate the JBoss Cache XML configuration to Infinispan XML; you can use the migration tool provided with Infinispan. For more information see: https://docs.jboss.org/author/display/ISPN/Configuration+Migration+Tools.

4.6. Dependency changes when using Maven

All Java EE dependencies included in JBoss Enterprise Application Platform 6 are now marked as provided.

4.6.1. Seam Bill of Materials

A Bill Of Materials (BOM) is a set of dependeny elements in the <dependencyManagement> section that is used to declare dependencies and their versions used in the application. The usage of Seam BOM is shown in Example 4.11, “Seam BOM usage”. The Seam BOM is deployed in JBoss Maven repository. For further information, see the Maven Repository User Guide in the Red Hat JBoss Web Framework Kit.

Example 4.11. Seam BOM usage


<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.jboss.seam</groupId>
      <artifactId>bom</artifactId>
      <version>2.3.5.Final-redhat-1</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    ...
  </dependencies>
</dependencyManagement>
<dependencies>
  <dependency>
    <groupId>org.jboss.seam</groupId>
    <artifactId>jboss-seam</artifactId>
    <type>ejb</type>
  </dependency>
  ...
</dependencies>
For further assistance, refer to the updated Seam examples distributed with Seam.

Chapter 5. Getting started with seam-gen

Seam includes a command line utility that makes it easy to set up an Eclipse project, generate a simple Seam skeleton code, and reverse-engineer an application from a pre-existing database. This is an easy way to familiarize yourself with Seam. Seam-gen works best in conjunction with Red Hat JBoss Enterprise Application Platform.
Seam-gen can be used without Eclipse, but this guide focuses on using seam-gen with Eclipse. If you prefer not to use Eclipse, you can still follow this guide — all steps can be performed from the command line.

5.1. Before you start

JBoss Enterprise Application Platform has sophisticated support for hot redeployment of WARs and EARs. Unfortunately, due to bugs in JVM, repeat redeployment of an EAR (common during development) uses all of the JVM's perm gen space. Therefore, we recommend you to run JBoss in a JVM with a large perm gen space during development.
If you are running JBoss from JBoss IDE, you can configure this in the server launch configuration, under "VM arguments". We suggest the following values:
-Xms512m -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=512m
The minimum recommended values are:
-Xms256m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=256m
If you are running the standalone profile of JBoss from the command line, you can configure the JVM options in bin/standalone.conf.

5.2. Setting up a new project

Configure seam-gen for your environment:
cd jboss-seam-2.3.5.Final-redhat-1
./seam setup
A prompt appears for the required information:
~/workspace/jboss-seam$ ./seam setup
Buildfile: build.xml
       
init:
       
setup:
[echo] Welcome to seam-gen :-)

[input] Enter your project workspace (the directory that contains your Seam projects) [C:/Projects] [C:/Projects] /Users/pmuir/workspace

[input] Enter your JBoss AS home directory [C:/Program Files/jboss-as-7.1.1.Final] [C:/Program Files/jboss-as-7.1.1.Final]/Applications/jboss-as-7.1.1.Final
       
[input] Enter the project name [myproject] [myproject] helloworld
       
[echo] Accepted project name as: helloworld
       
[input] Select a RichFaces skin [blueSky] ([blueSky], emeraldTown, ruby, classic, japanCherry, wine, deepMarine, DEFAULT, plain)

[input] Is this project deployed as an EAR (with EJB components) or a WAR (with no EJB support) [ear]  ([ear], war, )

[input] Enter the Java package name for your session beans [com.mydomain.helloworld] [com.mydomain.helloworld]
 org.jboss.helloworld
       
[input] Enter the Java package name for your entity beans [org.jboss.helloworld] [org.jboss.helloworld]
       
[input] Enter the Java package name for your test cases [org.jboss.helloworld.test] [org.jboss.helloworld.test]
       
[input] What kind of database are you using? [h2]  ([h2], hsql, mysql, oracle, postgres, mssql, db2, sybase, enterprisedb) mysql

[input] Enter the Hibernate dialect for your database [org.hibernate.dialect.MySQLDialect] [org.hibernate.dialect.MySQLDialect]
       
[input] Enter the filesystem path to the JDBC driver jar [lib/hsqldb.jar] [lib/hsqldb.jar] /Users/pmuir/java/mysql.jar

[input] Enter JDBC driver class for your database [com.mysql.jdbc.Driver] [com.mysql.jdbc.Driver]
       
[input] Enter the JDBC URL for your database [jdbc:mysql:///test] [jdbc:mysql:///test] jdbc:mysql:///helloworld
       
[input] Enter database username [sa] [sa] pmuir
       
[input] Enter database password [] []
       
[input] skipping input as property hibernate.default_schema.new has already been set.
       
[input] Enter the database catalog name (it is OK to leave this blank) [] []
       
[input] Are you working with tables that already exist in the database? [n]  (y, [n], ) y

[input] Do you want to drop and recreate the database tables and data in import.sql each time you deploy? [n]  (y, [n], ) n    
       
[propertyfile] Creating new property file: /Users/pmuir/workspace/jboss-seam/seam-gen/build.properties
       
[echo] Installing JDBC driver jar to JBoss server
       
[echo] Type 'seam create-project' to create the new project
       
BUILD SUCCESSFUL
Total time: 1 minute 32 seconds
~/workspace/jboss-seam $
The tool provides defaults, accept the defaults by pressing enter at the prompt.
Choose the deployment type for your project, EAR deployment or WAR deployment. EAR projects support EJB 3.0 and require Java EE 5. WAR projects do not support EJB 3.0, but can be deployed in a J2EE environment. The packaging of a WAR is simple to understand. If you have installed an EJB3-ready application server like JBoss, choose EAR. Otherwise, choose WAR. The remaining document assumes an EAR deployment, however, you can follow the same steps for a WAR deployment.
If you are working with an existing data model, inform seam-gen that the tables already exist in the database.
The settings are stored in seam-gen/build.properties, but you can also modify them by running seam setup a second time.
Create a new project in your Eclipse workspace directory, by typing:
seam new-project
C:\Projects\jboss-seam>seam new-project
Buildfile: build.xml
...
new-project:
[echo] A new Seam project named 'helloworld' was created in the C:\Projects directory

[echo] Type 'seam explode' and go to http://localhost:8080/helloworld
              
[echo] Eclipse Users: Add the project into Eclipse using File > New > Project and select General > Project (not Java Project)
              
[echo] NetBeans Users: Open the project in NetBeans
              
BUILD SUCCESSFUL
Total time: 7 seconds

C:\Projects\jboss-seam>
This copies the Seam jars, dependent jars, and the JDBC driver jar to a new Eclipse project. It also generates the required resources and configuration files; a facelets template file and stylesheet, Eclipse metadata, and an Ant build script. The Eclipse project is automatically deployed to an exploded directory structure in JBoss Enterprise Application Platform when you add the project using New -> Project... -> General -> Project -> Next, typing the Project name (helloworld in this case), and then clicking Finish. Do not select Java Project from the New Project wizard.
If your default JDK in Eclipse is not a Java SE 6 JDK, select a Java SE 6 compliant JDK using Project -> Properties -> Java Compiler.
Alternatively, deploy the project from outside Eclipse by typing seam explode.
Go to http://localhost:8080/helloworld to see a welcome page. This is a facelets page, view/home.xhtml, using the template view/layout/template.xhtml. You can edit this page, or the template, in Eclipse, and see the results immediately, by clicking refresh in the browser.
The XML configuration documents are mostly standard Java EE and similar between all Seam projects. (They are so easy to write that even seam-gen can do it.)

5.3. Creating a new action

Task:

This task will show you how to create a simple web page with a stateless action method.

Procedure 5.1. 

  1. Execute the command:
    seam new-action
  2. Seam prompts for some information, and generates a new Facelets page and Seam component for your project.
    Buildfile: build.xml
    
    validate-workspace:
    
    validate-project:
    
    action-input:
        [input] Enter the Seam component name
    ping
        [input] Enter the local interface name [Ping]
    
        [input] Enter the bean class name [PingBean]
    
        [input] Enter the action method name [ping]
    
        [input] Enter the page name [ping]
    
    
    setup-filters:
    
    new-action:
         [echo] Creating a new stateless session bean component with an action method
         [copy] Copying 1 file to C:\Projects\helloworld\src\hot\org\jboss\helloworld
         [copy] Copying 1 file to C:\Projects\helloworld\src\hot\org\jboss\helloworld
         [copy] Copying 1 file to C:\Projects\helloworld\src\hot\org\jboss\helloworld\test
         [copy] Copying 1 file to C:\Projects\helloworld\src\hot\org\jboss\helloworld\test
         [copy] Copying 1 file to C:\Projects\helloworld\view
         [echo] Type 'seam restart' and go to http://localhost:8080/helloworld/ping.seam
    
    BUILD SUCCESSFUL
    Total time: 13 seconds
    C:\Projects\jboss-seam>
    
  3. As we have added a new Seam component, it is necessary to restart the exploded directory deployment. You can do this by typing seam restart, or by running the restart target in the generated project's build.xml file from within Eclipse. Alternatively, you can edit the resources/META-INF/application.xml file in Eclipse.
    You do not need to restart JBoss each time you change the application.
  4. Go to http://localhost:8080/helloworld/ping.seam and click the button. The code behind this action is in the project src directory. Add a breakpoint to the ping() method, and click the button again.

5.4. Creating a form with an action

The next step is to create a form. Type: seam new-form
Buildfile: C:\Projects\jboss-seam\seam-gen\build.xml

validate-workspace:

validate-project:

action-input:
    [input] Enter the Seam component name
hello
    [input] Enter the local interface name [Hello]

    [input] Enter the bean class name [HelloBean]

    [input] Enter the action method name [hello]

    [input] Enter the page name [hello]


setup-filters:

new-form:
     [echo] Creating a new stateful session bean component with an action method
     [copy] Copying 1 file to C:\Projects\hello\src\hot\com\hello
     [copy] Copying 1 file to C:\Projects\hello\src\hot\com\hello
     [copy] Copying 1 file to C:\Projects\hello\src\hot\com\hello\test
     [copy] Copying 1 file to C:\Projects\hello\view
     [copy] Copying 1 file to C:\Projects\hello\src\hot\com\hello\test
     [echo] Type 'seam restart' and go to http://localhost:8080/hello/hello.seam

BUILD SUCCESSFUL
Total time: 5 seconds
C:\Projects\jboss-seam>
Restart the application again, and go to http://localhost:8080/helloworld/hello.seam. Look at the generated code. Experiment with adding new fields to the form and Seam component. (Remember to restart the deployment each time you alter the Java code.)

5.5. Generating an application from an existing database

Manually create tables in your database. (To switch to a different database, run seam setup again.) Now type:seam generate-entities
Restart the deployment, and go to http://localhost:8080/helloworld. You can browse the database, edit existing objects, and create new objects. The code generated here is very simple. Seam was designed so that data access code is easy to write by hand, even without the assistance of seam-gen.

5.6. Generating an application from existing JPA/EJB3 entities

Place your existing, valid entity classes in the src/main directory. Now, type: seam generate-ui
Restart the deployment, and go to http://localhost:8080/helloworld.

5.7. Deploying the application as an EAR

Not many changes are required before deploying the application with standard Java EE 6 packaging. Remove the exploded directory by running seam unexplode. To deploy the EAR, either type seam deploy at the command prompt, or run the deploy target of the generated project build script. To undeploy, use seam undeploy or the undeploy target.
By default, the application deploys with the dev profile. The EAR includes the persistence-dev.xml and import-dev.sql files, and deploys myproject-dev-ds.xml. You can change the profile to prod profile by typing: seam -Dprofile=prod deploy
You can also define new deployment profiles for your application. Add appropriately named files to your project — for example, persistence-staging.xml, import-staging.sql, and myproject-staging-ds.xml — and select the name of the profile with -Dprofile=staging.

5.8. Seam and incremental hot deployment

Some support for incremental hot deployment is included during development when you deploy your Seam application as an exploded directory. Add the following line to components.xml file to enable debug mode in Seam and Facelets:
<core:init debug="true">

Warning

Hot deployment of Facelets does not work if the hot deployment scanner is not enabled for the server profile.
The following files may now be redeployed without requiring a full restart of the web application:
  • any Facelets page
  • any pages.xml file
To change any Java code, do a full restart of the application. In JBoss, this can be accomplished by touching the top-level deployment descriptor: application.xml for an EAR deployment, or web.xml for a WAR deployment.
Seam supports incremental redeployment of JavaBean components for a fast edit/compile/test cycle. To use this, deploy the JavaBean components in the WEB-INF/dev directory. Here, the JavaBean components are loaded by a special Seam classloader instead of the WAR or EAR classloader.
This function has the following limitations:
  • The components must be JavaBean components — they cannot be EJB3 beans. (Seam is working to remove this limitation.)
  • Entities can never be hot-deployed.
  • Components deployed with components.xml cannot be hot-deployed.
  • Hot-deployable components are not visible to classes deployed outside WEB-INF/dev.
  • Seam debug mode must be enabled and jboss-seam-debug.jar must be included in WEB-INF/lib.
  • The Seam filter must be installed in web.xml.
  • You may see errors if the system is placed under any load and debug is enabled.
For WAR projects created with seam-gen, incremental hot deployment is available out of the box for classes in the src/hot source directory. However, seam-gen does not support incremental hot deployment for EAR projects.

Chapter 6. The contextual component model

The two core concepts of Seam are the notions of a context, and a component. Components are stateful objects, usually Enterprise JavaBeans (EJBs). An instance of a component is associated with a context, and assigned a name within that context. Bijection provides a mechanism for aliasing internal component names (instance variables) to contextual names, that allows component trees to be dynamically assembled and reassembled by Seam.

6.1. Seam contexts

Seam has many built-in contexts that are created and destroyed by the framework. The application does not control context demarcation through explicit Java API calls. Contexts are usually implicit. In some cases, however, contexts are demarcated with annotations.
There are many basic contexts as follows:
  • Stateless context
  • Event (for instance, a request) context
  • Page context
  • Conversation context
  • Session context
  • Application context
Some basic contexts serve similar purposes in Servlet and related specifications. One you may not have encountered previously is the conversation context. One reason that the state management in web applications is fragile and error-prone, is that, the three built-in contexts (request, session, and application) are not meaningful for business logic. A user login session, for example, is an arbitrary construct in terms of the application workflow. Therefore, most Seam components are scoped to the conversation, as these are the most meaningful contexts in terms of the application.

6.1.1. Stateless context

Components that are truly stateless (primarily stateless session beans) always operate in the stateless context — the absence of a context, since the instance Seam resolves is not stored. Stateless components are arguably object-oriented, but they are developed regularly and thus form an important part of any Seam application.

6.1.2. Event context

The event context is the "narrowest" stateful context, and expands the notion of the web request to cover other event types. The event context associated with the life cycle of a JSF request is the most important example of an event context, and the one you will work with most often. Components associated with the event context are destroyed at the end of the request, but their state is available and well- defined for at least the life cycle of the request.
When you invoke a Seam component with RMI, or Seam Remoting, the event context is created and destroyed just for the invocation.

6.1.3. Page context

The page context allows you to associate state with a particular instance of a rendered page. You can initialize state in your event listener, or while rendering the page, and can then access it from any event that originates from that page. This is especially useful for functionality such as clickable lists, where the list is backed by changing data on the server side. The state is serialized to the client, so this construct is extremely robust with respect to multi-window operation and the back button.

6.1.4. Conversation context

The conversation context is a central concept to Seam. A conversation is a single unit of work from the user's perspective. In reality, it may span several interactions with a user — several requests, and data transactions. But to the user, a conversation solves a single problem. For example, the processes of booking a hotel, approving a contract, and creating an order are all conversations. It may help to think of a conversation as implementing a single "use case", although the relationship is not necessarily this exact.
A conversation holds state associated with the user's present task, in the current window. A single user may have multiple conversations in progress at any point in time, usually spanning multiple windows. The conversation context ensures that states from different conversations do not collide and cause bugs.
Some conversations last only for a single request. Conversations that span multiple requests must be demarcated with annotations provided by Seam.
Conversations can be nested, with one conversation taking place inside a broader conversation. This is an advanced feature.
Between requests, conversation state is usually held in the Servlet session. Seam implements configurable conversation timeout to automatically destroy inactive conversations, which ensures that the state held by a single user login session does not continue to grow if a user abandons a conversation. In the same process, Seam serializes the processing of concurrent requests in the same long-running conversation context.
Alternatively, Seam can also be configured to store conversational state in the client browser.

6.1.5. Session context

A session context holds state associated with the user login session. There are some cases where it is useful for state to be shared between several conversations. However, session context should not usually hold components other than global information about the logged in user.
In a JSR-168 portal environment, the session context represents the portlet session.

6.1.6. Application context

The application context is the Servlet context from the Servlet specification. Application context is used primarily to hold static information such as configuration data, reference data, or metamodels. For example, Seam stores its own configuration and metamodel in the application context.

6.1.7. Context variables

A context defines a namespace through a set of context variables. These work similar to the session or request attributes in the Servlet specification. Any value may be bound to a context variable, but they are usually bound to Seam component instances.
The context variable name identifies a component instance within a context. (The context variable name usually matches the component name.) You can programmatically access a named component instance in a particular scope with the Contexts class, which provides access to several thread-bound instances of the Context interface:
User user = (User) Contexts.getSessionContext().get("user");
You may also set or change the value associated with a name:
Contexts.getSessionContext().set("user", user);
However, components are usually obtained from a context through injection. Component instances are subsequently given to contexts through outjection.

6.1.8. Context search priority

Sometimes, component instances are obtained from a particular known scope. At other times, all stateful scopes are searched, in the following order of priority:
  • Event context
  • Page context
  • Conversation context
  • Session context
  • Application context
You can perform a priority search by calling Contexts.lookupInStatefulContexts(). Whenever you access a component by name from a JSF page, a priority search occurs.

6.1.9. Concurrency model

Neither the Servlet, nor EJB specifications, define facilities for managing concurrent requests from the same client. The Servlet container allows all threads to run concurrently, without ensuring thread-safeness. The EJB container allows concurrent access of stateless components, and generates an exception when multiple threads access a stateful session bean. This is sufficient for web applications based around fine-grained, synchronous requests. However, for modern applications, which frequently use asynchronous (AJAX) requests, concurrency support is vital. Therefore, Seam adds a concurrency management layer to its context model.
Session and application contexts are multi-threaded in Seam, allowing concurrent requests to be processed concurrently. Event and page contexts are single-threaded. Seam serializes concurrent requests within a long-running conversation context in order to enforce a single thread per conversation per process model for the conversation context.
As session context is multi-threaded and often contains volatile state, Seam always protects session-scoped components from concurrent access while Seam interceptors are enabled. If interceptors are disabled, any required thread safety must be implemented by the component itself. By default, Seam serializes requests to session-scoped session beans and JavaBeans, and detects and breaks any deadlocks that occur. However, this is not default behavior for application-scoped components, since they do not usually hold volatile state, and global synchronization is extremely expensive. Serialized threading models can be forced on any session bean or JavaBean component by adding the @Synchronized annotation.
This concurrency model means that AJAX clients can safely use volatile session and conversational state, without the need for any special work on the part of the developer.

Important

Be warned that requests to stateful session beans are not serialized by Seam anymore. Serialization of requests to Stateful session beans are controlled by EJB container, so there is no need for Seam to duplicate that. So @Synchronized annotation is ignored on stateful session beans.

6.2. Seam components

Seam components are Plain Old Java Objects (POJOs). Specifically, they are JavaBeans, or Enterprise JavaBean 3.0 (EJB3). While Seam does not require components to be EJBs, and can be used without an EJB3-compliant container, Seam was designed with EJB3 in mind, and includes deep integration with EJB3. Seam supports the following component types:
  • EJB3 stateless session beans
  • EJB3 stateful session beans
  • EJB3 entity beans (for instance, JPA entity classes)
  • JavaBeans
  • EJB3 message-driven beans

6.2.1. Stateless session beans

Stateless session bean components cannot hold state across multiple invocations, so they usually operate upon the state of other components in the various Seam contexts. They can be used as JSF action listeners, but cannot provide properties to JSF components for display.
Stateless session beans always exist in the stateless context. They can be accessed concurrently as a new instance is used for each request. The EJB3 container assigns instances to requests. (Normally, instances are allocated from a reuseable pool, so instance variables can retain data from previous uses of the bean.)
Seam stateless session bean components are instantiated with either Component.getInstance() or @In(create=true). They should not be directly instantiated via JNDI look up or the new operator.

6.2.2. Stateful session beans

Stateful session bean components can not only hold state across multiple invocations of the bean, but also across multiple requests. Any application state that does not belong to the database is held by stateful session beans. This is a major difference between Seam and many other web application frameworks. Current conversation data should be stored in the instance variables of a stateful session bean bound to the conversation context, rather than in the HttpSession. This feature allows Seam to manage state life cycle, and ensures that there are no collisions between states related to concurrent conversations.
Stateful session beans are often used as JSF action listeners, and as backing beans to provide properties to JSF components for display or form submission.
By default, stateful session beans are bound to the conversation context. They may never be bound to the page, or to stateless contexts.
Concurrent requests to session-scoped stateful session beans are not serialized by Seam as long as EJB 3.1 has changed that. This is a difference in comparison to previous Seam 2.2.x.
Seam stateful session bean components are instantiated with either Component.getInstance() or @In(create=true). They should not be directly instantiated via JNDI look up or the new operator.

6.2.3. Entity beans

Entity beans can function as a Seam component when bound to a context variable. As entities have a persistent identity in addition to their contextual identity, entity instances are bound explicitly in Java code, rather than being instantiated implicitly by Seam.
Entity bean components do not support bijection, context demarcation, and invocation of an entity bean trigger validation.
Entity beans are not usually used as JSF action listeners, but often function as backing beans to provide properties to JSF components for display or form submission. They are commonly used as a backing bean coupled with a stateless session bean action listener to implement create/update/delete-type functionality.
By default, entity beans are bound to the conversation context, and can never be bound to the stateless context.

Note

In a clustered environment, it is less efficient to bind an entity bean directly to a conversation (or session-scoped Seam context variable) than it is to refer to the entity bean with a stateful session bean. Not all Seam applications define entity beans as Seam components for this reason.
Seam entity bean components are instantiated with Component.getInstance() or @In(create=true), or directly instantiated with the new operator.

6.2.4. JavaBeans

JavaBeans are used similarly to stateless or stateful session beans. However, they do not provide functions such as declarative transaction demarcation, declarative security, efficient clustered state replication, EJB3 persistence, timeout methods, and so on.
A later chapter discusses the use of Seam and Hibernate without an EJB container. In this case, components are JavaBeans rather than session beans.

Note

In a clustered environment, it is less efficient to cluster conversation-scoped or session-scoped Seam JavaBean components than it is to cluster stateful session bean components.
By default, JavaBeans are bound to the event context. Seam always serializes concurrent requests to session-scoped JavaBeans.
Seam JavaBean components are instantiated with Component.getInstance() or @In(create=true). They should not be directly instantiated using the new operator.

6.2.5. Message-driven beans

Message-driven beans can function as Seam components. However, their call method differs from that of other Seam components — rather than being invoked with the context variable, they listen for messages sent to JMS queues or topics.
Message-driven beans cannot be bound to Seam contexts, nor can they access the session or conversation state of their caller. However, they do support bijection and some other Seam functionality.
Message-driven beans are never instantiated by the application; they are instantiated by the EJB container when a message is received.

6.2.6. Interception

To perform actions such as bijection, context demarcation, and validation, Seam must intercept component invocations. For JavaBeans, Seam controls component instantiation completely, and no special configuration is required. For entity beans, interception is not required, as bijection and context demarcation are not defined. For session beans, an EJB interceptor must be registered for the session bean component. This can be done with an annotation, as follows:
@Stateless @Interceptors(SeamInterceptor.class) public class LoginAction implements Login { ... }
However, it is better to define the interceptor in ejb-jar.xml:
<interceptors> 
  <interceptor> 
    <interceptor-class>
      org.jboss.seam.ejb.SeamInterceptor
    </interceptor-class> 
  </interceptor> 
</interceptors> 
<assembly-descriptor> 
  <interceptor-binding> 
    <ejb-name>*</ejb-name> 
    <interceptor-class>
      org.jboss.seam.ejb.SeamInterceptor
    </interceptor-class> 
  </interceptor-binding> 
</assembly-descriptor>

6.2.7. Component names

All Seam components require names. Assign a name with the @Name annotation:
@Name("loginAction") 
@Stateless 
public class LoginAction implements Login { ... }
This is the Seam component name, and does not relate to any other name defined by the EJB specification. However, Seam component names work like JSF managed bean names, and can be thought of in identical terms.
@Name is not the only way to define a component name, but the name must always be specified. No other Seam annotation will function if a name is not defined.
When Seam instantiates a component, it binds the new instance to a variable matching the component name in the component's configured scope. This is identical to JSF managed bean behavior, except that Seam allows you to configure this mapping with annotations rather than XML. You can also programmatically bind a component to a context variable. This is useful if a particular component serves multiple roles within the system. For example, the current User might be bound to the currentUser session context variable, while a User that is the subject of some administration functionality might be bound to the user conversation context variable. Take care when binding programmatically, because it is possible to overwrite context variables that reference Seam components.
For very large applications, and for built-in Seam components, qualified component names are often used to avoid naming conflicts.
@Name("com.jboss.myapp.loginAction") 
@Stateless 
public class LoginAction implements Login { ... }
The qualified component name can be used both in Java code and in JSF's expression language:
<h:commandButton type="submit" value="Login" 
action="#{com.jboss.myapp.loginAction.login}"/>
As this is noisy, Seam also provides a means of aliasing a qualified name to a simple name. Add a line like this to the components.xml file:
<factory name="loginAction" scope="STATELESS" value="#{com.jboss.myapp.loginAction}"/>
All built-in Seam components have qualified names, but can be accessed through their unqualified names with Seam's namespace-import feature. The components.xml file included in the Seam JAR defines the following namespaces:
<components xmlns="http://jboss.org/schema/seam/components"> 
  <import>org.jboss.seam.core</import> 
  <import>org.jboss.seam.cache</import> 
  <import>org.jboss.seam.transaction</import> 
  <import>org.jboss.seam.framework</import> 
  <import>org.jboss.seam.web</import> 
  <import>org.jboss.seam.faces</import> 
  <import>org.jboss.seam.international</import> 
  <import>org.jboss.seam.theme</import> 
  <import>org.jboss.seam.jms</import> 
  <import>org.jboss.seam.mail</import> 
  <import>org.jboss.seam.security</import> 
  <import>org.jboss.seam.security.management</import>  
  <import>org.jboss.seam.security.permission</import> 
  <import>org.jboss.seam.captcha</import> 
  <import>org.jboss.seam.excel.exporter</import> 
  <!-- ... ---> 
</components>
When attempting to resolve an unqualified name, Seam checks each of these namespaces, in order. Additional application-specific namespaces can be included in your application's components.xml file.

6.2.8. Defining the component scope

The @Scope annotation allows you to override the scope (context) of a component to define the context a component instance is bound to when instantiated by Seam.
@Name("user") 
@Entity 
@Scope(SESSION) 
public class User { ... }
org.jboss.seam.ScopeType defines an enumeration of possible scopes.

6.2.9. Components with multiple roles

Some Seam component classes can fulfill multiple roles in the system. For example, the User class is usually a session-scoped component representing the current user, but in user administration screens it becomes a conversation-scoped component. The @Role annotation allows you to define an additional named role for a component, with a different scope — it allows you to bind the same component class to different context variables. (Any Seam component instance can be bound to multiple context variables, but this allows you to do it at the class level to take advantage of automatic instantiation.)
@Name("user") 
@Entity 
@Scope(CONVERSATION) 
@Role(name="currentUser", scope=SESSION)
public class User { ... }
The @Roles annotation allows you to specify additional roles as required.
@Name("user") 
@Entity 
@Scope(CONVERSATION) 
@Roles({ @Role(name="currentUser", scope=SESSION), 
         @Role(name="tempUser", scope=EVENT)}) 
public class User { ... }

6.2.10. Built-in components

Seam is implemented as a set of built-in interceptors and components. This makes it easy for applications to interact with built-in components at runtime, or to customize basic Seam functionality by replacing the built-in components with custom implementations. The built-in components are defined in the Seam namespace org.jboss.seam.core, and in the Java package of the same name.
The built-in components may be injected like any other Seam component, but they also provide convenient static instance() methods:
FacesMessages.instance().add("Welcome back, #{user.name}!");

6.3. Bijection

Dependency injection or inversion of control (IoC) allows one component to reference another by having the container "inject" the component into a setter method or instance variable. In previous dependency injection implementations, injection occurred at component construction, and the reference did not change for the lifetime of the component instance. This is reasonable for stateless components — from the client's perspective, all instances of a particular stateless component are interchangeable. However, Seam emphasizes the use of stateful components, so traditional dependency injection as a construct is less useful. Seam introduces the notion of bijection as a generalization of injection. In contrast to injection, bijection is:
contextual
Bijection is used to assemble stateful components from different contexts. A component from a wider context can even refer to a component from a narrower context.
bidirectional
Values are injected from context variables into attributes of the invoked component, and returned (via outjection) to the context, allowing the invoked component to manipulate contextual variable values simply by setting its own instance variables.
dynamic
As the value of contextual variables change over time, and as Seam components are stateful, bijection takes place every time a component is invoked.
In essence, bijection allows you to alias a context variable to a component instance variable, by specifying that the value of the instance variable is injected, outjected, or both. Annotations are used to enable bijection.
The @In annotation specifies that a value should be injected, either into an instance variable:
@Name("loginAction") 
@Stateless 
public class LoginAction implements Login 
{ 
  @In User user; 
  ... 
}
or into a setter method:
@Name("loginAction") 
@Stateless 
public class LoginAction implements Login 
{ 
  User user; 
  @In 
  public void setUser(User user) 
  { 
    this.user=user; 
  } 
  ... 
}
By default, Seam performs a priority search of all contexts, using the name of the property or instance variable being injected. You can specify the context variable name explicitly, for example, @In("currentUser").
If you want Seam to create an instance of the component, where there is no existing component instance bound to the named context variable, you should specify @In(create=true). If the value is optional (it can be null), specify @In(required=false).
For some components, specifying @In(create=true) each time it is used can be repetitive. In such cases, annotate the component @AutoCreate. This causes @In(create=true)to be created whenever required, even without the explicit use of create=true.
You can even inject the value of an expression:
@Name("loginAction") 
@Stateless 
public class LoginAction implements Login 
{ 
  @In("#{user.username}") String username; 
  ... 
}
Injected values are disinjected (that is, set to null) immediately after method completion and outjection.
(More information about component life cycle and injection can be found in the next chapter.)
The @Out annotation specifies that an attribute should be outjected, either from an instance variable:
@Name("loginAction") 
@Stateless 
public class LoginAction implements Login 
{ 
  @Out User user; 
  ... 
}
or from a getter method:
@Name("loginAction") 
@Stateless 
public class LoginAction implements Login 
{ 
  User user; 
  @Out 
  public User getUser() 
  { 
    return user; 
  } 
  ... 
}
An attribute may be both injected and outjected:
@Name("loginAction") 
@Stateless 
public class LoginAction implements Login 
{ 
  @In 
  @Out User user; 
  ... 
}
or:
@Name("loginAction") 
@Stateless 
public class LoginAction implements Login 
{ 
  User user;
  @In 
  public void setUser(User user) 
  { 
    this.user=user; 
  }
  @Out 
  public User getUser() 
  { 
    return user; 
  } 
  ... 
}

6.4. Life cycle methods

Session bean and entity bean Seam components support all common EJB3 life cycle callbacks (@PostConstruct, @PreDestroy, etc.), but Seam also supports the use of any of these callbacks with JavaBean components. However, as these annotations are not available in a J2EE environment, Seam defines two additional component life cycle callbacks, equivalent to @PostConstruct and @PreDestroy.
The @Create method is called after Seam instantiates a component. Components may define only one @Create method.
The @Destroy method is called when the context that the Seam component is bound to ends. Components may define only one @Destroy method.
In addition, stateful session bean components must define a method with no parameters, annotated @Remove. This method is called by Seam when the context ends.
Finally, the @Startup annotation can be applied to any application-scoped or session-scoped component. The @Startup annotation tells Seam to instantiate the component immediately, when the context begins, instead of waiting until it is first referenced by a client. It is possible to control the order of instantiation of start up components by specifying @Startup(depends={....}).

6.5. Conditional installation

The @Install annotation controls conditional installation of components that are required in some deployment scenarios and not required in others. This is useful when you want to:
  • mock out an infrastructural component in a test
  • change a component's implementation in certain deployment scenarios
  • install some components only if their dependencies are available. (This is useful for framework authors.)
@Install allows you to specify precedence and dependencies.
The precedence of a component is a number that Seam uses to decide which component to install when there are multiple classes with the same component name in the classpath. Seam chooses the component with the higher precedence. Some predefined precedence values are (in ascending order):
  1. BUILT_IN — the lowest precedence components are the components built in to Seam.
  2. FRAMEWORK — components defined by third-party frameworks may override built-in components, but are overridden by application components.
  3. APPLICATION — the default precedence. This is appropriate for most application components.
  4. DEPLOYMENT — for application components which are deployment-specific.
  5. MOCK — for mock objects used in testing.
Suppose we have a component named messageSender that talks to a JMS queue.
@Name("messageSender") 
public class MessageSender 
{ 
  public void sendMessage() 
  { 
    //do something with JMS 
  } 
}
In your unit tests, there is no available JMS queue, so you would like to stub out this method. Create a mock component that exists in the classpath when unit tests are running, but is never deployed with the application:
@Name("messageSender") 
@Install(precedence=MOCK) 
public class MockMessageSender extends MessageSender 
{ 
  public void sendMessage() 
  { 
    //do nothing! 
  } 
}
The precedence helps Seam decide which version to use when it finds both components in the classpath.
If you are able to control precisely which classes are in the classpath, this works well. But if you are writing a reuseable framework with many dependencies, you do not want to break that framework across multiple jars. You want to be able to decide which components to install based on other installed components, and classes available in the classpath. The @Install annotation also controls this functionality. Seam uses this mechanism internally to enable the conditional installation of many built-in components.

6.6. Logging

Before Seam, even the simplest log message required verbose code:
private static final Log log = LogFactory.getLog(CreateOrderAction.class);
public Order createOrder(User user, Product product, int quantity) 
{ 
  if ( log.isDebugEnabled() ) 
  { 
    log.debug("Creating new order for user: " + user.username() + " product: " + product.name() + " quantity: " + quantity);
  } 
  return new Order(user, product, quantity); 
}
Seam provides a logging API that simplifies this code significantly:
@Logger private Log log; 
public Order createOrder(User user, Product product, int quantity) 
{ 
  log.debug("Creating new order for user: #0 product: #1 quantity: #2", user.username(), product.name(), quantity); 
  return new Order(user, product, quantity); 
}
Except for entity bean components (which require the log variable to be static), this works regardless of whether the log variable is declared static.
String concatenation occurs inside the debug() method, so the verbose if ( log.isDebugEnabled() ) guard is unnecessary. Usually, you would not need to explicitly specify the log category, as Seam knows where it is injecting the log.
If User and Product are Seam components available in the current contexts, the code is even more concise:
@Logger private Log log; 
public Order createOrder(User user, Product product, int quantity) 
{ 
  log.debug("Creating new order for user: #{user.username} product: #{product.name} quantity: #0", quantity); 
  return new Order(user, product, quantity); 
}
Seam logging automatically chooses whether to send output to log4j or JDK logging — if log4j is in the classpath, Seam uses log4j; if not, Seam uses JDK logging.

SECURITY WARNING: Do not use string concatenation to construct log messages

Seam logging evaluates expression language (EL) statements in log messages. This is safe if used as intended, because all user-provided input is bound to a parameter in the EL statement. If an application does not use the Seam logging facility as intended, and includes user-provided strings in log messages directly via string concatenation, then a remote attacker could inject EL statements directly into the log messages, which would be evaluated on the server. This could lead to a variety of security impacts. To protect against this issue, ensure that all user-provided input in log messages is bound to a parameter, and not included directly in log messages using string concatenation.

6.7. The Mutable interface and @ReadOnly

Many application servers feature HttpSession clustering where changes to the state of mutable objects bound to the session are replicated only when setAttribute is called explicitly. This can lead to bugs that manifest only upon failover, which cannot be effectively tested during development. Further, the replication messages themselves are inefficient, as they contain the entire serialized object graph, bound to the session attribute.
EJB stateful session beans must perform automatic dirty checking (that is, they must automatically detect object state changes to synchronize updated states with the database) and replicate mutable state. A sophisticated EJB container can introduce optimizations such as attribute-level replication. Unfortunately, not all Seam users will be working in an environment that supports EJB3. Therefore, Seam provides an extra layer of cluster-safe state management for session-scoped and conversation-scoped JavaBean and entity bean components.
For session-scoped or conversation-scoped JavaBean components, Seam automatically forces replication by calling setAttribute() once in every request where the component was invoked by the application. However, this strategy is inefficient for read-mostly components. Control this behavior by implementing the org.jboss.seam.core.Mutable interface, or by extending org.jboss.seam.core.AbstractMutable and writing your own dirty-checking logic inside the component. For example,
@Name("account") 
public class Account extends AbstractMutable 
{ 
  private BigDecimal balance; 
  public void setBalance(BigDecimal balance) 
  { 
    setDirty(this.balance, balance); 
    this.balance = balance; 
  } 
  public BigDecimal getBalance() 
  { 
    return balance; 
  } 
  ... 
}
Or, you can use the @ReadOnly annotation to achieve a similar effect:
@Name("account") 
public class Account 
{ 
  private BigDecimal balance; 
  public void setBalance(BigDecimal balance) 
  { 
    this.balance = balance; 
  } 
  @ReadOnly 
  public BigDecimal getBalance() 
  { 
    return balance; 
  } 
  ... 
}
For session-scoped or conversation-scoped entity bean components, Seam automatically forces replication by calling setAttribute() once in every request, unless the (conversation-scoped) entity is currently associated with a Seam-managed persistence context, in which case replication is unnecessary. This strategy is not necessarily efficient, so session or conversation scoped entity beans should be used with care. You can always write a stateful session bean or JavaBean component to "manage" the entity bean instance. For example:
@Stateful @Name("account") 
public class AccountManager extends AbstractMutable 
{ 
  private Account account; // an entity bean 
  @Unwrap 
  public Account getAccount() 
  { 
    return account; 
  } 
  ... 
}

Note

The EntityHome class in the Seam Application Framework is an excellent example of managing an entity bean instance using a Seam component.

6.8. Factory and manager components

It is often necessary to work with objects that are not Seam components, but we still prefer to be able to inject them into our components with @In annotation, use them in value-binding and method-binding expressions, and tie them into the Seam context life cycle (@Destroy, for example). Therefore, Seam contexts can hold objects that are not Seam components, and Seam provides several features that simplify working with non-component objects bound to contexts.
The factory component pattern allows a Seam component to act as the instantiator for a non-component object. A factory method is called when a context variable is referenced but has no value bound to it. Factory methods are defined with the @Factory annotation. The factory method binds a value to the context variable, and determines the scope of the bound value. There are two styles of factory method. The first style returns a value, which is bound to the context by Seam:
@Factory(scope=CONVERSATION) 
public List<Customer> getCustomerList() 
{ 
  return ... ; 
}
The second style is a method of type void, which binds the value to the context variable itself:
@DataModel List<Customer> customerList; 
@Factory("customerList") 
public void initCustomerList() 
{ 
  customerList = ...  ; 
}
In either case, the factory method is called when the customerList context variable is referenced, and its value is null. The factory method then has no further part in the life cycle of the value. The manager component pattern is an even more powerful pattern. In this case, a Seam component bound to a context variable manages the value of the context variable while remaining invisible to clients.
A manager component is any component with an @Unwrap method. This method returns the value that will be visible to clients, and is called every time a context variable is referenced.
@Name("customerList") 
@Scope(CONVERSATION) 
public class CustomerListManager 
{ 
  ... 
  @Unwrap 
  public List<Customer> getCustomerList() 
  { 
    return ... ; 
  } 
}
The manager component pattern is especially useful where more control is required over component life cycle. For example, if you have a heavyweight object that needs a cleanup operation when the context ends, you could @Unwrap the object, and perform cleanup in the @Destroy method of the manager component.
@Name("hens") 
@Scope(APPLICATION)
public class HenHouse 
{ 
  Set<Hen> hens;
  @In(required=false) Hen hen; 
  @Unwrap 
  public List<Hen> getHens() 
  { 
    if (hens == null) 
    { 
      // Setup our hens 
    } 
    return hens; 
  } 
  @Observer({"chickBorn", "chickenBoughtAtMarket"}) 
  public addHen() 
  { 
    hens.add(hen); 
  } 
  @Observer("chickenSoldAtMarket") 
  public removeHen()  
  { 
    hens.remove(hen); 
  } 
  @Observer("foxGetsIn") 
  public removeAllHens() 
  { 
    hens.clear(); 
  } 
  ... 
}
Here, the managed component observes many events that change the underlying object. The component manages these actions itself, and because the object is unwrapped each time it is accessed, a consistent view is provided.

Chapter 7. Configuring Seam components

Seam aims to minimize the need for XML-based configuration. However, there are various reasons you might want to configure Seam components with XML. To isolate deployment-specific information from the Java code, to enable the creation of reusable frameworks, to configure Seam's built-in functionality, and so on. Seam provides two basic approaches to component configuration; property settings in a properties file or web.xml file, and property settings components.xml file.

7.1. Configuring components through property settings

You can provide Seam with configuration properties with either servlet context parameters (in system properties), or with a properties file named seam.properties in the root of the classpath.
The configurable Seam component must expose JavaBean-style property setter methods for the configurable attributes. That is, if a Seam component named com.jboss.myapp.settings has a setter method named setLocale(), you can provide either:
  • a property named com.jboss.myapp.settings.locale in the seam.properties file
  • a system property named org.jboss.seam.properties.com.jboss.myapp.settings.locale through -D at startup
  • the same system property as a Servlet context parameter
Any of these can set the value of the locale attribute in the root of the class path.
The same mechanism is used to configure Seam itself. For example, to set conversation timeout, provide a value for org.jboss.seam.core.manager.conversationTimeout in web.xml, seam.properties, or through a system property prefixed with org.jboss.seam.properties. (There is a built-in Seam component named org.jboss.seam.core.manager with a setter method named setConversationTimeout().)

7.2. Configuring components via components.xml

The components.xml file is more powerful than property settings. It allows you to:
  • configure components that have been installed automatically, including built-in components, and application components that have been annotated with @Name and picked up by Seam's deployment scanner.
  • install classes with no @Name annotation as Seam components. This is most useful for infrastructural components which can be installed multiple times with different names (for example, Seam-managed persistence contexts).
  • install components that have a @Name annotation but are not installed by default because of an @Install annotation that indicates that the component should not be installed.
  • override the scope of a component.
The components.xml file appears in one of the following three locations:
  • The WEB-INF directory of a WAR.
  • The META-INF directory of a JAR.
  • Any JAR directory containing classes with a @Name annotation.
Seam components are installed when the deployment scanner discovers a class with a @Name annotation in an archive with a seam.properties file, or a META-INF/components.xml file, unless the component also has an @Install annotation indicating that it should not be installed by default. The components.xml file handles special cases where the annotations must be overridden.
The following example installs and configures two different Seam-managed persistence contexts:
<components xmlns="http://jboss.org/schema/seam/components" xmlns:persistence="http://jboss.org/schema/seam/persistence">
    <persistence:managed-persistence-context name="customerDatabase" persistence-unit-jndi-name="java:/customerEntityManagerFactory"/>      
    <persistence:managed-persistence-context name="accountingDatabase" persistence-unit-jndi-name="java:/accountingEntityManagerFactory"/>
</components>
The following example also installs and configures two different Seam-managed persistence contexts:
<components>
  <component name="customerDatabase" class="org.jboss.seam.persistence.ManagedPersistenceContext">
    <property name="persistenceUnitJndiName">
      java:/customerEntityManagerFactory
    </property>
  </component>    
  <component name="accountingDatabase" class="org.jboss.seam.persistence.ManagedPersistenceContext">
    <property name="persistenceUnitJndiName">
      java:/accountingEntityManagerFactory
    </property>
  </component>
</components>
The following examples creates a session-scoped Seam-managed persistence context. (This is not recommended in practice.)
<components xmlns="http://jboss.org/schema/seam/components" xmlns:persistence="http://jboss.org/schema/seam/persistence">
  <persistence:managed-persistence-context name="productDatabase" scope="session" persistence-unit-jndi-name="java:/productEntityManagerFactory"/>        
</components>
<components>       
  <component name="productDatabase" scope="session" class="org.jboss.seam.persistence.ManagedPersistenceContext">
    <property name="persistenceUnitJndiName">
      java:/productEntityManagerFactory
    </property>
  </component>
</components>
The auto-create option is commonly used for infrastructural objects such as persistence contexts, removing the need to specify create=true explicitly when using the @In annotation.
<components xmlns="http://jboss.org/schema/seam/components" xmlns:persistence="http://jboss.org/schema/seam/persistence">
  <persistence:managed-persistence-context name="productDatabase" auto-create="true" persistence-unit-jndi-name="java:/productEntityManagerFactory"/>
</components>
<components>            
  <component name="productDatabase" auto-create="true" class="org.jboss.seam.persistence.ManagedPersistenceContext">
    <property name="persistenceUnitJndiName">
      java:/productEntityManagerFactory
    </property>
  </component>
</components>
The <factory> declaration specifies a value-binding or method-binding expression that initializes the value of a context variable when the variable is first referenced.
<components> 
  <factory name="contact" method="#{contactManager.loadContact}" 
           scope="CONVERSATION"/>
</components>
You can create an alias (a second name) for a Seam component as follows:
<components> 
  <factory name="user" value="#{actor}" scope="STATELESS"/>
</components>
You can create an alias for a commonly used expression as follows:
<components> 
  <factory name="contact" value="#{contactManager.contact}" scope="STATELESS"/>
</components>
auto-create="true" is often used with the <factory> declaration as follows:
<components> 
  <factory name="session" value="#{entityManager.delegate}" scope="STATELESS" auto-create="true"/> 
</components>
The components.xml file is sometimes used (with minor changes) during both deployment and testing. Seam allows wildcards of the form @wildcard@ to be placed in the components.xml file. This can be replaced at deployment time by your Ant build script, or by providing a file named components.properties in the classpath. (The latter approach appears in the Seam examples.)

7.3. Fine-grained configuration files

If a large number of components require XML configuration, it is better to split the components.xml file into several smaller files. With Seam, configuration for a class named com.helloworld.Hello can be placed in a resource named com/helloworld/Hello.component.xml. (This pattern is also used in Hibernate.) The root element of the file may either be a <components> or <component> element.
The <components> element allows you to define multiple components in the file:
<components>
  <component class="com.helloworld.Hello" name="hello">
    <property name="name">#{user.name}</property>
  </component>
  <factory name="message" value="#{hello.message}"/> 
</components>
The <component> element allows you to configure only one component, but is less verbose:
<component name="hello"> 
  <property name="name">#{user.name}</property> 
</component>
The class name in the latter is implied by the file in which the component definition appears.
Alternatively, you can put configuration for all classes in the com.helloworld package in com/helloworld/components.xml file.

7.4. Configurable property types

Properties of string, primitive, or primitive wrapper type are configured as follows:
  • org.jboss.seam.core.manager.conversationTimeout 60000
  • <core:manager conversation-timeout="60000"/>
<component name="org.jboss.seam.core.manager"> 
  <property name="conversationTimeout">60000</property> 
</component>
Even maps with String-valued keys and string, or primitive values are supported:
<component name="issueEditor">
  <property name="issueStatuses">
    <key>open</key> <value>open issue</value>
    <key>resolved</key> <value>issue resolved by developer</value>
    <key>closed</key> <value>resolution accepted by user</value>
  </property>  
</component>
When configuring multi-valued properties, Seam preserves the order of attributes set out in the components.xml file by default. If SortedSet/SortedMap are used, Seam refers to TreeMap/TreeSet. If the property has a concrete type (LinkedList, for example) Seam uses that type.
You can also override the type by specifying a fully qualified class name:
<component name="issueEditor">
  <property name="issueStatusOptions" type="java.util.LinkedHashMap">
    <key>open</key> <value>open issue</value>
    <key>resolved</key> <value>issue resolved by developer</value>
    <key>closed</key> <value>resolution accepted by user</value>
  </property> 
</component>
You can link components with a value-binding expression. Note that as this occurs upon component instantiation, not invocation, this is quite different to injection with @In. It is more similar to the dependency injection facilities offered by traditional Inversion of Control containers such as JavaServer Faces (JSF) or Spring.
<drools:managed-working-memory name="policyPricingWorkingMemory" rule-base="#{policyPricingRules}"/>
<component name="policyPricingWorkingMemory" class="org.jboss.seam.drools.ManagedWorkingMemory"> 
  <property name="ruleBase">
   #{policyPricingRules}
  </property>
</component>
Seam also resolves EL expression strings prior to assigning the initial value to the bean property of the component, so some contextual data can be injected into components:
<component name="greeter" class="com.example.action.Greeter">
  <property name="message">
    Nice to see you, #{identity.username}!
  </property>
</component>
However, there is one important exception: if the initial value is assigned to either a Seam ValueExpression or MethodExpression, then the evaluation of the EL is deferred, and the appropriate expression wrapper is created and assigned to the property. The message templates on the Home component of the Seam Application Framework are a good example of this:
<framework:entity-home name="myEntityHome" class="com.example.action.MyEntityHome" entity-class="com.example.model.MyEntity" created-message="'#{myEntityHome.instance.name}'has been successfully added."/>
Within the component, you can access the expression string by calling getExpressionString() on either ValueExpression or MethodExpression. If the property is a ValueExpression, resolve the value with getValue(). If the property is a MethodExpression, invoke the method with invoke({Object arguments}). To assign a value to a MethodExpression property, the entire initial value must be a single EL expression.

7.5. Using XML Namespaces

Previous examples have alternated between two component declaration methods: with and without using XML namespaces. The following example shows a typical components.xml file that does not use namespaces:
<?xml version="1.0" encoding="UTF-8"?>
 <components xmlns="http://jboss.org/schema/seam/components"
            xsi:schemaLocation=
   "http://jboss.org/schema/seam/components 
    http://jboss.org/schema/seam/components-2.3.xsd">

  <component class="org.jboss.seam.core.init">
    <property name="debug">true</property>
    <property name="jndiPattern">@jndiPattern@</property>
  </component>   
 </components>
As you can see, this code is verbose. More importantly, the component and attribute names cannot be validated at development time.
Using namespaces gives you:
<?xml version="1.0" encoding="UTF-8"?>
 <components xmlns="http://jboss.org/schema/seam/components"
 xmlns:core="http://jboss.org/schema/seam/core"            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation=
   "http://jboss.org/schema/seam/core 
   http://jboss.org/schema/seam/core-2.3.xsd 
   http://jboss.org/schema/seam/components 
    http://jboss.org/schema/seam/components-2.3.xsd">
  <core:init debug="true" jndi-pattern="@jndiPattern@"/>
 </components>
Although the schema declarations are verbose, the XML content itself is lean and easy to understand. The schemas provide detailed information about each component and the available attributes, allowing XML editors to offer intelligent auto-completion. Using namespaced elements makes it easier to generate and maintain correct components.xml files.
This works well for built-in Seam components, but for user components there are two available options. First, Seam supports mixing both models, allowing the use of generic <component> declarations for user components, and namespaced declarations for built-in components. More importantly, Seam allows you to quickly declare namespaces for your own components.
Any Java package can be associated with an XML namespace by annotating the package with @Namespace. (Package-level annotations are declared in a file named package-info.java in the package directory.) An example of this from the seampay demo is:
@Namespace(value="http://jboss.org/schema/seam/examples/ seampay") package org.jboss.seam.example.seampay; import org.jboss.seam.annotations.Namespace;
You can write:
<components xmlns="http://jboss.org/schema/seam/components" xmlns:pay="http://jboss.org/schema/seam/examples/seampay" ... >
  <pay:payment-home new-instance="#{newPayment}" created-message="Created a new payment to #{newPayment.payee}" />
  <pay:payment name="newPayment"
               payee="Somebody"
               account="#{selectedAccount}"
               payment-date="#{currentDatetime}"
               created-date="#{currentDatetime}" />
     ...
</components>
Or:
<components xmlns="http://jboss.org/schema/seam/components"   xmlns:pay="http://jboss.org/schema/seam/examples/seampay"
            ... >
  <pay:payment-home>
    <pay:new-instance>"#{newPayment}"</pay:new-instance>
    <pay:created-message>
      Created a new payment to #{newPayment.payee}
    </pay:created-message>
  </pay:payment-home>    
  <pay:payment name="newPayment">
    <pay:payee>Somebody"</pay:payee>
    <pay:account>#{selectedAccount}</pay:account>
    <pay:payment-date>#{currentDatetime}</pay:payment-date>
    <pay:created-date>#{currentDatetime}</pay:created-date>
   </pay:payment>
   ...
</components>
The previous examples illustrate the two usage models of a namespaced element. In the first declaration, <pay:payment-home> references the paymentHome component:
package org.jboss.seam.example.seampay;
...
@Name("paymentHome")
public class PaymentController extends EntityHome<Payment> {
  ... 
}
The element name is the hyphenated form of the component name. The attributes of the element are the hyphenated forms of the property names.
In the second declaration, the <pay:payment> element refers to the Payment class in the org.jboss.seam.example.seampay package. In this case, Payment is an entity that is being declared as a Seam component:
package org.jboss.seam.example.seampay;
...
@Entity
public class Payment implements Serializable 
{
  ...
}
A schema is required for validation and auto-completion to work for user-defined components. Seam cannot yet generate a schema for a set of components automatically, so schema must be manually created. You can use schema definitions for standard Seam packages for guidance.
The following are the namespaces used by Seam:
  • components — http://jboss.org/schema/seam/components
  • core — http://jboss.org/schema/seam/core
  • drools — http://jboss.org/schema/seam/drools
  • framework — http://jboss.org/schema/seam/framework
  • jms — http://jboss.org/schema/seam/jms
  • remoting — http://jboss.org/schema/seam/remoting
  • theme — http://jboss.org/schema/seam/theme
  • security — http://jboss.org/schema/seam/security
  • mail — http://jboss.org/schema/seam/mail
  • web — http://jboss.org/schema/seam/web
  • pdf — http://jboss.org/schema/seam/pdf
  • spring — http://jboss.org/schema/seam/spring

Chapter 8. Events, interceptors and exception handling

To complement the contextual component model, there are two further basic concepts that facilitate the extremely loose coupling distinctive of Seam applications. The first is a strong event model, where events are mapped to event listeners with method-binding expressions like those in JavaServer Faces (JSF). The second is the pervasive use of annotations and interceptors to apply cross-cutting concerns to components that implement business logic.

8.1. Seam events

The Seam component model was developed for use with event-driven applications, specifically to enable the development of fine-grained, loosely-coupled components in a fine-grained eventing model. There are several event types in Seam:
  • JSF events
  • Seam page actions
  • Seam component-driven events
  • Seam contextual events
Each of these events is mapped to Seam components with JSF EL method-binding expressions. For a JSF event, this is defined in the JSF template:
<h:commandButton value="Click me!" action="#{helloWorld.sayHello}"/>

8.2. Page actions

A Seam page action is an event occurring immediately before a page is rendered. Declare page actions in WEB-INF/pages.xml. You can define a page action for a particular JSF view ID:
<pages> 
 <page view-id="/hello.xhtml" action="#{helloWorld.sayHello}"/>
</pages>
Or you can use a * wildcard as a suffix to the view-id to specify an action that applies to all view IDs that match that pattern:
<pages> 
    <page view-id="/hello/*" action="#{helloWorld.sayHello}"/> 
</pages>

Note

If the <page> element is defined in a fine-grained page descriptor, the view-id attribute can be omitted, as it is already implied.
If multiple page actions, with wildcards, match the current view-id; Seam calls all the actions, in the order of least-specific to most-specific.
The page action method can return a JSF outcome. If the outcome is not null, Seam uses the defined navigation rules to navigate to a view.
The view ID mentioned in the <page> element need not correspond to a real JSP or Facelets page. This way, you can reproduce the functionality of a traditional action-oriented framework like Struts or WebWork using page actions. This is useful for performing complex actions in response to non-Faces requests like HTTP GET.
Multiple or conditional page actions can be specified with the <action> tag:
<pages> 
  <page view-id="/hello.xhtml">
    <action execute="#{helloWorld.sayHello}" 
            if="#{not validation.failed}"/>
    <action execute="#{hitCount.increment}"/> 
  </page> 
</pages>
Page actions are executed on both an initial (non-Faces) request and a postback (Faces) request. If you use the page action to load data, it may conflict with the standard JSF actions being executed on a postback. One way to disable the page action is to set up a condition that resolves to true only upon an initial request.
<pages> 
  <page view-id="/dashboard.xhtml"> 
    <action execute="#{dashboard.loadData}" 
      if="#{not FacesContext.renderKit.responseStateManager.isPostback(FacesContext)}"/> 
  </page> 
</pages>
This condition consults the ResponseStateManager#isPostback(FacesContext) to determine if the request is a postback. The ResponseStateManager is accessed using FacesContext.getCurrentInstance().getRenderKit().getResponseStateManager().
Seam offers a built-in condition that accomplishes this result less verbosely. You can disable a page action on a postback by setting the on-postback attribute to false:
<pages> 
    <page view-id="/dashboard.xhtml"> 
    <action execute="#{dashboard.loadData}" on-postback="false"/> 
    </page> 
</pages>
The on-postback attribute defaults to true to maintain backward compatibility.

8.3. Page parameters

A Faces request (a JSF form submission) encapsulates both, action (a method binding) and parameters (input value bindings). A page action can also require parameters. As non-Faces (GET) requests can be bookmarked, page parameters are passed as human-readable request parameters. You can use page parameters with or without an action method.

8.3.1. Mapping request parameters to the model

Seam allows you to provide a value binding that maps a named request parameter to an attribute of a model object.
<pages> 
  <page view-id="/hello.xhtml" action="#{helloWorld.sayHello}"> 
    <param name="firstName" value="#{person.firstName}"/> 
    <param name="lastName" value="#{person.lastName}"/> 
  </page> 
</pages>
The <param> declaration is bidirectional, as with value bindings for JSF input:
  • When a non-Faces (GET) request for the view ID occurs, Seam sets the value of the named request parameter to the model object, after performing appropriate type conversions.
  • Any <s:link> or <s:button> includes the request parameter transparently. The parameter value is determined by evaluating the value binding during the render phase (when the <s:link> is rendered).
  • Any navigation rule with a <redirect/> to the view ID includes the request parameter transparently. The parameter value is determined by evaluating the value binding at the end of the invoke application phase.
  • The value is transparently propagated with any JSF form submission for the page with the given view ID. This means that view parameters behave like PAGE-scoped context variables for Faces requests.
However we arrive at /hello.xhtml, the value of the model attribute referenced in the value binding is held in memory, without the need for a conversation (or other server-side state).

8.4. Propagating request parameters

If only the name attribute is specified, the request parameter is propagated with the PAGE context (that is, it is not mapped to model property).
<pages> 
  <page view-id="/hello.xhtml" action="#{helloWorld.sayHello}"> 
    <param name="firstName" /> 
    <param name="lastName" /> 
  </page> 
</pages>
Page parameter propagation is especially useful when building multi-layered master-detail CRUD pages. You can use it to "remember" your view (for example, when pressing the Save button), and which entity you were editing.
  • Any <s:link> or <s:button> transparently propagates the request parameter if that parameter is listed as a page parameter for the view.
  • The value is transparently propagated with any JSF form submission for the page with the given view ID. This means that view parameters behave like PAGE-scoped context variables for Faces requests.
Although this is fairly complex, it is definitely worthwhile to dedicate time to an understanding of page parameters. Page parameters are the most elegant method of propagating state across non-Faces requests. Page parameters are particularly useful in certain situations. For example, if we have search screens with bookmarkable results pages, page parameters allow you to write handling for both POST and GET requests in the same code. Page parameters eliminate repetitive request parameter-listing in the view definition, and simplify redirect code.

8.5. URL rewriting with page parameters

Rewriting occurs based on patterns found for views in pages.xml. Seam URL rewriting performs both incoming and outgoing URL rewriting based on the same pattern. A simple pattern for this process is:
<page view-id="/home.xhtml"> 
  <rewrite pattern="/home" /> 
</page>
In this case, any incoming request for /home will be sent to /home.xhtml. Any link generated that would normally point to /home.seam will instead be rewritten as /home. Rewrite patterns only match the portion of the URL before the query parameters, so /home.seam?conversationId=13 and /home.seam?color=red will both be matched by this rewrite rule.
Rewrite rules can take query parameters into consideration, as shown with the following rules.
<page view-id="/home.xhtml"> 
  <rewrite pattern="/home/{color}" /> 
  <rewrite pattern="/home" /> 
</page>
In this case, an incoming request for /home/red will be served as if it was a request for /home.seam?color=red. Similarly, if color is a page parameter, an outgoing URL that would normally show as /home.seam?color=blue would instead be output as /home/blue. Rules are processed in order, so it is important to list specific rules before general rules.
Default Seam query parameters can also be mapped with URL rewriting, further concealing Seam's fingerprints. In the following example, /search.seam?conversationId=13 would be written as /search-13.
<page view-id="/search.xhtml"> 
  <rewrite pattern="/search-{conversationId}" /> 
  <rewrite pattern="/search" /> 
</page>
Seam URL rewriting provides simple, bidirectional rewriting on a per-view basis. For more complex rewriting rules that cover non-Seam components, Seam applications can continue to use the org.tuckey.URLRewriteFilter, or apply rewriting rules at the web server.
To use URL rewriting, the Seam rewrite filter must be enabled. Rewrite filter configuration is discussed in Section 28.1.3.3, “URL rewriting”.

8.6. Conversion and Validation

You can specify a JSF converter for complex model properties, in either of the following ways:
<pages> 
  <page view-id="/calculator.xhtml" action="#{calculator.calculate}"> 
    <param name="x" value="#{calculator.lhs}"/> 
    <param name="y" value="#{calculator.rhs}"/> 
    <param name="op" converterId="com.my.calculator.OperatorConverter" 
           value="#{calculator.op}"/> 
  </page> 
</pages>
<pages> 
  <page view-id="/calculator.xhtml" action="#{calculator.calculate}"> 
    <param name="x" value="#{calculator.lhs}"/> 
    <param name="y" value="#{calculator.rhs}"/> 
    <param name="op" converter="#{operatorConverter}" 
           value="#{calculator.op}"/> 
  </page> 
</pages>
JSF validators, and required="true" may also be used, in either of the following ways:
<pages> 
  <page view-id="/blog.xhtml"> 
    <param name="date" value="#{blog.date}" 
           validatorId="com.my.blog.PastDate" required="true"/> 
  </page> 
</pages>
<pages> 
  <page view-id="/blog.xhtml"> 
    <param name="date" value="#{blog.date}" 
           validator="#{pastDateValidator}" required="true"/> 
  </page> 
</pages>
Model-based Hibernate validator annotations are automatically recognized and validated. Seam also provides a default date converter to convert a string parameter value to a date and back.
When type conversion or validation fails, a global FacesMessage is added to the FacesContext.

8.7. Navigation

You can use standard JSF navigation rules defined in faces-config.xml in a Seam application. However, these rules have several limitations:
  • It is not possible to specify that request parameters are used when redirecting.
  • It is not possible to begin or end conversations from a rule.
  • Rules work by evaluating the return value of the action method; it is not possible to evaluate an arbitrary EL expression.
Another problem is that "orchestration" logic is scattered between pages.xml and faces-config.xml. It is better to unify this logic under pages.xml.
This JSF navigation rule:
<navigation-rule> 
  <from-view-id>/editDocument.xhtml</from-view-id> 
  <navigation-case> 
    <from-action>#{documentEditor.update}</from-action> 
    <from-outcome>success</from-outcome> 
    <to-view-id>/viewDocument.xhtml</to-view-id> 
    <redirect/> 
  </navigation-case> 
</navigation-rule>
Can be rewritten as follows:
<page view-id="/editDocument.xhtml"> 
  <navigation from-action="#{documentEditor.update}"> 
    <rule if-outcome="success"> 
      <redirect view-id="/viewDocument.xhtml"/> 
    </rule> 
  </navigation> 
</page>
However, this method pollutes DocumentEditor with string-valued return values (the JSF outcomes). Instead, Seam allows you to write:
<page view-id="/editDocument.xhtml"> 
  <navigation from-action="#{documentEditor.update}" 
              evaluate="#{documentEditor.errors.size}"> 
    <rule if-outcome="0"> 
      <redirect view-id="/viewDocument.xhtml"/> 
    </rule> 
  </navigation> 
</page>
Or even:
<page view-id="/editDocument.xhtml"> 
  <navigation from-action="#{documentEditor.update}"> 
    <rule if="#{documentEditor.errors.empty}"> 
      <redirect view-id="/viewDocument.xhtml"/> 
    </rule> 
  </navigation> 
</page>
The first form evaluates a value binding to determine the outcome value used by the subsequent rules. The second approach ignores the outcome and evaluates a value binding for each possible rule.
When an update succeeds, you probably want to end the current conversation, as:
<page view-id="/editDocument.xhtml"> 
  <navigation from-action="#{documentEditor.update}"> 
    <rule if="#{documentEditor.errors.empty}"> 
      <end-conversation/> 
      <redirect view-id="/viewDocument.xhtml"/> 
    </rule> 
  </navigation> 
</page>
As the conversation has ended, subsequent requests will not know which document you are interested in. You can pass the document ID as a request parameter, which also makes the view bookmarkable:
<page view-id="/editDocument.xhtml"> 
  <navigation from-action="#{documentEditor.update}"> 
    <rule if="#{documentEditor.errors.empty}"> 
      <end-conversation/> 
      <redirect view-id="/viewDocument.xhtml"> 
        <param name="documentId" value="#{documentEditor.documentId}"/> 
      </redirect> 
    </rule> 
  </navigation> 
</page>
Null outcomes are a special case in JSF, and are interpreted as instructions to redisplay the page. The following navigation rule matches any non-null outcome, but not the null outcome:
<page view-id="/editDocument.xhtml"> 
  <navigation from-action="#{documentEditor.update}"> 
    <rule> 
      <render view-id="/viewDocument.xhtml"/> 
    </rule> 
  </navigation> 
</page>
To perform navigation when a null outcome occurs, use the following:
<page view-id="/editDocument.xhtml"> 
  <navigation from-action="#{documentEditor.update}"> 
    <render view-id="/viewDocument.xhtml"/> 
  </navigation> 
</page>
The view ID can be given as a JSF EL expression:
<page view-id="/editDocument.xhtml"> 
  <navigation> 
    <rule if-outcome="success"> 
      <redirect view-id="/#{userAgent}/displayDocument.xhtml"/> 
    </rule> 
  </navigation> 
</page>

8.8. Fine-grained files for defining navigation, page actions and parameters

If you have a large number of different page actions and parameters — or even just a large number of navigation rules — it is suggested that you split the declarations into several smaller files. You can define actions and parameters for a page with the view ID /calc/calculator.xhtml in a resource named calc/calculator.page.xml. In this case, <page> is the root element, and the view ID is implied:
<page action="#{calculator.calculate}"> 
  <param name="x" value="#{calculator.lhs}"/> 
  <param name="y" value="#{calculator.rhs}"/> 
  <param name="op" converter="#{operatorConverter}" value="#{calculator.op}"/> 
</page>

8.9. Component-driven events

Seam components interact by calling each other's methods. Stateful components can even implement the observer/observable pattern. However, to enable more loosely-coupled interaction, Seam provides component-driven events.
Specify event listeners (observers) in components.xml.
<components> 
  <event type="hello"> 
    <action execute="#{helloListener.sayHelloBack}"/> 
    <action execute="#{logger.logHello}"/> 
  </event> 
</components>
Here, the event type is an arbitrary string.
When an event occurs, the actions registered for that event are called in the order they appear in components.xml. Seam provides a built-in component to raise events.
@Name("helloWorld") 
public class HelloWorld 
{ 
    public void sayHello() 
    { 
        FacesMessages.instance().add("Hello World!"); 
        Events.instance().raiseEvent("hello"); 
    } 
}
You can also use an annotation, like:
@Name("helloWorld") 
public class HelloWorld 
{ 
    @RaiseEvent("hello") 
    public void sayHello() 
    { 
        FacesMessages.instance().add("Hello World!"); 
    } 
}
This event producer is not dependent upon event consumers. The event listener can now be implemented with absolutely no dependency upon the producer:
@Name("helloListener") 
public class HelloListener 
{ 
    public void sayHelloBack() 
    { 
        FacesMessages.instance().add("Hello to you too!"); 
    } 
}
The method binding defined above in components.xml maps the event to the consumer. You can also do this with annotations:
@Name("helloListener") 
public class HelloListener { 
    @Observer("hello") 
    public void sayHelloBack() { 
        FacesMessages.instance().add("Hello to you too!"); 
    } 
}
In Seam, event objects do not need to propagate state between the event producer and listener. State is held in the Seam contexts, and shared between components. However, you can pass an event object as:
@Name("helloWorld") 
public class HelloWorld { 
    private String name; 
    public void sayHello() { 
        FacesMessages.instance().add("Hello World, my name is #0.", name); 
        Events.instance().raiseEvent("hello", name); 
    } 
}
@Name("helloListener") 
public class HelloListener { 
    @Observer("hello") 
    public void sayHelloBack(String name) { 
        FacesMessages.instance().add("Hello #0!", name); 
    } 
}

8.10. Contextual events

Seam defines a number of built-in events that the application uses for framework integration. The events are:

Table 8.1. Contextual Events

Event Description
org.jboss.seam.validationFailed
Called when JSFvalidation fails.
org.jboss.seam.noConversation
Called when there is no long-running conversation and a long-running conversation is required.
org.jboss.seam.preSetVariable.<name>
Called when the context variable <name> is set.
org.jboss.seam.postSetVariable.<name>
Called when the context variable <name> is set.
org.jboss.seam.preRemoveVariable.<name>
Called when the context variable <name> is unset.
org.jboss.seam.postRemoveVariable.<name>
Called when the context variable <name> is unset.
org.jboss.seam.preDestroyContext.<SCOPE>
Called before the <SCOPE> context is destroyed.
org.jboss.seam.postDestroyContext.<SCOPE>
Called after the <SCOPE> context is destroyed.
org.jboss.seam.beginConversation
Called whenever a long-running conversation begins.
org.jboss.seam.endConversation
Called whenever a long-running conversation ends.
org.jboss.seam.conversationTimeout
Called when a conversation timeout occurs. The conversation ID is passed as a parameter.
org.jboss.seam.postCreate.<name>
Called when the component <name> is created.
org.jboss.seam.preDestroy.<name>
Called when the component <name> is destroyed.
org.jboss.seam.beforePhase
Called before the start of a JSF phase.
org.jboss.seam.afterPhase
Called after the end of a JSF phase.
org.jboss.seam.postInitialization
Called when Seam has initialized and started all components.
org.jboss.seam.postReInitialization
Called when Seam has re-initialized and started all components after a redeploy.
org.jboss.seam.exceptionHandled.<type>
Called when an uncaught exception is handled by Seam.
org.jboss.seam.exceptionHandled
Called when an uncaught exception is handled by Seam.
org.jboss.seam.exceptionNotHandled
Called when there is no handler for an uncaught exception.
org.jboss.seam.afterTransactionSuccess
Called when a transaction succeeds in the Seam Application Framework.
org.jboss.seam.afterTransactionSuccess.<name>
Called when a transaction succeeds in the Seam Application Framework managing the entity <name> .
org.jboss.seam.security.loggedOut
Called when a user logs out.
org.jboss.seam.security.loginFailed
Called when a user authentication attempt fails.
org.jboss.seam.security.loginSuccessful
Called when a user is successfully authenticated.
org.jboss.seam.security.notAuthorized
Called when an authorization check fails.
org.jboss.seam.security.notLoggedIn
Called when there is no authenticated user and authentication is required.
org.jboss.seam.security.postAuthenticate
Called after a user is authenticated.
org.jboss.seam.security.preAuthenticate
Called before attempting to authenticate a user.
Seam components observe these events just as they observe any other component-driven event.

8.11. Seam interceptors

EJB3 introduced a standard interceptor model for session bean components. To add an interceptor to a bean, you need to write a class with a method annotated @AroundInvoke and annotate the bean with an @Interceptors annotation that specifies the name of the interceptor class. For example, the following interceptor checks that the user is logged in before invoking an action listener method:
public class LoggedInInterceptor 
{ 
   @AroundInvoke 
   public Object checkLoggedIn(InvocationContext invocation)      throws Exception 
     { 
       boolean isLoggedIn = Contexts.getSessionContext()
        .get("loggedIn")!=null; 
        if (isLoggedIn) 
        { 
         //the user is already logged in return invocation.proceed(); 
        } 
        else 
        { 
         //the user is not logged in, fwd to login page return "login"; 
        } 
    } 
}
To apply this interceptor to a session bean acting as an action listener, you must annotate the session bean @Interceptors(LoggedInInterceptor.class). However, Seam builds upon the interceptor framework in EJB3 by allowing you to use @Interceptors as a meta-annotation for class level interceptors (those annotated @Target(TYPE)). In this example, you create a @LoggedIn annotation, as follows:
@Target(TYPE) 
@Retention(RUNTIME) 
@Interceptors(LoggedInInterceptor.class) 
    public @interface LoggedIn {}
You can annotate the action listener bean with @LoggedIn to apply the interceptor:
@Stateless 
@Name("changePasswordAction") 
@LoggedIn 
@Interceptors(SeamInterceptor.class) 
public class ChangePasswordAction implements ChangePassword { 
    ... 
    public String changePassword() 
    { 
      ... 
    } 
}
Where interceptor order is important, add @Interceptor annotations to your interceptor classes to specify an order of interceptors.
@Interceptor(around={BijectionInterceptor.class, 
                     ValidationInterceptor.class, 
                     ConversationInterceptor.class}, 
    within=RemoveInterceptor.class) 
public class LoggedInInterceptor 
{ 
    ... 
}
You can have a client-side interceptor, for built-in EJB3 functions:
@Interceptor(type=CLIENT) 
public class LoggedInInterceptor 
{ 
    ... 
}
EJB interceptors are stateful, and their life cycles match the life cycles of the component they intercept. For interceptors that do not need to maintain state, Seam allows performance optimization where @Interceptor(stateless=true) is specified.
Much of Seam's functionality is implemented as a set of built-in Seam interceptors, including the interceptors named in the previous example. These interceptors exist for all interceptable Seam components; you need not specify them explicitly through annotations.
Seam interceptors can also be used with JavaBean components. EJB defines interception not only for business methods (using @AroundInvoke), but also for the life cycle methods @PostConstruct, @PreDestroy, @PrePassivate, and @PostActive. Seam supports these life cycle methods on both component and interceptor, not only for EJB3 beans, but also for JavaBean components (except @PreDestroy, which is not meaningful for JavaBean components).

8.12. Managing exceptions

JSF has a limited ability to handle exceptions. To work around this problem, Seam alows you to define treatment of an exception class through annotation, or through declaration in an XML file. This combines with the EJB3-standard @ApplicationException annotation, which specifies whether the exception should cause a transaction rollback.

8.12.1. Exceptions and transactions

EJB specifies well-defined rules to control whether an exception immediately marks the current transaction for rollback, when the exception is generated by a business method of the bean. System exceptions always cause a transaction rollback. Application exceptions do not cause a rollback by default, but they cause a rollback if @ApplicationException(rollback=true) is specified. (An application exception is any checked exception, or any unchecked exception annotated @ApplicationException. A system exception is any unchecked exception without an @ApplicationException annotation.)

Note

Marking a transaction for rollback is not the same as actually rolling back the transaction. The exception rules say that the transaction should only be marked for rollback, but it may still be active after the exception is generated.
Seam also applies the EJB3 exception rollback rules to Seam JavaBean components. These rules apply only in the Seam component layer. When an exception occurs outside the Seam component layer, Seam rolls back active transactions.

8.12.2. Enabling Seam exception handling

To enable Seam's exception handling, the master Servlet filter must be declared in web.xml:
<filter> 
  <filter-name>Seam Filter</filter-name> 
<filter-class>org.jboss.seam.servlet.SeamFilter</filter-class>
</filter> 
<filter-mapping> 
  <filter-name>Seam Filter</filter-name> 
  <url-pattern>*.seam</url-pattern> 
</filter-mapping>
For the exception handlers to execute, disable Facelets development mode in web.xml and Seam debug mode in components.xml.

8.12.3. Using annotations for exception handling

The following exception results in an HTTP 404 error whenever it propagates outside the Seam component layer. The exception does not roll back the current transaction immediately, but the transaction is rolled back if the exception is not caught by another Seam component.
@HttpError(errorCode=404) 
public class ApplicationException extends Exception 
{ 
    ... 
}
This exception results in a browser redirect whenever it propagates outside the Seam component layer. It also ends the current conversation. It causes an immediate rollback of the current transaction.
@Redirect(viewId="/failure.xhtml") 
@ApplicationException(rollback=true) 
public class UnrecoverableApplicationException extends RuntimeException 
{ 
    ... 
}

Note

Seam cannot handle exceptions that occur during JSF's RENDER_RESPONSE phase, as it is not possible to perform a redirect once writing to the response has begun.
You can also use EL to specify the viewId to redirect to. When this exception propagates outside the Seam component layer, it results in a redirect and a message to the user. It also immediately rolls back the current transaction.
@Redirect(viewId="/error.xhtml", message="Unexpected error") 
public class SystemException extends RuntimeException 
{ 
    ... 
}

8.12.4. Using XML for exception handling

As annotations cannot be added to all exception classes, Seam allows you to specify this functionality in pages.xml.
<pages>
  <exception class="javax.persistence.EntityNotFoundException">
    <http-error error-code="404"/>
  </exception>
  <exception class="javax.persistence.PersistenceException">
    <end-conversation/>
    <redirect view-id="/error.xhtml">
      <message>Database access failed</message>
    </redirect>
  </exception>
  <exception>
    <end-conversation/>
    <redirect view-id="/error.xhtml">
      <message>Unexpected failure</message>
    </redirect>
  </exception>   
</pages>
The final <exception> declaration does not specify a class, and acts as catch-all for any exception without specified handling through annotations or in pages.xml.
You can also use EL to specify the view-id to redirect to. You can also access the handled exception instance through EL. Seam places the exception in the conversation context. For example, to access the exception message:
...
  throw new AuthorizationException("You are not allowed to do this!");
<pages>
  <exception class="org.jboss.seam.security.AuthorizationException">
    <end-conversation/>
    <redirect view-id="/error.xhtml">
      <message severity="WARN">
        #{org.jboss.seam.handledException.message}
      </message>
    </redirect>
  </exception>
</pages>
The org.jboss.seam.handledException holds the nested exception that was handled by an exception handler. The outermost (wrapper) exception is also available as org.jboss.seam.caughtException.

8.12.4.1. Suppressing exception logging

For the exception handlers defined in pages.xml, it is possible to declare the level at which the exception is logged, or to suppress exception logging altogether. The log and log-level attributes are used to control exception logging. No log message is generated if the specified exception occurs when log="false" is set, as shown here:
<exception class="org.jboss.seam.security.NotLoggedInException" 
           log="false"> 
  <redirect view-id="/register.xhtml">
    <message severity="warn">
      You must be a member to use this feature
    </message> 
  </redirect> 
</exception>
If the log attribute is not specified, then it defaults to true — that is, the exception will be logged. Alternatively, you can specify the log-level to control the level at which the exception is logged:
<exception class="org.jboss.seam.security.NotLoggedInException" 
           log-level="info"> 
  <redirect view-id="/register.xhtml"> 
    <message severity="warn">
      You must be a member to use this feature
    </message> 
  </redirect> 
</exception>
The acceptable values for log-level are: fatal, error, warn, info, debug, and trace. If the log-level is not specified, or if an invalid value is configured, log-level defaults to error.

8.12.5. Some common exceptions

If you are using JPA:
<exception class="javax.persistence.EntityNotFoundException">
  <redirect view-id="/error.xhtml">
    <message>Not found</message>
  </redirect>
</exception>
<exception class="javax.persistence.OptimisticLockException">
  <end-conversation/>
  <redirect view-id="/error.xhtml">
    <message>
      Another user changed the same data, please try again
    </message>
  </redirect>
</exception>
If you are using the Seam Application Framework:
<exception class="org.jboss.seam.framework.EntityNotFoundException">
  <redirect view-id="/error.xhtml"> 
    <message>Not found</message>
  </redirect> 
</exception>
If you are using Seam Security:
<exception class="org.jboss.seam.security.AuthorizationException">
  <redirect>
    <message>You do not have permission to do this</message>
  </redirect>
</exception>
<exception class="org.jboss.seam.security.NotLoggedInException">
  <redirect view-id="/login.xhtml">
    <message>Please log in first</message>
  </redirect>
</exception>
And, for JSF:
<exception class="javax.faces.application.ViewExpiredException"> 
  <redirect view-id="/error.xhtml">
    <message>Your session has timed out, please try again</message> 
  </redirect> 
</exception>
A ViewExpiredException occurs when a user posts to a page after the session has expired. The conversation-required and no-conversation-view-id settings in the Seam page descriptor, discussed in Section 9.4, “Requiring a long-running conversation”, allow finer-grained control over session expiration while accessing a page used within a conversation.

Chapter 9. Conversations and workspace management

This chapter discusses Seam's conversation model in detail.
The notion of a Seam conversation came about as a combination of three separate concepts:
  • The concept of a workspace, and effective workspace management.
  • The concept of an application transaction with optimistic semantics. Existing frameworks, based around a stateless architecture, were unable to provide effective management of extended persistence contexts.
  • The concept of a workflow task.
By unifying these ideas and providing deep support in the framework, we have created a powerful construct that allows richer and more efficient applications, using less verbose code.

9.1. Seam's conversation model

All examples so far operate under a simple conversation model with the following rules:
  • A conversation context is always active during the apply request values, process validation, update model values, invoke application, and render response phases of the JSF request life cycle.
  • At the end of the restore view phase of the JSF request life cycle, Seam attempts to restore any previous long-running conversation context. If none exists, Seam creates a new temporary conversation context.
  • When a @Begin method is encountered, the temporary conversation context is promoted to a long-running conversation.
  • When an @End method is encountered, any long-running conversation context is demoted to a temporary conversation.
  • At the end of the render response phase of the JSF request life cycle, Seam either stores the contents of a long-running conversation context, or destroys the contents of a temporary conversation context.
  • Faces request (a JSF postback) propagates the conversation context. By default, non-Faces requests (for example, GET requests) do not propagate the conversation context.
  • If the JSF request life cycle is foreshortened by a redirect, Seam transparently stores and restores the current conversation context, unless the conversation was already ended through @End(beforeRedirect=true).
Seam transparently propagates the conversation context (including the temporary conversation context) across JSF postbacks and redirects. Without special additions, a non-Faces request (a GET request, for example) does not propagate the conversation context, and is processed in a new temporary conversation. This is usually — but not always — the desired behavior.
To propagate a Seam conversation across a non-Faces request, the Seam conversation ID must be explicitly coded as a request parameter:
<a href="main.jsf?#{manager.conversationIdParameter}=#{conversation.id}"> 
  Continue
</a>
Or, for JSF:
<h:outputLink value="main.jsf"> 
  <f:param name="#{manager.conversationIdParameter}" 
     value="#{conversation.id}"/> 
  <h:outputText value="Continue"/> 
</h:outputLink>
If you use the Seam tag library, this is equivalent:
<h:outputLink value="main.jsf"> 
  <s:conversationId/> 
  <h:outputText value="Continue"/> 
</h:outputLink>
The code to disable propagation of the conversation context for a postback is similar:
<h:commandLink action="main" value="Exit"> 
  <f:param name="conversationPropagation" value="none"/> 
</h:commandLink>
The equivalent for the Seam tag library is:
<h:commandLink action="main" value="Exit"> 
  <s:conversationPropagation type="none"/> 
</h:commandLink>

Note

Disabling conversation context propagation is not the same as ending the conversation.
The conversationPropagation request parameter or <s:conversationPropagation> tag can also be used to begin and end conversations, or to begin a nested conversation.
<h:commandLink action="main" value="Exit"> 
  <s:conversationPropagation type="end"/> 
</h:commandLink>
<h:commandLink action="main" value="Select Child"> 
  <s:conversationPropagation type="nested"/> 
</h:commandLink>
<h:commandLink action="main" value="Select Hotel"> 
  <s:conversationPropagation type="begin"/> 
</h:commandLink>
<h:commandLink action="main" value="Select Hotel"> 
  <s:conversationPropagation type="join"/> 
</h:commandLink>
This conversation model makes it easy to build applications which behave correctly with respect to multi-window operation. For many applications, this is all that is required. Some complex applications have one or both of the following additional requirements:
  • A conversation spans many smaller units of user interaction, which execute serially or even concurrently. The smaller nested conversations have their own isolated set of conversation state, and have access to the state of the outer conversation.
  • The user can switch between many conversations within the same browser window. This feature is called workspace management.

9.2. Nested conversations

A nested conversation is created by invoking a method marked @Begin(nested=true) within the scope of an existing conversation. A nested conversation has its own conversation context, but can read values from the outer conversation's context. The outer conversation's context is read-only within a nested conversation, but because objects are obtained by reference, changes to the objects themselves are reflected in the outer context.
  • Nesting a conversation initializes a context that is stacked on the context of the original, or outer, conversation. The outer conversation is considered the parent.
  • Values outjected or set directly into the nested conversation’s context do not affect the objects accessible in the parent conversation’s context.
  • Injection, or a context lookup from the conversation context, first looks for the value in the current conversation context. If no value is found, lookup continues down the conversation stack, if the conversation is nested. This behavior can be overridden.
When an @End is subsequently encountered, the nested conversation is destroyed, and the outer conversation resumes, popping the conversation stack. Conversations can be nested to any arbitrary depth.
Certain user activities (workspace management, or the back button) can cause the outer conversation to be resumed before the inner conversation ends. In this case, it is possible to have multiple concurrent nested conversations belonging to the same outer conversation. If the outer conversation ends before a nested conversation ends, Seam destroys all nested conversation contexts along with the outer context.
The conversation at the bottom of the conversation stack is the root conversation. Destroying this conversation always destroys all descendant conversations. You can achieve this declaratively by specifying @End(root=true).
A conversation can be thought of as a continuable state. Nested conversations allow the application to capture a consistent continuable state at various points in a user interaction. This ensures correct behavior in the case of backbuttoning and workspace management.
As mentioned previously, if a component exists in a parent conversation of the current nested conversation, the nested conversation uses the same instance. Occasionally, it is useful to have a different instance in each nested conversation, so that the component instance of the parent conversation is invisible to its child conversations. You can achieve this behavior by annotating the component @PerNestedConversation.

9.3. Starting conversations with GET requests

JSF does not define action listeners triggered when a page is accessed through a non-Faces request (for example, an HTTP GET request). This can occur when a user bookmarks the page, or navigates to the page through <h:outputLink>.
Sometimes you want a conversation to begin immediately when a page is accessed. As there is no JSF action method, you cannot annotate the action with @Begin.
Further problems arise when the page requires state to be fetched into a context variable. You have seen two methods of solving this problem. If the state is held in a Seam component, you can fetch the state in a @Create method. If not, you can define a @Factory method for the context variable.
If neither option works for you, Seam allows you to define a page action in the pages.xml file.
<pages> 
  <page view-id="/messageList.xhtml" action="#{messageManager.list}"/>
  ... 
</pages>
This action method is called at the beginning of the render response phase — that is, any time the page is about to be rendered. If a page action returns a non-null outcome, Seam processes appropriate JSF and Seam navigation rules. This can result in a completely different page rendering.
If beginning a conversation is all you want to do before rendering the page, you can use a built-in action method:
<pages> 
  <page view-id="/messageList.xhtml" action="#{conversation.begin}"/>
  ... 
</pages>
You can also call this built-in action from a JSF control, and #{conversation.end} similarly ends conversations.
You can use the <begin-conversation> element for further control over joining existing conversations, or beginning a nested conversation or an atomic conversation; as follows:
<pages> 
  <page view-id="/messageList.xhtml"> 
    <begin-conversation nested="true" /> 
  <page> 
  ... 
</pages>
There is also a <end-conversation> element.
<pages> 
  <page view-id="/home.xhtml"> 
    <end-conversation/> 
  <page> 
  ...
</pages>
You have five options to begin a conversation immediately when a page is accessed:
  • Annotate the @Create method with @Begin
  • Annotate the @Factory method with @Begin
  • Annotate the Seam page action method with @Begin
  • Use <begin-conversation> in pages.xml
  • Use #{conversation.begin} as the Seam page action method

9.4. Requiring a long-running conversation

Some pages are relevant only in the context of a long-running conversation. One way to restrict access to such a page is to make the existence of a long-running conversation a prerequisite to the page being rendered.
Seam's page descriptor has a conversation-required attribute, which allows you to indicate that the current conversation must be long-running (or nested) for a page to be rendered, as:
<page view-id="/book.xhtml" conversation-required="true"/>

Note

At present, you cannot indicate which long-running conversation is required. However, you can build on the basic authorization by checking whether a specific value is also present in the conversation within a page action.
When Seam determines that the page has been requested when no long-running conversation is present, it performs the following actions:
  • Raises a contextual event called org.jboss.seam.noConversation
  • Registers a warning status message with the bundle key org.jboss.seam.NoConversation
  • Redirects the user to an alternative page, if defined in the no-conversation-view-id attribute, as:
    <pages no-conversation-view-id="/main.xhtml"/>
    This page will be used across the entire application; at present, multiple alternative pages cannot be defined.

9.5. Using <s:link> and <s:button>

JSF command links always perform a form submission with JavaScript, which causes problems with the web browser's 'open in new window' or 'open in new tab' feature. To get this functionality in plain JSF use <h:outputLink>, but there are two major limitations to this method:
  • JSF provides no way to attach an action listener to <h:outputLink>
  • JSF does not propagate the selected row of a DataModel, as there is no actual form submission.
To solve the first problem, Seam implements the notion of a page action. You can work around the second problem by passing a request parameter and requerying for the selected object on the server-side; in some cases like the Seam blog example application, this is the best approach. As it is RESTful and does not require a server-side state, bookmarking is supported. In other cases, where bookmarking is not required, @DataModel and @DataModelSelection are transparent and convenient.
To replace this missing functionality, and to simplify conversation propagation further, Seam provides the <s:link> JSF tag.
The link can specify only the JSF ID:
<s:link view="/login.xhtml" value="Login"/>
It can also specify an action method, in which case the action outcome determines the page that results:
<s:link action="#{login.logout}" value="Logout"/>
If both a JSF view ID and an action method are specified, the view will be used unless the action method returns a non-null outcome:
<s:link view="/loggedOut.xhtml"  action="#{login.logout}" value="Logout"/>
The link automatically propagates the selected row of a DataModel inside <h:dataTable>:
<s:link view="/hotel.xhtml" action="#{hotelSearch.selectHotel}" 
   value="#{hotel.name}"/>
You can leave the scope of an existing conversation:
<s:link view="/main.xhtml" propagation="none"/>
You can begin, end, or nest conversations:
<s:link action="#{issueEditor.viewComment}" propagation="nest"/>
Finally, use <s:button> to 'link' rendered as a button:
<s:button action="#{login.logout}" value="Logout"/>

9.6. Success messages

Messages are commonly displayed to the user to indicate the success or failure of an action. A JSF FacesMessage is convenient for this function. However, a successful action often requires a browser redirect. Since JSF does not propagate Faces messages across redirects, it is difficult to display success messages in plain JSF.
The built-in conversation-scoped Seam component named facesMessages solves this problem. (This requires the Seam redirect filter.)
@Name("editDocumentAction") 
@Stateless 
public class EditDocumentBean implements EditDocument 
{
    @In EntityManager em; 
    @In Document document; 
    @In FacesMessages facesMessages; 

    public String update() 
    { 
        em.merge(document); 
        facesMessages.add("Document updated");  
    } 
}
When a message is added to facesMessages, it is used in the next render response phase for the current conversation. Since Seam preserves temporary conversation contexts across redirects, this works even without a long-running conversation.
You can include JSF EL expressions in a Faces message summary:
facesMessages.add("Document #{document.title} was updated");
Messages are displayed as:
<h:messages globalOnly="true"/>

9.7. Natural conversation IDs

When working with conversations that deal with persistent objects, there are several reasons to use the natural business key of the object instead of the standard 'surrogate' conversation ID.
Easy redirect to existing conversation
If the user requests the same operation twice, it can be useful to redirect to an existing conversation. Consider the following example:
You are on Ebay, halfway through paying for an item you won as a Christmas present for your parents. You want to send it straight to them, but once you have entered your payment details, you cannot remember your parents' address. While you find the address, you accidentally reuse the same browser window, but now you need to return to complete the payment for the item.
With a natural conversation, you can easily rejoin the previous conversation and pick up where you left. In this case, you can rejoin the payForItem conversation with the itemId as the conversation ID.
User-friendly URLs
A user-friendly URL is meaningful (refers to page contents plainly, without using ID numbers), and has a navigable hierarchy (that is, you can navigate by editing the URL).
With a natural conversation, applications can generate long, complex URLs, but display simple, memorable URLs by using URLRewrite. In the case of the hotel booking example, http://seam-hotels/book.seam?hotel=BestWesternAntwerpen is rewritten as http://seam-hotels/book/BestWesternAntwerpen — much clearer. Note that URLRewrite relies on parameters; for example hotel in the previous example must map to a unique parameter on the domain model.

9.8. Creating a natural conversation

Natural conversations are defined in pages.xml file:
<conversation name="PlaceBid" parameter-name="auctionId" 
              parameter-value="#{auction.auctionId}"/>
In the above definition the conversation name is PlaceBid. The conversation name identifies a named conversation uniquely, and is used by the page definition to identify the named conversation in which to participate.
The parameter-name attribute defines the request parameter that holds the natural conversation ID, and replaces the default conversation ID parameter. In this case, parameter-name is auctionId. This means that the URL of your page will contain auctionId=765432 instead of a conversation parameter like cid=123.
The final attribute, parameter-value, defines an EL expression to evaluate the value of the natural business key to be used as the conversation ID. In this example, the conversation ID will be the primary key value of the auction instance currently in scope.
Next, we define the pages participating in the named conversation. This is done by specifying the conversation attribute for a page definition:
<page view-id="/bid.xhtml" conversation="PlaceBid" login-required="true"> 
  <navigation from-action="#{bidAction.confirmBid}">            <rule if-outcome="success"> 
      <redirect view-id="/auction.xhtml"> 
        <param name="id" value="#{bidAction.bid.auction.auctionId}"/> 
      </redirect> 
    </rule>        
  </navigation> 
</page>

9.9. Redirecting to a natural conversation

When initiating or redirecting to a natural conversation, there are several ways to specify the natural conversation name. You can start with the following page definition:
<page view-id="/auction.xhtml"> 
  <param name="id" value="#{auctionDetail.selectedAuctionId}"/> 
  <navigation from-action="#{bidAction.placeBid}"> 
    <redirect view-id="/bid.xhtml"/> 
  </navigation> 
</page>
Here that invoking #{bidAction.placeBid} redirects you to /bid.xhtml, which is configured with the natural conversation ID PlaceBid. Your action method declaration looks like this:
@Begin(join = true) 
public void placeBid()
When named conversations are specified in the <page/> element, redirection to the named conversation occurs as part of navigation rules following the invocation of the action method. This can cause problems when redirecting to an existing conversation, as redirection needs to occur before the action method is invoked. Therefore, the conversation name must be specified before the action is invoked. One method of doing this uses the <s:conversationName> tag:
<h:commandButton id="placeBidWithAmount" styleClass="placeBid" 
   action="#{bidAction.placeBid}"> 
  <s:conversationName value="PlaceBid"/> 
</h:commandButton>
You can also specify the conversationName attribute for either the s:link or s:button:
<s:link value="Place Bid" action="#{bidAction.placeBid}" 
   conversationName="PlaceBid"/>

9.10. Workspace management

Workspace management is the ability to 'switch' conversations in a single window. Seam workspace management is completely transparent at the Java level. To enable workspace management:
  • Provide description text for each view ID (when using JSF or Seam navigation rules) or page node. Workspace switchers display this description text.
  • Include one or more workspace switcher JSF or Facelets fragments in your page. Standard fragments support workspace management via a drop-down menu and a list of conversations, or "breadcrumbs".

9.10.1. Workspace management and JSF navigation

Seam uses JSF or Seam navigation rules to switch to a conversation by restoring the current view-id for that conversation. The descriptive text for the workspace is defined in the pages.xml file, which Seam expects to find in the WEB-INF directory alongside faces-config.xml file:
<pages> 
  <page view-id="/main.xhtml"> 
    <description>Search hotels: #{hotelBooking.searchString}</description> 
  </page> 
  <page view-id="/hotel.xhtml"> 
    <description>View hotel: #{hotel.name}</description> 
  </page> 
  <page view-id="/book.xhtml"> 
    <description>Book hotel: #{hotel.name}</description> 
  </page> 
  <page view-id="/confirm.xhtml"> 
    <description>Confirm: #{booking.description}</description> 
  </page> 
</pages>

Note

The Seam application works even if the pages.xml file is not present. However, workplace switching is not available in this case.

9.10.2. The conversation switcher

Include the following fragment in your JSF or Facelets page to include a drop-down menu that allows you to switch to any current conversation, or any other page of the application:
<h:selectOneMenu value="#{switcher.conversationIdOrOutcome}"> 
  <f:selectItem itemLabel="Find Issues" itemValue="findIssue"/> 
  <f:selectItem itemLabel="Create Issue" itemValue="editIssue"/> 
  <f:selectItems value="#{switcher.selectItems}"/> 
</h:selectOneMenu> 
<h:commandButton action="#{switcher.select}" value="Switch"/>
This example includes a menu that contains an item for each conversation, and two additional items that allow you to begin an additional conversation.
Only conversations with a description (specified in pages.xml file) are included in the drop-down menu.

9.10.3. The conversation list

The conversation list is similar to the conversation switcher, except that it is displayed as a table:
<h:dataTable value="#{conversationList}" var="entry" 
   rendered="#{not empty conversationList}">
  <h:column>
    <f:facet name="header">Workspace</f:facet>
    <h:commandLink action="#{entry.select}" value="#{entry.description}"/>
    <h:outputText value="[current]" rendered="#{entry.current}"/>
  </h:column>
  <h:column>
    <f:facet name="header">Activity</f:facet>
    <h:outputText value="#{entry.startDatetime}">
      <f:convertDateTime type="time" pattern="hh:mm a"/>
    </h:outputText>
    <h:outputText value=" - "/>
    <h:outputText value="#{entry.lastDatetime}">
      <f:convertDateTime type="time" pattern="hh:mm a"/>
    </h:outputText>
  </h:column>
  <h:column>
    <f:facet name="header">Action</f:facet>
    <h:commandButton action="#{entry.select}" value="#{msg.Switch}"/>
    <h:commandButton action="#{entry.destroy}" value="#{msg.Destroy}"/>
  </h:column>
</h:dataTable>
You can customize the conversation list for your applications.
Only conversations with a description are included in the list.
Note that the conversation list also allows you to destroy workspaces.

9.10.4. Breadcrumbs

Breadcrumbs are a list of links to conversations in the current conversation stack. They are useful for applications with a nested conversation model:
<ui:repeat value="#{conversationStack}" var="entry"> 
  <h:outputText value=" | "/> 
  <h:commandLink value="#{entry.description}" action="#{entry.select}"/> 
</ui:repeat>

9.11. Conversational components and JSF component bindings

Conversational components have one minor limitation: they cannot be used to hold bindings to JSF components. (Generally we recommend avoiding this feature of JSF unless absolutely necessary, as it creates a hard dependency from application logic to the view.) On a postback request, component bindings are updated during the Restore View phase, before the Seam conversation context has been restored.
You can work around this by storing component bindings with an event-scoped component, and injecting this into the requiring conversation-scoped component.
@Name("grid") 
@Scope(ScopeType.EVENT) 
public class Grid { 
    private HtmlPanelGrid htmlPanelGrid; // getters and setters 
    ... 
}
@Name("gridEditor") 
@Scope(ScopeType.CONVERSATION) 
public class GridEditor { 
    @In(required=false) 
    private Grid grid; 
    ... 
}
Also, a conversation-scoped component cannot be injected into an event-scoped component with a JSF control bound to it. This includes Seam built-in components like facesMessages.
You can also access the JSF component tree with the implicit uiComponent handle. The following example accesses the getRowIndex() of the UIData component that backs the data table during iteration, and prints the current row number:
<h:dataTable id="lineItemTable" var="lineItem" 
   value="#{orderHome.lineItems}"> 
  <h:column> 
    Row: #{uiComponent[&#39;lineItemTable&#39;].rowIndex} 
  </h:column> 
  ... 
</h:dataTable>
In this map, JSF UI components are available with their client identifier.

9.12. Concurrent calls to conversational components

Section 6.1.9, “Concurrency model” contains a general discussion of concurrent calls to Seam components. This section discusses the most common situation in which you will encounter concurrency — when accessing conversational components from AJAX requests. This section also describes the options provided by an AJAX client library, and RichFaces, to control events originating from the client.
Conversational components do not allow true concurrent access, so Seam queues each request for serial processing. This allows each request to be executed in a deterministic fashion. However, there are some limitations to a simple queue. If a method, for whatever reason, takes a long time to complete, running it whenever the client generates a request can lead to Denial of Service attacks. AJAX is also often used to provide quick status updates to users, so continuing to run an action after a long time is not useful.
Therefore, when you work inside a long-running conversation, Seam queues the action even for a period of time (the concurrent request timeout). If Seam cannot process the event before timeout, it creates a temporary conversation and prints a message for the user, informing them of the timeout. It is therefore important not to flood the server with AJAX events.
You can set a sensible default for the concurrent request timeout (in milliseconds) in the components.xml file:
<core:manager concurrent-request-timeout="500" />
The concurrent request timeout can also be adjusted on a page-by-page basis:
<page view-id="/book.xhtml" conversation-required="true" 
      login-required="true" concurrent-request-timeout="2000" />
The above section discussed AJAX requests that appear serially. The client tells the server that an event has occurred, and then rerenders part of the page based on the result. This approach is sufficient when the AJAX request is lightweight (the methods called are simple, for example, calculating the sum of a column of numbers), but complex computations require a different approach.
A poll-based approach is where the client sends an AJAX request to the server, causing actions to begin immediate asynchronous execution on the server. The client then polls the server for updates while the actions are executed. This approach is useful when it is important that no action in a long-running action sequence times out.

9.12.1. How should we design our conversational AJAX application?

The first question is whether to use the simpler "serial" request method, or a polling approach.
If you want to use serial requests, you must estimate the time required for your request to complete. You may need to alter the concurrent request timeout for this page, as discussed in the previous section. A queue on the server side is probably necessary, to prevent requests from flooding the server. If the event occurs often (for example, a keystroke, or onblur of input fields) and immediate client update is not a priority, set a request delay on the client side. Remember to factor the possibility of server-side queuing into your request delay.
Finally, the client library may provide an option to abort unfinished duplicate requests in favor of the most recent requests.
A polling approach requires less fine-tuning — simply mark your action method @Asynchronous and decide on a polling interval:
int total; 
// This method is called when an event occurs on the client // It takes a really long time to execute 
@Asynchronous
public void calculateTotal() 
{ 
    total = someReallyComplicatedCalculation(); 
} 
// This method is called as the result of the poll 
// It's very quick to execute    
public int getTotal() 
{ 
    return total; 
}

9.12.2. Dealing with errors

However carefully you design your application to queue concurrent requests to your conversational component, there is a risk that the server may become overloaded. The overloaded server will be unable to process all the requests before the request has to wait longer than the concurrent-request-timeout. In this case, Seam generates ConcurrentRequestTimeoutException that can be handled in the pages.xml file. We recommend sending an HTTP 503 error:
<exception class="org.jboss.seam.ConcurrentRequestTimeoutException" log-level="trace">
 <http-error error-code="503" />
</exception>

503 Service Unavailable (HTTP/1.1 RFC)

The server is currently unable to handle the request due to a temporary overloading or maintenance of the server. The implication is that this is a temporary condition which will be alleviated after some delay.
Alternatively you could redirect to an error page:
<exception class="org.jboss.seam.ConcurrentRequestTimeoutException" log-level="trace">
 <end-conversation/>
 <redirect view-id="/error.xhtml">
  <message>The server is too busy to process your request, please try again later</message>
 </redirect>
</exception>
Seam Remoting and JSF 2 can both handle HTTP error codes. Seam Remoting will pop-up a dialog box showing the HTTP error. JSF 2 provides support for handling HTTP errors by providing a user definable callback. For example, to show the error message to the user:
<script type="text/javascript">
 jsf.ajax.addOnError(function(data) 
 { 
  alert("An error occurred");
 });
</script>

JSF 2 JavaScript documentation

More details about JSF 2 JavaScript API can be seen at: http://javaserverfaces.java.net/nonav/docs/2.0/jsdocs/symbols/jsf.ajax.html
If instead of an error code, the server reports that the view has expired, perhaps because the session timed out, you can use a standard javax.faces.context.ExceptionHandler to handle this scenario.

9.12.3. RichFaces (Ajax4jsf)

RichFaces (Ajax4jsf) is the most common AJAX library used with Seam, and provides all the controls discussed in the previous section.
attachQueue
Customizes queuing for specific components and behaviors. It can override queue settings of a component or attach specific requests to a queue.
ignoreDupResponses
Ignores the response produced by a request if a more recent "similar" request is already queued. ignoreDupResponses="true" does not cancel the processing of the request on the server side; it only prevents unnecessary updates on the client side.
With Seam conversations, this option should be used with care, as it allows multiple concurrent requests.
requestDelay
Defines the time in milliseconds for which the request remains on the queue. If, at this time, the request has not been processed, the request is either sent (regardless of whether a response has been received), or discarded (if there is a more recent "similar" event queued).
With Seam conversations, this option should be used with care, as it allows multiple concurrent requests. The delay that you set (in combination with the concurrent request timeout) must be longer than the time the action will take to execute.
<a:poll render="total" interval="1000" />
Polls the server and renders an area, as required.

Chapter 10. Seam and Object/Relational Mapping

Seam provides extensive support for the two most popular persistence architectures for Java: Hibernate, and the Java Persistence API introduced with Enterprise JavaBeans 3.0 (EJB3). Seam's unique state-management architecture allows the most sophisticated ORM integration of any web application framework.

10.1. Introduction

Seam was created because of frustration with the statelessness typical of the previous generation of Java application architectures. Seam's state management architecture was originally designed to solve problems relating to persistence, particularly problems associated with optimistic transaction processing. Scalable online applications always use optimistic transactions. An atomic (database/JTA) level transaction should not span a user interaction unless the application is designed to support only a very small number of concurrent clients. But almost all work involves first displaying data to a user, and then updating that data. Hibernate was designed to support a persistence context that spanned an optimistic transaction.
Unfortunately, the "stateless" architectures that preceded Seam and EJB3 had no construct to represent an optimistic transaction. Instead, these architectures provided persistence contexts scoped to the atomic transaction. This resulted in many problems for users, and causes the number one user complaint: Hibernate's LazyInitializationException. A construct was required to represent an optimistic transaction in the application tier.
EJB3 recognizes this problem, and introduces the idea of a stateful component (a stateful session bean) with an extended persistence context scoped to the lifetime of the component. This is a partial solution to the problem (and is a useful construct in and of itself), but there are still two issues with this approach:
  • The life cycle of the stateful session bean must be managed manually with code in the web tier.
  • Propagation of the persistence context between stateful components in the same optimistic transaction is possible, but very complex.
Seam solves the first problem by providing conversations, and scoping stateful session bean components to the conversation. (Most conversations actually represent optimistic transactions in the data layer.) This is sufficient for many simple applications where persistence context propagation is not required, such as the Seam booking example application. For more complex applications, with many loosely-interacting components in each conversation, propagation of the persistence context across components becomes an important issue. Therefore, Seam extends the persistence context management model of EJB3, to provide conversation-scoped extended persistence contexts.

10.2. Seam managed transactions

EJB session beans feature declarative transaction management. The EJB container can start a transaction transparently when the bean is invoked, and end it when the invocation ends. If we write a session bean method that acts as a JSF action listener, all work associated with that action can be performed as one transaction, and committed or rolled back when the action is completely processed. This is a useful feature, and for some Seam applications, this is all that is required.
However, there is a problem with this approach: in a request from a single method call to a session bean, a Seam application may not perform all data access.
  • when the request requires processing by several loosely-coupled components, with each component being called independently from the web layer. It is common to see multiple calls per request from the web layer to EJB components in Seam.
  • when view rendering requires lazily-fetched associations.
The more transactions that exist per request, the more likely we are to encounter atomicity and isolation problems while our application processes many concurrent requests. All write operations should occur in the same transaction.
To work around this problem, Hibernate users developed the open session in view pattern. This is also important because some frameworks (Spring, for example) use transaction-scoped persistence contexts, which caused LazyInitializationExceptions when unfetched associations were accessed.
Open session in view is usually implemented as a single transaction that spans the entire request. The most serious problem with this implementation is that we cannot be certain that a transaction is successful until we commit it — but when the transaction commits, the view is fully rendered, and the rendered response may already be synchronized the client, so there is no way to notify the user that their transaction did not succeed.
Seam solves the problems with transaction isolation and association fetching, while working around the major flaw in open session in view, with two changes:
  • Seam uses an extended persistence context that is scoped to the conversation instead of the transaction.
  • Seam uses two transactions per request. The first spans from the beginning of the restore view phase until the end of the invoke application phase; the second spans the length of the render response phase. (In some applications, the first phase will begin later, at the beginning of the apply request values phase.)
The next section takes you through the setup of a conversation-scoped persistence context. Before this, we will enable Seam transaction management. You can use conversation-scoped persistence contexts without Seam transaction management, and Seam transaction management is useful even without Seam-managed persistence contexts, but they work most effectively together.

10.2.1. Disabling Seam-managed transactions

Seam transaction management is enabled by default for all JSF requests, but can be disabled in components.xml:
<core:init transaction-management-enabled="false"/>

<transaction:no-transaction />

10.2.2. Configuring a Seam transaction manager

Seam provides a transaction management abstraction for beginning, committing, rolling back, and synchronizing with transactions. By default, Seam uses a JTA transaction component to integrate with container-managed and programmatic EJB transactions. If you work in a Java EE 5 environment, install the EJB synchronization component in components.xml:
<transaction:ejb-transaction />
However, if you work in a non-EE 5 container, Seam attempts to auto-detect the correct transaction synchronization mechanism. If Seam is unable to detect the correct mechanism, you may need to configure one of the following:
  • configure JPA RESOURCE_LOCAL managed transactions with the javax.persistence.EntityTransaction interface. EntityTransaction starts the transaction at the beginning of the apply request values phase.
  • configure Hibernate managed transactions with the org.hibernate.Transaction interface. HibernateTransaction starts the transaction at the beginning of the apply request values phase.
  • configure Spring managed transactions with the org.springframework.transaction.PlatformTransactionManager interface. The Spring PlatformTransactionManagement manager may begin the transaction at the beginning of the apply request values phase if the userConversationContext attribute is set.
  • Explicitly disable Seam managed transactions
To configure JPA RESOURCE_LOCAL transaction management, add the following to your components.xml, where #{em} is the name of the persistence:managed-persistence-context component. If your managed persistence context is named entityManager, you may leave out the entity-manager attribute. (For further information, see Section 10.3, “Seam-managed persistence contexts”.)
<transaction:entity-transaction entity-manager="#{em}"/>
To configure Hibernate managed transactions, declare the following in your components.xml, where #{hibernateSession} is the name of the project's persistence:managed-hibernate-session component. If your managed hibernate session is named session, you can opt to leave out the session attribute. (For further information, see Section 10.3, “Seam-managed persistence contexts”.)
<transaction:hibernate-transaction session="#{hibernateSession}"/>
To explicitly disable Seam managed transactions, declare the following in your components.xml:
<transaction:no-transaction />
For information about configuring Spring-managed transactions see Section 26.5, “Using Spring PlatformTransactionManagement”.

10.2.3. Transaction synchronization

Transaction synchronization provides callbacks for transaction-related events such as beforeCompletion() and afterCompletion(). By default, Seam uses its own transaction synchronization component, which requires explicit use of the Seam transaction component when committing transactions so that synchronization callbacks are correctly executed. If you work in a Java EE 5 environment, declare <transaction:ejb-transaction/> in components.xml to ensure that Seam synchronization callbacks are called correctly if the container commits a transaction outside Seam.

10.3. Seam-managed persistence contexts

If you use Seam outside a Java EE 5 environment, you cannot rely upon the container to manage the persistence context lifestyle. Even within EE 5 environments, propagating the persistence context between loosely-coupled components in a complex application can be difficult and error-prone.
In this case, you will need to use a managed persistence context (for JPA) or a managed session (for Hibernate) in your components. A Seam-managed persistence context is just a built-in Seam component that manages an instance of EntityManager or Session in the conversation context. You can inject it with @In.
Seam-managed persistence contexts are extremely efficient in a clustered environment. Seam can perform optimizations for container-managed persistence contexts that the EJB3 specification does not allow. Seam supports transparent failover of extended persistence contexts, without replicating any persistence context state between nodes. (We hope to add this support to the next revision of the EJB specification.)

10.3.1. Using a Seam-managed persistence context with JPA

Configuring a managed persistence context is easy. In components.xml, write:
<persistence:managed-persistence-context name="bookingDatabase" 
   auto-create="true" 
   persistence-unit-jndi-name="java:/EntityManagerFactories/bookingData"/>
This configuration creates a conversation-scoped Seam component named bookingDatabase, which manages the life cycle of EntityManager instances for the persistence unit (EntityManagerFactory instance) with JNDI name java:/EntityManagerFactories/bookingData.
You must bind the EntityManagerFactory into JNDI. In JBoss, you can do this by adding the following property setting to persistence.xml.
<property name="jboss.entity.manager.factory.jndi.name" 
          value="java:/EntityManagerFactories/bookingData"/>
Now we can inject our EntityManager with:
@In EntityManager bookingDatabase;
If you use EJB3, and mark your class or method @TransactionAttribute(REQUIRES_NEW), then the transaction and persistence context should not propagate to method calls on this object. However, since the Seam-managed persistence context propagates to any component within the conversation, it propagates to methods marked REQUIRES_NEW. Therefore, if you mark a method REQUIRES_NEW, you should access the entity manager with @PersistenceContext.

10.3.2. Using a Seam-managed Hibernate session

Seam-managed Hibernate sessions work in a similar fashion. In components.xml:
<persistence:hibernate-session-factory name="hibernateSessionFactory"/>

<persistence:managed-hibernate-session name="bookingDatabase" 
             auto-create="true" 
             session-factory-jndi-name="java:/bookingSessionFactory"/>
Here, java:/bookingSessionFactory is the name of the session factory specified in hibernate.cfg.xml.
<session-factory name="java:/bookingSessionFactory">
  <property name="transaction.flush_before_completion">true</property>
  <property name="connection.release_mode">after_statement</property>
  <property name="transaction.manager_lookup_class">
    org.hibernate.transaction.JBossTransactionManagerLookup
  </property>
  <property name="transaction.factory_class">
    org.hibernate.transaction.JTATransactionFactory
  </property>
  <property name="connection.datasource">
    java:/bookingDatasource
  </property>
  ...
</session-factory>

Note

Seam does not synchronize the session with the database, so always enable hibernate.transaction.flush_before_completion to ensure that the session is automatically synchronized before the JTA transaction commits.
We can now inject a managed Hibernate Session into our JavaBean components with the following code:
@In Session bookingDatabase;

10.3.3. Seam-managed persistence contexts and atomic conversations

Conversation-scoped persistence contexts let you program optimistic transactions spanning multiple server requests, without using merge(), reloading data at the beginning of each request, or wrestling with exceptions (LazyInitializationException or NonUniqueObjectException).
You can achieve transaction isolation and consistency by using optimistic locking. Both Hibernate and EJB3 make optimistic locking easy with the @Version annotation.
By default, the persistence context is synchronized with the database (flushed) at the end of each transaction. Sometimes this is desirable, but often we prefer all changes to be held in memory, and only written to the database when the conversation ends successfully. This allows for truly atomic conversations with EJB3 persistence. However, Hibernate provides this feature as a vendor extension to the FlushModeTypes defined by the specification. We expect other vendors will soon provide a similar extension.
Seam lets you specify FlushModeType.MANUAL when beginning a conversation. Currently, this works only when Hibernate is the underlying persistence provider, but we plan to support other equivalent vendor extensions.
@In EntityManager em; //a Seam-managed persistence context
@Begin(flushMode=MANUAL)

public void beginClaimWizard() {
  claim = em.find(Claim.class, claimId);
}
Now, the claim object remains managed by the persistence context for the entire conversation. We can make changes to the claim:
public void addPartyToClaim() {
  Party party = ....;
  claim.addParty(party);
}
But these changes will not be flushed to the database until we explicitly force synchronization to occur:
@End public void commitClaim() { 
  em.flush(); 
}
You can also set the flushMode to MANUAL from pages.xml, for example in a navigation rule:
<begin-conversation flush-mode="MANUAL" />
You can set any Seam-managed persistence context to use manual flush mode:

             <components xmlns="http://www.jboss.org/schemas/seam/components" 
	xmlns:core="http://www.jboss.org/schemas/seam/core"> 
  <core:manager conversation-timeout="120000" 
        default-flush-mode="manual" /> 
</components>

Important

When using SMPC in your Stateful bean, manual flush mode is ignored as this mode is a specific Hibernate extension to the JPA specification. Seam cannot control the flush mode of the persistence context on an SFSB. This means there is no manual flush available for SFSB.

10.4. Using the JPA "delegate"

The EntityManager interface lets you access a vendor-specific API with the getDelegate() method. We recommend using Hibernate as your vendor, and org.hibernate.Session as your delegate interface, but if you require a different JPA provider, see Section 28.2, “Using Alternate JPA Providers” for further information.
Regardless of your vendor, there are several approaches to using the delegate in your Seam components. One approach is:
@In EntityManager entityManager; 
@Create public void init() { 
  ((Session)entityManager.getDelegate() ).enableFilter("currentVersions");
}
If you, like most Java users, would rather avoid using typecasts, you can also access the delegate by adding the following line to components.xml:
<factory name="session" scope="STATELESS" auto-create="true" 
         value="#{entityManager.delegate}"/>
The session can now be injected directly:
@In Session session;

@Create
public void init() {
  session.enableFilter("currentVersions");
}

10.5. Using EL in EJB-QL/HQL

Seam proxies the EntityManager or Session object whenever you use a Seam-managed persistence context or inject a container-managed persistence context with @PersistenceContext. This lets you safely and efficiently use EL expressions in your query strings. For example, this:
User user = em.createQuery("from User where username=#{user.username}")
                          .getSingleResult();
is equivalent to:
User user = em.createQuery("from User where username=:username")
                          .setParameter("username", user.getUsername()) 
                          .getSingleResult();

Warning

Do not use the format below, because it is vulnerable to SQL injection attacks, as well as being inefficient.

User user = em.createQuery("from User where username=" + user.getUsername()).getSingleResult(); //BAD!

10.6. Using Hibernate filters

Hibernate's most unique, useful feature is the filter. Filters provide a restricted view of the data in the database. You can find more information in the Hibernate documentation, but this section takes you through one easy, effective method of incorporating filters into Seam.
Seam-managed persistence contexts can have a list of filters defined, which will be enabled whenever an EntityManager or Hibernate Session is first created. (These can only be used when Hibernate is the underlying persistence provider.)
<persistence:filter name="regionFilter">
  <persistence:name>region</persistence:name>
  <persistence:parameters>
    <key>regionCode</key>
    <value>#{region.code}</value>
  </persistence:parameters>
</persistence:filter>

<persistence:filter name="currentFilter">
  <persistence:name>current</persistence:name>
  <persistence:parameters>
    <key>date</key>
    <value>#{currentDate}</value>
  </persistence:parameters>
</persistence:filter>

<persistence:managed-persistence-context name="personDatabase"
  persistence-unit-jndi-name="java:/EntityManagerFactories/personDatabase">
  <persistence:filters>
    <value>#{regionFilter}</value>
    <value>#{currentFilter}</value>
  </persistence:filters>
</persistence:managed-persistence-context>

Chapter 11. JSF form validation in Seam

In plain JSF, validation is defined in the view:
<h:form>
  <h:messages/>

  <div>
    Country:
    <h:inputText value="#{location.country}" required="true">
      <my:validateCountry/>
    </h:inputText>
  </div>
    
  <div>
    Zip code:
    <h:inputText value="#{location.zip}" required="true">
      <my:validateZip/>
    </h:inputText>
  </div>

  <h:commandButton/>
</h:form>
In practice, this approach usually violates DRY, since most "validation" actually enforces constraints that are part of the data model, and exist all the way down to the database schema definition. Seam provides support for model-based constraints defined with Hibernate Validator.
We will begin by defining our constraints, on our Location class:
public class Location {
    private String country;
    private String zip;
  
    @NotNull
    @Size(max=30)
    public String getCountry() { return country; }
    public void setCountry(String c) { country = c; }

    @NotNull
    @Size(max=6)
    @Pattern("^\d*$")
    public String getZip() { return zip; }
    public void setZip(String z) { zip = z; }
}
In practice, it may be more elegant to use custom constraints rather than those built into Hibernate Validator:
public class Location {
    private String country;
    private String zip;
  
    @NotNull
    @Country
    public String getCountry() { return country; }
    public void setCountry(String c) { country = c; }

    @NotNull
    @ZipCode
    public String getZip() { return zip; }
    public void setZip(String z) { zip = z; }
}
Whichever method we choose, we no longer need specify the validation type to be used in the JSF page. Instead, we use <s:validate> to validate against the constraint defined on the model object.
<h:form>
  <h:messages/>

  <div>
    Country:
    <h:inputText value="#{location.country}" required="true">
      <s:validate/>
    </h:inputText>
  </div>
  
  <div>
    Zip code:
    <h:inputText value="#{location.zip}" required="true">
      <s:validate/>
    </h:inputText>
  </div>
  
  <h:commandButton/>

</h:form>

Note

Specifying @NotNull on the model does not eliminate the need for required="true" to appear on the control. This is a limitation of the JSF validation architecture.
This approach defines constraints on the model, and presents constraint violations in the view.
The design is better, but not much less verbose than our initial design. Now, we will use <s:validateAll>:
<h:form>
    
  <h:messages/>

  <s:validateAll>

  <div>
    Country:
    <h:inputText value="#{location.country}" required="true"/>
  </div>

  <div>
    Zip code:
    <h:inputText value="#{location.zip}" required="true"/>
  </div>

  <h:commandButton/>

  </s:validateAll>

</h:form>
This tag adds an <s:validate> to every input in the form. In a large form, this can save a lot of typing.
Next, we need to display feedback to the user when validation fails. Currently, all messages are displayed at the top of the form. To correlate the message with an input, you must define a label by using the standard label attribute on the input component.
<h:inputText value="#{location.zip}" required="true" label="Zip:"> 
  <s:validate/> 
</h:inputText>
Inject this value into the message string with the placeholder {0} (the first and only parameter passed to a JSF message for a Hibernate Validator restriction). See the internationalization section for more information on where to define these messages.

Note

validator.length={0} length must be between {min} and {max}
We would prefer the message to be displayed beside the field with the error, highlight the field and label, and display an image next to the field. In plain JSF, only the first is possible. We also want to display a colored asterisk beside the label of each required form field.
This is a lot of functionality for each field. We do not want to specify highlighting and the layout of the image, message, and input field for every field on the form, so we specify the layout in a facelets template:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:s="http://jboss.org/schema/seam/taglib">           
  <div>
  
    <s:label styleClass="#{invalid?'error':''}">
      <ui:insert name="label"/>
      <s:span styleClass="required" rendered="#{required}">*</s:span>
    </s:label>
        
    <span class="#{invalid?'error':''}">
      <h:graphicImage value="/img/error.gif" rendered="#{invalid}"/>
      <s:validateAll>
        <ui:insert/>
      </s:validateAll>
    </span>
        
    <s:message styleClass="error"/>
        
  </div>
    
</ui:composition>
We can include this template for each of our form fields by using <s:decorate>:
<h:form>
  <h:messages globalOnly="true"/>

    <s:decorate template="edit.xhtml">
        <ui:define name="label">Country:</ui:define>
        <h:inputText value="#{location.country}" required="true"/>
    </s:decorate>
    
    <s:decorate template="edit.xhtml">
        <ui:define name="label">Zip code:</ui:define>
        <h:inputText value="#{location.zip}" required="true"/>
    </s:decorate>

    <h:commandButton/>

</h:form>
Finally, we can use RichFaces Ajax to display validation messages while the user navigates around the form:
<h:form>
  <h:messages globalOnly="true"/>

    <s:decorate id="countryDecoration" template="edit.xhtml">
        <ui:define name="label">Country:</ui:define>
        <h:inputText value="#{location.country}" required="true">
            <a:ajax event="blur" render="countryDecoration" bypassUpdates="true"/>
        </h:inputText>
    </s:decorate>
    
    <s:decorate id="zipDecoration" template="edit.xhtml">
        <ui:define name="label">Zip code:</ui:define>
        <h:inputText value="#{location.zip}" required="true">
            <a:ajax event="blur" render="zipDecoration" bypassUpdates="true"/>
        </h:inputText>
    </s:decorate>

    <h:commandButton/>

</h:form>
Stylistically, it is better to define explicit IDs for important page controls, particularly if you want automated UI testing. If explicit IDs are not provided, JSF will generate its own — but they will not remain static if anything on the page is changed.
<h:form id="form">
  <h:messages globalOnly="true"/>

    <s:decorate id="countryDecoration" template="edit.xhtml">
        <ui:define name="label">Country:</ui:define>
        <h:inputText id="country" value="#{location.country}" required="true">
            <a:ajax event="blur" render="countryDecoration" bypassUpdates="true"/>
        </h:inputText>
    </s:decorate>
    
    <s:decorate id="zipDecoration" template="edit.xhtml">
        <ui:define name="label">Zip code:</ui:define>
        <h:inputText id="zip" value="#{location.zip}" required="true">
            <a:ajax event="blur" render="zipDecoration" bypassUpdates="true"/>
        </h:inputText>
    </s:decorate>

    <h:commandButton/>

</h:form>
If you want to specify a different message to be displayed when validation fails, you can use the Seam message bundle with the Hibernate Validator:
public class Location {
    private String name;
    private String zip;
  
    // Getters and setters for name

    @NotNull
    @Size(max=6)
    @ZipCode(message="#{messages['location.zipCode.invalid']}")
    public String getZip() { return zip; }
    public void setZip(String z) { zip = z; }
}
location.zipCode.invalid = The zip code is not valid for #{location.name}

Chapter 12. Groovy integration

Seam has a great capability for Rapid Application Development (RAD). Seam allows you to utilize dynamic languages with your existing platform, while retaining compatibility with standard Java APIs. Static and dynamic languages are integrated, so there is no need for context-switching, and you can use the same annotations and APIs to write a dynamic Seam component as you would for a regular Seam component.

12.1. Groovy introduction

Groovy is an agile, Java-based dynamic language, with additional features inspired by Python, Ruby, and Smalltalk. Being Java-based, with Java objects and classes, Groovy is easy to learn and integrates seamlessly with existing Java libraries and frameworks.

12.2. Writing Seam applications in Groovy

Since Groovy objects are Java objects, any Seam component can be written and deployed with Groovy. You can also combine Groovy and Java classes in the same application.

12.2.1. Writing Groovy components

You will need to use Groovy 1.1 or higher to support annotations. The rest of this chapter shows how to use Groovy in a Seam application.

12.2.1.1. Entity

Example 12.1. Using Groovy in a Seam Application

@Entity
@Name("hotel")
class Hotel implements Serializable {
    @Id @GeneratedValue
    Long id
          
        @Size(max=50) @NotNull
        String name

        @Size(max=100) @NotNull
        String address

        @Size(max=40) @NotNull
        String city

        @Size(min=2, max=10) @NotNull
        String state

        @Size(min=4, max=6) @NotNull
        String zip

        @Size(min=2, max=40) @NotNull
        String country

        @Column(precision=6, scale=2)
        BigDecimal price

        @Override
        String toString(){
        return "Hotel(${name},${address},${city},${zip})"
    }
}
Since Groovy supports properties, there is no need to explicitly write verbose getters and setters. In the previous example, the hotel class can be accessed from Java as hotel.getCity() — the getters and setters are generated by the Groovy compiler. This makes the entity code very concise.

12.2.2. Seam component

You can write Seam components in Groovy exactly as you would in Java: annotations mark classes as Seam components.

Example 12.2. Writing Seam Components in Groovy

@Scope(ScopeType.SESSION)
@Name("bookingList")
class BookingListAction implements Serializable
{
    @In EntityManager em
    @In User user
    @DataModel List<Booking> bookings
    @DataModelSelection Booking booking
    @Logger Log log

    @Factory 
    public void getBookings()
    {
        bookings = em.createQuery('''
        select b from Booking b
        where b.user.username = :username
        order by b.checkinDate''').
            setParameter("username", user.username).
            getResultList()
    }
        
    public void cancel()
    {
        log.info("Cancel booking: #{bookingList.booking.id} 
                 for #{user.username}")
        Booking cancelled = em.find(Booking.class, booking.id)
        if (cancelled != null) em.remove( cancelled )
           getBookings()
           FacesMessages.instance().add("Booking cancelled for confirmation 
                                         number #{bookingList.booking.id}", 
                                         new Object[0])
    }
}

12.2.3. seam-gen

Seam-gen interacts transparently with Groovy. No additional infrastructure is required to include Groovy code in seam-gen-backed projects — when writing an entity, just place your .groovy files in src/main. When writing an action, place your .groovy files in src/hot.

12.3. Deployment

Deploying Groovy classes works like deploying Java classes. As with JavaBeans component classes, Seam can redeploy GroovyBeans component classes without restarting the application.

12.3.1. Deploying Groovy code

Groovy entities, session beans, and components all require compilation to deploy — use the groovyc ant task. Once compiled, a Groovy class is identical to a Java class, and the application server will treat them equally. This allows a seamless mix of Groovy and Java code.

12.3.2. Native .groovy file deployment at development time

Seam supports .groovy file hot deployment (deployment without compilation) in incremental hot deployment mode. This mode is development-only, and enables a fast edit/test cycle. Follow the configuration instructions at Section 5.8, “Seam and incremental hot deployment” to set up .groovy hot deployment. Deploy your Groovy code (.groovy files) into the WEB-INF/dev directory. The GroovyBean components will deploy incrementally, without needing to restart either application or application server.

Note

The native .groovy file deployment has the same limitations as the regular Seam hot deployment:
  • components must be either JavaBeans or GroovyBeans — they cannot be EJB3 beans.
  • entities cannot be hot deployed.
  • hot-deployable components are not visible to any classes deployed outside WEB-INF/dev.
  • Seam debug mode must be enabled.

12.3.3. seam-gen

Seam-gen transparently supports Groovy file deployment and compilation. This includes the native .groovy file hot deployment available during development. In WAR-type projects, Java and Groovy classes in src/hot are automatic candidates for incremental hot deployment. In production mode, Groovy files will be compiled prior to deployment.
There is a Booking demonstration, written completely in Groovy and supporting incremental hot deployment, in examples/groovybooking.

Important

The groovybooking example uses maven-antrun-plugin, which JBDS cannot import automatically. As a result, the example cannot be built automatically in JBDS. To build the example, use Maven (mvn clean package).

Chapter 13. The Seam Application Framework

Seam makes it easy to create applications with annotated plain Java classes. We can make common programming tasks even easier by providing a set of pre-built components that are reusable with configuration or extension.
The Seam Application Framework can reduce the amount of code you need to write in a web application for basic database access with either Hibernate or JPA. The framework contains a handful of simple classes that are easy to understand and to extend where required.

13.1. Introduction

The components provided by the Seam Application Framework can be used in two separate approaches. The first approach is to install and configure an instance of the component in components.xml, as with other built-in Seam components. For example, the following fragment (from components.xml) installs a component that performs basic CRUD operations for a Person entity:
<framework:entity-home name="personHome" entity-class="eg.Person" 
           entity-manager="#{personDatabase}"> 
<framework:id>#{param.personId}</framework:id> 
</framework:entity-home>
If this approach seems too XML-heavy, you can approach this through extension:
@Name("personHome") 
public class PersonHome extends EntityHome<Person> { 
@In EntityManager personDatabase; 
  public EntityManager getEntityManager() { 
    return personDatabase; 
  } 
}
The major advantage to the second approach is that the framework classes were designed for extension and customization, so it is easy to add extra functionality or override the built-in functionality.
Another advantage is that you have the option of using EJB stateful session beans (or plain JavaBean components) as your classes:
@Stateful 
@Name("personHome") 
public class PersonHome extends EntityHome<Person> 
                        implements LocalPersonHome { }
You can also make your classes stateless session beans. In this case you must use injection to provide the persistence context, even if it is called entityManager:
@Stateless 
@Name("personHome") 
public class PersonHome extends EntityHome<Person> 
                        implements LocalPersonHome { 
  @In EntityManager entityManager; 
  public EntityManager getPersistenceContext() { 
    entityManager; 
  } 
}
At present, the Seam Application Framework provides four main built-in components: EntityHome and HibernateEntityHome for CRUD, and EntityQuery and HibernateEntityQuery for queries.
The Home and Query components are written so that they can be session-, event- or conversation-scoped. The scope depends upon the state model you wish to use in your application.
The Seam Application Framework works only with Seam-managed persistence contexts. By default, components will expect a persistence context named entityManager.

13.2. Home objects

A Home object provides persistence operations for a particular entity class. Suppose we have our Person class:
@Entity 
public class Person { 
  @Id private Long id; 
  private String firstName; 
  private String lastName; 
  private Country nationality; 
  //getters and setters... 
}
We can define a personHome component either through configuration:
<framework:entity-home name="personHome" entity-class="eg.Person" />
Or through extension:
@Name("personHome") 
public class PersonHome extends EntityHome<Person> {}
A Home object provides operations like persist(), remove(), update() and getInstance(). Before you can call remove() or update(), you must set the identifier of the object you are interested in, using the setId() method.
For example, we can use a Home directly from a JSF page:
<h1>Create Person</h1> 
<h:form> 
  <div>
    First name: <h:inputText value="#{personHome.instance.firstName}"/>
  </div> 
  <div>
    Last name:  <h:inputText value="#{personHome.instance.lastName}"/>
  </div> 
  <div> 
    <h:commandButton value="Create Person" 
       action="#{personHome.persist}"/> 
  </div> 
</h:form>
It is useful to be able to refer to Person as person, so we will add that line to components.xml (if we are using configuration):
<factory name="person" value="#{personHome.instance}"/> 
<framework:entity-home name="personHome" entity-class="eg.Person" />
Or, if we are using extension, we can add a @Factory method to PersonHome:
@Name("personHome") 
public class PersonHome extends EntityHome<Person> { 
  @Factory("person") 
  public Person initPerson() { 
    return getInstance(); 
  } 
}
This change simplifies our JSF page to the following:
<h1>Create Person</h1> 
<h:form> 
  <div>
    First name: <h:inputText value="#{person.firstName}"/>
  </div> 
  <div>
    Last name: <h:inputText value="#{person.lastName}"/>
  </div> 
  <div> 
    <h:commandButton value="Create Person" 
       action="#{personHome.persist}"/> 
  </div> 
</h:form>
This is all the code required to create new Person entries. If we want to be able to display, update, and delete pre-existing Person entries in the database, we need to be able to pass the entry identifier to the PersonHome. An excellent method is through page parameters:
<pages> 
  <page view-id="/editPerson.xhtml"> 
    <param name="personId" value="#{personHome.id}"/> 
  </page> 
</pages>
Now we can add the extra operations to our JSF page:
<h1> 
  <h:outputText rendered="#{!personHome.managed}" value="Create Person"/> 
  <h:outputText rendered="#{personHome.managed}" value="Edit Person"/> 
</h1> 
<h:form> 
  <div>
    First name: <h:inputText value="#{person.firstName}"/>
  </div> 
  <div>
    Last name: <h:inputText value="#{person.lastName}"/>
  </div> 
  <div> 
    <h:commandButton value="Create Person" action="#{personHome.persist}" 
       rendered="#{!personHome.managed}"/> 
    <h:commandButton value="Update Person" action="#{personHome.update}" 
       rendered="#{personHome.managed}"/> 
    <h:commandButton value="Delete Person" action="#{personHome.remove}" 
       rendered="#{personHome.managed}"/> 
  </div> 
</h:form>
When we link to the page with no request parameters, the page will be displayed as a Create Person page. When we provide a value for the personId request parameter, it will be an Edit Person page.
If we need to create Person entries with their nationality initialized, we can do so easily. Via configuration:
<factory name="person" value="#{personHome.instance}"/> 
<framework:entity-home name="personHome" entity-class="eg.Person" 
           new-instance="#{newPerson}"/> 
<component name="newPerson" class="eg.Person"> 
  <property name="nationality">#{country}</property> 
</component>
Or via extension:
@Name("personHome") 
public class PersonHome extends EntityHome<Person> { 
  @In Country country; 
  @Factory("person") 
  public Person initPerson() { 
    return getInstance(); 
  } 
  protected Person createInstance() { 
    return new Person(country); 
  } 
}
The Country could be an object managed by another Home object, for example, CountryHome.
To add more sophisticated operations (association management, etc.), we simply add methods to PersonHome.
@Name("personHome") 
public class PersonHome extends EntityHome<Person> { 
  @In Country country; 
  @Factory("person") 
  public Person initPerson() { 
    return getInstance(); 
  } 
  protected Person createInstance() { 
    return new Person(country); 
  } 
  public void migrate() { 
    getInstance().setCountry(country); 
    update(); 
  } 
}
The Home object raises an org.jboss.seam.afterTransactionSuccess event when a transaction (a call to persist(), update() or remove()) succeeds. By observing this event, we can refresh our queries when the underlying entities change. If we only want to refresh certain queries when a particular entry is persisted, updated, or removed, we can observe the org.jboss.seam.afterTransactionSuccess.<name> (where <name> is the name of the entity).
The Home object automatically displays Faces messages when an operation succeeds. To customize these messages we can, again, use configuration:
<factory name="person" value="#{personHome.instance}"/> 
<framework:entity-home name="personHome" entity-class="eg.Person" 
           new-instance="#{newPerson}"> 
  <framework:created-message>
    New person #{person.firstName} #{person.lastName} created
  </framework:created-message> 
  <framework:deleted-message>
    Person #{person.firstName} #{person.lastName} deleted
  </framework:deleted-message> 
  <framework:updated-message>
    Person #{person.firstName} #{person.lastName} updated
  </framework:updated-message> 
</framework:entity-home> 
<component name="newPerson" class="eg.Person"> 
  <property name="nationality">#{country}</property> 
</component>
Or extension:
@Name("personHome") 
public class PersonHome extends EntityHome<Person> { 
  @In Country country; 
  @Factory("person") 
  public Person initPerson() { 
    return getInstance(); 
  } 
  protected Person createInstance() { 
    return new Person(country); 
  } 
  protected String getCreatedMessage() { 
    return createValueExpression("New person #{person.firstName} 
                                             #{person.lastName} created"); 
  } 
  protected String getUpdatedMessage() { 
    return createValueExpression("Person #{person.firstName} 
                                         #{person.lastName} updated"); 
  } 
  protected String getDeletedMessage() { 
  return createValueExpression("Person #{person.firstName} 
                                       #{person.lastName} deleted"); 
  } 
}
The best way to specify messages is to put them in a resource bundle known to Seam — by default, the bundle named messages.
Person_created=New person #{person.firstName} #{person.lastName} created 
Person_deleted=Person #{person.firstName} #{person.lastName} deleted 
Person_updated=Person #{person.firstName} #{person.lastName} updated
This enables internationalization, and keeps your code and configuration clean of presentation concerns.

13.3. Query objects

If we need a list of all Person instances in the database, we can use a Query object, like the following.
<framework:entity-query name="people" ejbql="select p from Person p"/>
We can use it from a JSF page:
<h1>List of people</h1> 
<h:dataTable value="#{people.resultList}" var="person"> 
  <h:column> 
    <s:link view="/editPerson.xhtml" 
            value="#{person.firstName} #{person.lastName}"> 
      <f:param name="personId" value="#{person.id}"/> 
    </s:link> 
  </h:column> 
</h:dataTable>
If you require pagination support:
<framework:entity-query name="people" ejbql="select p from Person p" 
           order="lastName" max-results="20"/>
Use a page parameter to determine which page to display:
<pages> 
  <page view-id="/searchPerson.xhtml"> 
    <param name="firstResult" value="#{people.firstResult}"/> 
  </page> 
</pages>
The JSF code for pagination control is slightly verbose, but manageable:
<h1>Search for people</h1> 
<h:dataTable value="#{people.resultList}" var="person"> 
  <h:column>
   
    <s:link view="/editPerson.xhtml" 
            value="#{person.firstName} #{person.lastName}">       
      <f:param name="personId" value="#{person.id}"/> 
    </s:link>
     
  </h:column>
   
</h:dataTable>
 
<s:link view="/search.xhtml" rendered="#{people.previousExists}" 
        value="First Page"> 
  <f:param name="firstResult" value="0"/> 
</s:link>
 
<s:link view="/search.xhtml" rendered="#{people.previousExists}" 
        value="Previous Page"> 
  <f:param name="firstResult" value="#{people.previousFirstResult}"/> 
</s:link>
 
<s:link view="/search.xhtml" rendered="#{people.nextExists}" 
        value="Next Page"> 
  <f:param name="firstResult" value="#{people.nextFirstResult}"/> 
</s:link>
 
<s:link view="/search.xhtml" rendered="#{people.nextExists}" 
        value="Last Page"> 
  <f:param name="firstResult" value="#{people.lastFirstResult}"/> 
</s:link>
Real search screens let the user enter optional search criteria to narrow the list of returned results. The Query object lets you specify optional restrictions to support this usecase:
<component name="examplePerson" class="Person"/> 
<framework:entity-query name="people" ejbql="select p from Person p" 
           order="lastName" max-results="20"> 
  <framework:restrictions> 
  
    <value>
      lower(firstName) like lower(concat(#{examplePerson.firstName},'%&'))
    </value>
    
    <value>
      lower(lastName) like lower(concat(#{examplePerson.lastName},'%&'))
    </value>
     
  </framework:restrictions> 
</framework:entity-query>
Notice the use of an "example" object.
<h1>Search for people</h1> 
<h:form>
 
  <div>
    First name: <h:inputText value="#{examplePerson.firstName}"/>
  </div>
   
  <div>
    Last name: <h:inputText value="#{examplePerson.lastName}"/>
  </div>
   
  <div>
    <h:commandButton value="Search" action="/search.xhtml"/>
  </div>
   
</h:form>
 
<h:dataTable value="#{people.resultList}" var="person"> 
  <h:column> 
    <s:link view="/editPerson.xhtml" 
       value="#{person.firstName} #{person.lastName}"> 
      <f:param name="personId" value="#{person.id}"/> 
    </s:link> 
  </h:column>  
</h:dataTable>
To refresh the query when the underlying entities change, we observe the org.jboss.seam.afterTransactionSuccess event:
<event type="org.jboss.seam.afterTransactionSuccess"> 
  <action execute="#{people.refresh}" /> 
</event>
Or, to refresh the query when the person entity is persisted, updated or removed through PersonHome:
<event type="org.jboss.seam.afterTransactionSuccess.Person"> 
  <action execute="#{people.refresh}" /> 
</event>
Unfortunately, Query objects do not work well with join fetch queries. We do not recommend using pagination with these queries. You will need to implement your own method of total result number calculation by overriding getCountEjbql().
All of the examples in this section have shown re-use via configuration. It is equally possibly to re-use via extension:

13.4. Controller objects

The class Controller and its subclasses (EntityController, HibernateEntityController and BusinessProcessController) are an optional part of the Seam Application Framework. These classes provide a convenient method to access frequently-used built-in components and component methods. They save keystrokes, and provide an excellent foothold for new users to explore the rich functionality built into Seam.
For example, RegisterAction (from the Seam registration example) looks like this:
@Stateless 
@Name("register") 
public class RegisterAction extends EntityController implements Register { 
  @In private User user; 
  public String register() { 
    List existing = createQuery("select u.username from 
                                 User u where u.username=:username").
                                 setParameter("username", 
                                 user.getUsername()).getResultList(); 
    if ( existing.size()==0 ) { 
      persist(user); 
      info("Registered new user #{user.username}"); 
      return "/registered.jspx"; 
    } else { 
      addFacesMessage("User #{user.username} already exists"); 
      return null; 
    } 
  } 
}

Chapter 14. Seam and JBoss Rules

Seam makes it easy to call JBoss Rules (Drools) rulebases from Seam components.

14.1. Installing rules

The first step is to make an instance of org.drools.RuleBase available in a Seam context variable. For testing purposes, Seam provides a built-in component that compiles a static set of rules from the classpath. You can install this component via components.xml:
<drools:rule-base name="policyPricingRules">
  <drools:rule-files>
    <value>policyPricingRules.drl</value> 
  </drools:rule-files> 
</drools:rule-base>
This component compiles rules from a set of DRL (.drl) or decision table (.xls) files and caches an instance of org.drools.RuleBase in the Seam APPLICATION context. Note that you will likely need to install multiple rule bases in a rule-driven application.
If you want to use a Drools DSL, you must also specify the DSL definition:
<drools:rule-base name="policyPricingRules" dsl-file="policyPricing.dsl"> 
  <drools:rule-files> 
    <value>policyPricingRules.drl</value> 
  </drools:rule-files> 
</drools:rule-base>
If you want to register a custom consequence exception handler through the RuleBaseConfiguration, you need to write the handler. This is demonstrated in the following example:
@Scope(ScopeType.APPLICATION)
@Startup
@Name("myConsequenceExceptionHandler")
public class MyConsequenceExceptionHandler 
       implements ConsequenceExceptionHandler, Externalizable {
  public void readExternal(ObjectInput in) throws IOException, 
                                           ClassNotFoundException { }

  public void writeExternal(ObjectOutput out) throws IOException { }

  public void handleException(Activation activation, 
                              WorkingMemory workingMemory,
                              Exception exception) {
    throw new ConsequenceException( exception, activation.getRule() );
  }
}
and register it:
<drools:rule-base name="policyPricingRules" 
                  dsl-file="policyPricing.dsl" 
                  consequence-exception-handler=
                    "#{myConsequenceExceptionHandler}">
  <drools:rule-files>
    <value>policyPricingRules.drl</value>
  </drools:rule-files>
</drools:rule-base>
In most rules-driven applications, rules must be dynamically deployable. A Drools RuleAgent is useful to manage the RuleBase. The RuleAgent can connect to a Drools rule server (BRMS), or hot-deploy rules packages from a local file repository. The RulesAgent-managed RuleBase is also configurable via components.xml:
<drools:rule-agent name="insuranceRules" 
        configurationFile="/WEB-INF/deployedrules.properties" />
The properties file contains properties specific to the RulesAgent. The following is an example configuration file from the Drools example distribution:
newInstance=true 
url=http://localhost:8080/drools-jbrms/org.drools.brms.JBRMS/package/
           org.acme.insurance/fmeyer 
localCacheDir=/Users/fernandomeyer/projects/jbossrules/drools-examples/ 
               drools-examples-brms/cache 
poll=30 
name=insuranceconfig
It is also possible to configure the options on the component directly, bypassing the configuration file.
<drools:rule-agent name="insuranceRules" 
        url="http://localhost:8080/drools-jbrms/org.drools.brms.JBRMS/
                    package/org.acme.insurance/fmeyer" 
        local-cache-dir="/Users/fernandomeyer/projects/jbossrules/
                          drools-examples/drools-examples-brms/cache" 
        poll="30" 
 configuration-name="insuranceconfig" />
Next, make an instance of org.drools.WorkingMemory available to each conversation. (Each WorkingMemory accumulates facts relating to the current conversation.)
<drools:managed-working-memory name="policyPricingWorkingMemory" 
        auto-create="true" rule-base="#{policyPricingRules}"/>
Notice that we referred the policyPricingWorkingMemory back to our rule base via the ruleBase configuration property.
We can also add means to be notified of rule engine events, including, for example, rules firing or objects being asserted by adding event listeners to WorkingMemory.

<drools:managed-working-memory name="policyPricingWorkingMemory" 
                               auto-create="true" 
                               rule-base="#{policyPricingRules}">
  <drools:event-listeners>
    <value>org.drools.event.DebugWorkingMemoryEventListener</value>
    <value>org.drools.event.DebugAgendaEventListener</value>
  </drools:event-listeners>
</drools:managed-working-memory>

14.2. Using rules from a Seam component

We can now inject our WorkingMemory into any Seam component, assert facts, and fire rules:
@In WorkingMemory policyPricingWorkingMemory; 
@In Policy policy; 
@In Customer customer; 

public void pricePolicy() throws FactException { 
  policyPricingWorkingMemory.insert(policy);
  policyPricingWorkingMemory.insert(customer);  
  policyPricingWorkingMemory.fireAllRules(); 
}

Chapter 15. Security

15.1. Overview

The Seam Security API provides a multitude of security-related features for your Seam-based application, including:
  • Authentication — an extensible, Java Authentication and Authorization Service (JAAS) based authentication layer that allows users to authenticate against any security provider.
  • Identity Management — an API for managing the users and roles of a Seam application at runtime.
  • Authorization — an extremely comprehensive authorization framework, supporting user roles, persistent and rule-based permissions, and a pluggable permission-resolver that makes it easy to implement customized security logic.
  • Permission Management — a set of built-in Seam components that make it easy to manage an application's security policy.
  • CAPTCHA support — to assist in the prevention of automated software/scripts abusing your Seam-based site.
This chapter covers each of these features in detail.

15.2. Disabling Security

In some situations, you may need to disable Seam Security (during unit tests, for instance, or to use a different security approach, like native JAAS). To disable the security infrastructure, call the static method Identity.setSecurityEnabled(false). However, when you want to configure the application, a more convenient alternative is to control the following settings in components.xml:
  • Entity Security
  • Hibernate Security Interceptor
  • Seam Security Interceptor
  • Page restrictions
  • Servlet API security integration
This chapter documents the vast number of options available when establishing the user's identity (authentication) and establishing access constraints (authorization). We will begin with the foundation of the security model: authentication.

15.3. Authentication

Seam Security provides Java Authentication and Authorization Service (JAAS) based authorization features, providing a robust and highly configurable API for handling user authentication. If your authentication needs are not this complex, Seam also offers a simplified authentication method.

15.3.1. Configuring an Authenticator component

Note

If you use Seam's Identity Management features, you can skip this section — it is not necessary to create an authenticator component.
Seam's simplified authentication method uses a built-in JAAS login module (SeamLoginModule) to delegate authentication to one of your own Seam components. (This module requires no additional configuration files, and comes pre-configured within Seam.) With this, you can write an authentication method with the entity classes provided by your own application, or authenticate through another third-party provider. Configuring this simplified authentication requires the identity component to be configured in components.xml

             <components xmlns="http://jboss.org/schema/seam/components" 
xmlns:core="http://jboss.org/schema/seam/core" 
xmlns:security="http://jboss.org/schema/seam/security" 
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
            xsi:schemaLocation= 
   "http://jboss.org/schema/seam/components 
   http://jboss.org/schema/seam/components-2.3.xsd 
   http://jboss.org/schema/seam/security 
    http://jboss.org/schema/seam/security-2.3.xsd">

<security:identity authenticate-method="#{authenticator.authenticate}"/>

</components>
#{authenticator.authenticate} is a method binding that indicates the authenticate method of the authenticator component will be used to authenticate the user.

15.3.2. Writing an authentication method

The authenticate-method property specified for identity in components.xml specifies the method used by SeamLoginModule to authenticate users. This method takes no parameters, and is expected to return a Boolean indicating authentication success or failure. Username and password are obtained from Credentials.getUsername() and Credentials.getPassword() respectively. (A reference to the credentials component can be obtained via Identity.instance().getCredentials().) Any role that the user is a member of should be assigned with Identity.addRole(). The following is a complete example of an authentication method inside a POJO component:
@Name("authenticator")
public class Authenticator {
  @In EntityManager entityManager;
  @In Credentials credentials;
  @In Identity identity;

  public boolean authenticate() {
    try {
      User user = (User) entityManager.createQuery(
          "from User where username = :username and password = :password")
          .setParameter("username", credentials.getUsername())
          .setParameter("password", credentials.getPassword())
          .getSingleResult();

      if (user.getRoles() != null) {
        for (UserRole mr : user.getRoles())
          identity.addRole(mr.getName());
        }

        return true;
      } catch (NoResultException ex) {
        return false;
      }

   }

}
In the example, both User and UserRole are application-specific entity beans. The roles parameter is populated with roles that the user is a member of. This is added to the Set as literal string values — for example, "admin", "user", etc. If the user record is not found, and a NoResultException is thrown, the authentication method returns false to indicate authentication failure.

Note

It is important to keep authenticator methods minimal and free from any side-effects — they can be invoked multiple times during a single request, so any special code that should execute when authentication succeeds or fails should implement an event observer. See Section 15.10, “Security Events” later in this chapter for more information about events raised by Seam Security.

15.3.2.1. Identity.addRole()

The Identity.addRole() method's behavior depends upon current session authentication. If the session is not authenticated, addRole() should only be called during the authentication process. When called here, the role name is placed in a temporary list of pre-authenticated roles. Once authentication succeeds, the pre-authenticated roles then become "real" roles, and calling Identity.hasRole() for those roles returns true. The following sequence diagram represents the list of pre-authenticated roles as a first class object to clarify its position in the authentication process.
If the current session is already authenticated, then calling Identity.addRole() grants the specified role to the current user immediately.

15.3.2.2. Writing an event observer for security-related events

If, upon successful log in, some user statistics require updates, you can write an event observer for the org.jboss.seam.security.loginSuccessful event, like this:
   
@In UserStats userStats;

@Observer("org.jboss.seam.security.loginSuccessful")
public void updateUserStats() {
  userStats.setLastLoginDate(new Date());
  userStats.incrementLoginCount();
}
This observer method can be placed anywhere, even in the Authenticator component itself. More information about other security-related events appears later in the chapter.

15.3.3. Writing a login form

The credentials component provides both username and password properties, catering for the most common authentication scenario. These properties can be bound directly to the username and password fields on a login form. Once these properties are set, calling identity.login() authenticates the user with the credentials provided. An example of a simple login form is as follows:
<div>
  <h:outputLabel for="name" value="Username"/>
  <h:inputText id="name" value="#{credentials.username}"/>
</div>

<div>
  <h:outputLabel for="password" value="Password"/>
  <h:inputSecret id="password" value="#{credentials.password}"/>
</div>

<div>
  <h:commandButton value="Login" action="#{identity.login}"/>
</div>
Similarly, the user is logged out by calling #{identity.logout}. This action clears the security state of the currently authenticated user and invalidate the user's session.

15.3.4. Configuration Summary

There are three easy steps to configure authentication:
  • Configure an authentication method in components.xml.
  • Write an authentication method.
  • Write a login form so that the user can authenticate.

15.3.5. Remember Me

Seam Security supports two different modes of the Remember Me functionality common to many web-based applications. The first mode allows the username to be stored in the user's browser as a cookie, and leaves the browser to remember the password. The second mode stores a unique token in a cookie, and lets a user authenticate automatically when they return to the site, without having to provide a password.

Warning

Although it is convenient for users, automatic client authentication through a persistent cookie on the client machine is dangerous because the effects of any cross-site scripting (XSS) security hole are magnified. Without the authentication cookie, the only cookie an attacker can steal with XSS is the user's current session cookie — so an attack can only occur while a user has a session open. If a persistent Remember Me cookie is stolen, an attacker can log in without authentication at any time. If you wish to use automatic client authentication, it is vital to protect your website against XSS attacks.
Browser vendors introduced the Remember Passwords feature to combat this issue. Here, the browser remembers the username and password used to log in to a particular website and domain, and automatically fills in the login form when there is no session active. A log in keyboard shortcut on your website can make the log in process almost as convenient as the "Remember Me" cookie, and much safer. Some browsers (for example, Safari on OS X) store the login form data in the encrypted global operation system keychain. In a networked environment, the keychain can be transported with the user between laptop and desktop — cookies are not usually synchronized.
Although persistent Remember Me cookies with automatic authentication are widely used, they are bad security practice. Cookies that recall only the user's login name, and fill out the login form with that username as a convenience, are much more secure.
No special configuration is required to enable the Remember Me feature for the default (safe, username-only) mode. In your login form, simply bind the Remember Me checkbox to rememberMe.enabled, as seen in the following example:
<div>
  <h:outputLabel for="name" value="User name"/>
  <h:inputText id="name" value="#{credentials.username}"/>
</div>
  
<div>
  <h:outputLabel for="password" value="Password"/>
  <h:inputSecret id="password" value="#{credentials.password}" redisplay="true"/>
</div>      
  
<div class="loginRow">
  <h:outputLabel for="rememberMe" value="Remember me"/>
  <h:selectBooleanCheckbox id="rememberMe" value="#{rememberMe.enabled}"/>
</div>

15.3.5.1. Token-based Remember Me Authentication

To use the automatic, token-based mode of the Remember Me feature, you must first configure a token store. These authentication tokens are commonly stored within a database. Seam supports this method, but you can also implement your own token store by using the org.jboss.seam.security.TokenStore interface. This section assumes that you will be using the provided JpaTokenStore implementation to store authentication tokens inside a database table.
First, create a new Entity to hold the tokens. The following is one possible structure:
@Entity
public class AuthenticationToken implements Serializable {  
  private Integer tokenId;
  private String username;
  private String value;
   
  @Id @GeneratedValue
  public Integer getTokenId() {
    return tokenId;
  }
   
  public void setTokenId(Integer tokenId) {
     this.tokenId = tokenId;
  }
   
  @TokenUsername
  public String getUsername() {
     return username;
  }
   
  public void setUsername(String username) {
    this.username = username;
  }
   
  @TokenValue
  public String getValue() {
    return value;
  }
   
  public void setValue(String value) {
    this.value = value;
  }
}
Several special annotations, @TokenUsername and @TokenValue, are used to configure the username and token properties of the entity. These annotations are required for the entity that holds the authentication tokens.
The next step is to configure JpaTokenStore to store and retrieve authentication tokens with this entity bean. Do this by specifying the token-class attribute in components.xml:
 
<security:jpa-token-store 
     token-class="org.jboss.seam.example.seamspace.AuthenticationToken"/>
The final step is to configure the RememberMe component in components.xml. Its mode should be set to autoLogin:
  
<security:remember-me mode="autoLogin"/>
Users who check the Remember Me checkbox will now be authenticated automatically.
To ensure that users are automatically authenticated when returning to the site, the following section should be placed in components.xml:
<event type="org.jboss.seam.security.notLoggedIn">
  <action execute="#{redirect.captureCurrentView}"/>
  <action execute="#{identity.tryLogin()}"/>
</event>
<event type="org.jboss.seam.security.loginSuccessful">
  <action execute="#{redirect.returnToCapturedView}"/>
</event>

15.3.6. Handling Security Exceptions

So that users do not receive a basic default error page when a security error occurs, you should edit pages.xml to redirect users to a more attractive page. The two main exceptions thrown by the security API are:
  • NotLoggedInException — This exception is thrown when the user attempts to access a restricted action or page when they are not logged in.
  • AuthorizationException — This exception is only thrown if the user is already logged in, and they have attempted to access a restricted action or page for which they do not have the necessary privileges.
In the case of a NotLoggedInException, we recommend the user be redirected to a login or registration page so that they can log in. For an AuthorizationException, it may be useful to redirect the user to an error page. Here's an example of a pages.xml file that redirects both of these security exceptions:
<pages>

  ...

  <exception class="org.jboss.seam.security.NotLoggedInException">
    <redirect view-id="/login.xhtml">
      <message>You must be logged in to perform this action</message>
    </redirect>
  </exception>

  <exception class="org.jboss.seam.security.AuthorizationException">
    <end-conversation/>
      <redirect view-id="/security_error.xhtml">
        <message>
          You do not have the necessary security privileges to perform this action.
        </message>
      </redirect>
  </exception>

</pages>
Most web applications require more sophisticated handling of login redirection. Seam includes some special functionality, outlined in the following section.

15.3.7. Login Redirection

When an unauthenticated user tries to access a particular view or wildcarded view ID, you can have Seam redirect the user to a login screen as follows:
<pages login-view-id="/login.xhtml">

  <page view-id="/members/*" login-required="true"/> 
... 
</pages>

Note

This is more refined than the exception handler shown above, but should probably be used in conjunction with it.
After the user logs in, we want to automatically redirect them to the action that required log in. If you add the following event listeners to components.xml, attempts to access a restricted view while not logged in are remembered. Upon a successful log in, the user is redirected to the originally requested view, with any page parameters that existed in the original request.
<event type="org.jboss.seam.security.notLoggedIn">
  <action execute="#{redirect.captureCurrentView}"/>
</event>

<event type="org.jboss.seam.security.postAuthenticate">
  <action execute="#{redirect.returnToCapturedView}"/>
</event>

Note

Login redirection is implemented as a conversation-scoped mechanism, so do not end the conversation in your authenticate() method.

15.3.8. HTTP Authentication

Although we do not recommend it unless absolutely necessary, Seam provides the means to authenticate with either HTTP Basic or HTTP Digest (RFC 2617) methods. For either form, you must first enable the authentication-filter component in components.xml:
<web:authentication-filter url-pattern="*.seam" auth-type="basic"/> 
To enable basic authentication, set auth-type to basic. For digest authentication, set it to digest. If you want to use digest authentication, you must also set the key and realm:
<web:authentication-filter url-pattern="*.seam" auth-type="digest" 
     key="AA3JK34aSDlkj" realm="My App"/> 
The key can be any String value. The realm is the name of the authentication realm that is presented to the user when they authenticate.

15.3.8.1. Writing a Digest Authenticator

If using digest authentication, your authenticator class should extend the abstract class org.jboss.seam.security.digest.DigestAuthenticator, and use the validatePassword() method to validate the user's plain text password against the digest request. Here is an example:
public boolean authenticate() {
  try {
    User user = (User) entityManager.createQuery(
         "from User where username = "username")
         .setParameter("username", identity.getUsername())
         .getSingleResult();

    return validatePassword(user.getPassword());
  } catch (NoResultException ex) {
    return false;
  }
}

15.3.9. Advanced Authentication Features

This section explores some of the advanced features provided by the security API for addressing more complex security requirements.

15.3.9.1. Using your container's JAAS configuration

If you prefer not to use the simplified JAAS configuration provided by the Seam Security API, you can use the default system JAAS configuration by adding a jaas-config-name property to components.xml. For example, if you use Red Hat JBoss Enterprise Application Platform and want to use the other policy (which uses the UsersRolesLoginModule login module provided by JBoss Enterprise Application Platform), then the entry in components.xml would look like this:
<security:identity jaas-config-name="other"/>
Keep in mind that doing this does not mean that your user will be authenticated in your Seam application container — it instructs Seam Security to authenticate itself with the configured JAAS security policy.

15.4. Identity Management

Identity Management provides a standard API for managing a Seam application's users and roles, regardless of the identity store (database, LDAP, etc.) used in back-end operations. The identityManager component is at the core of the Identity Management API, and provides all methods for creating, modifying, and deleting users, granting and revoking roles, changing passwords, enabling and disabling user accounts, authenticating users, and listing users and roles.
Before use, the identityManager must be configured with at least one IdentityStore. These components interact with the back-end security provider.

15.4.1. Configuring IdentityManager

The identityManager component allows you to configure separate identity stores for authentication and authorization. This means that users can be authenticated against one identity store (for example, an LDAP directory), but have their roles loaded from another identity store (such as a relational database).
Seam provides two IdentityStore implementations out of the box. The default, JpaIdentityStore, uses a relational database to store user and role information. The other implementation is LdapIdentityStore, which uses an LDAP directory to store users and roles.
The identityManager component has two configurable properties: identityStore and roleIndentityStore. The value for these properties must be an EL expression that refers to a Seam component with the IdentityStore interface. If left unconfigured, the default (JpaIdentityStore) will be used. If only the identityStore property is configured, the same value will be used for roleIdentityStore. For example, the following entry in components.xml will configure identityManager to use an LdapIdentityStore for both user-related and role-related operations:
<security:identity-manager identity-store="#{ldapIdentityStore}"/>
The following example configures identityManager to use an LdapIdentityStore for user-related operations, and JpaIdentityStore for role-related operations:
<security:identity-manager identity-store="#{ldapIdentityStore}" 
          role-identity-store="#{jpaIdentityStore}"/>
The following sections explain each identity storage method in greater detail.

15.4.2. JpaIdentityStore

This method stores users and roles in a relational database. It is designed to allow flexible database design and table structure. A set of special annotations lets entity beans store user and role records.

15.4.2.1. Configuring JpaIdentityStore

Both user-class and role-class must be configured before JpaIdentityStore can be used. These properties refer to the entity classes used to store user and role records, respectively. The following example shows the components.xml file from the SeamSpace example:
 
<security:jpa-identity-store 
          user-class="org.jboss.seam.example.seamspace.MemberAccount" 
          role-class="org.jboss.seam.example.seamspace.MemberRole"/> 

15.4.2.2. Configuring the Entities

The following table describes the special annotations used to configure entity beans for user and role storage.

Table 15.1. User Entity Annotations

Annotation
Status
Description
@UserPrincipal
Required
This annotation marks the field or method containing the user's username.
@UserPassword
Required
This annotation marks the field or method containing the user's password. It allows a hash algorithm to be specified for password hashing. Possible values for hash are md5, sha and none. For example:
@UserPassword(hash = "md5") 
public String getPasswordHash() { 
  return passwordHash; 
}
It is possible to extend the PasswordHash component to implement other hashing algorithms, if required.
@UserFirstName
Optional
This annotation marks the field or method containing the user's first name.
@UserLastName
Optional
This annotation marks the field or method containing the user's last name.
@UserEnabled
Optional
This annotation marks the field or method containing the enabled user status. This should be a Boolean property. If not present, all user accounts are assumed to be enabled.
@UserRoles
Required
This annotation marks the field or method containing the roles of the user. This property will be described in more detail in a later section.

Table 15.2. Role Entity Annotations

Annotation
Status
Description
@RoleName
Required
This annotation marks the field or method containing the name of the role.
@RoleGroups
Optional
This annotation marks the field or method containing the group memberships of the role.
@RoleConditional
Optional
This annotation marks the field or method that indicates whether a role is conditional. Conditional roles are explained later in this chapter.

15.4.2.3. Entity Bean Examples

As mentioned previously, JpaIdentityStore is designed to be as flexible as possible when it comes to the database schema design of your user and role tables. This section looks at a number of possible database schemas that can be used to store user and role records.
15.4.2.3.1. Minimal schema example
Here, a simple user and role table are linked via a many-to-many relationship using a cross-reference table named UserRoles.
@Entity
public class User {
  private Integer userId;
  private String username;
  private String passwordHash;
  private Set<Role> roles;
  
  @Id @GeneratedValue
  public Integer getUserId() { return userId; }
  public void setUserId(Integer userId) { this.userId = userId; }
  
  @UserPrincipal
  public String getUsername() { return username; }
  public void setUsername(String username) { this.username = username; }
  
  @UserPassword(hash = "md5")
  public String getPasswordHash() { return passwordHash; }
  public void setPasswordHash(String passwordHash) { 
    this.passwordHash = passwordHash; 
  }
  
  @UserRoles
  @ManyToMany(targetEntity = Role.class)
  @JoinTable(name = "UserRoles", 
       joinColumns = @JoinColumn(name = "UserId"),
       inverseJoinColumns = @JoinColumn(name = "RoleId"))
  public Set<Role> getRoles() { return roles; }
  public void setRoles(Set<Role> roles) { this.roles = roles; }
}
@Entity
public class Role {
  private Integer roleId;
  private String rolename;
  
  @Id @Generated
  public Integer getRoleId() { return roleId; }
  public void setRoleId(Integer roleId) { this.roleId = roleId; }
  
  @RoleName
  public String getRolename() { return rolename; }
  public void setRolename(String rolename) { this.rolename = rolename; }
}
15.4.2.3.2. Complex Schema Example
This example builds on the previous minimal example by including all optional fields, and allowing group memberships for roles.
@Entity
public class User {
  private Integer userId;
  private String username;
  private String passwordHash;
  private Set<Role> roles;
  private String firstname;
  private String lastname;
  private boolean enabled;
  
  @Id @GeneratedValue
  public Integer getUserId() { return userId; }
  public void setUserId(Integer userId) { this.userId = userId; }
  
  @UserPrincipal
  public String getUsername() { return username; }
  public void setUsername(String username) { this.username = username; }
  
  @UserPassword(hash = "md5")
  public String getPasswordHash() { return passwordHash; }
  public void setPasswordHash(String passwordHash) { 
    this.passwordHash = passwordHash; 
  }
  
  @UserFirstName
  public String getFirstname() { return firstname; }
  public void setFirstname(String firstname) { 
    this.firstname = firstname; 
  }
  
  @UserLastName
  public String getLastname() { return lastname; }
  public void setLastname(String lastname) { this.lastname = lastname; }
  
  @UserEnabled
  public boolean isEnabled() { return enabled; }
  public void setEnabled(boolean enabled) { this.enabled = enabled; }
  
  @UserRoles
  @ManyToMany(targetEntity = Role.class)
  @JoinTable(name = "UserRoles", 
    joinColumns = @JoinColumn(name = "UserId"),
    inverseJoinColumns = @JoinColumn(name = "RoleId"))
  public Set<Role> getRoles() { return roles; }
  public void setRoles(Set<Role> roles) { this.roles = roles; }
}
@Entity
public class Role {
  private Integer roleId;
  private String rolename;
  private boolean conditional;
  
  @Id @Generated
  public Integer getRoleId() { return roleId; }
  public void setRoleId(Integer roleId) { this.roleId = roleId; }
  
  @RoleName
  public String getRolename() { return rolename; }
  public void setRolename(String rolename) { this.rolename = rolename; }
  
  @RoleConditional
  public boolean isConditional() { return conditional; }
  public void setConditional(boolean conditional) { 
    this.conditional = conditional; 
  }
  
  @RoleGroups
  @ManyToMany(targetEntity = Role.class)
  @JoinTable(name = "RoleGroups", 
             joinColumns = @JoinColumn(name = "RoleId"),
             inverseJoinColumns = @JoinColumn(name = "GroupId"))
  public Set<Role> getGroups() { return groups; }
  public void setGroups(Set<Role> groups) { this.groups = groups; }  
  
}

15.4.2.4. JpaIdentityStore Events

When using JpaIdentityStore with IdentityManager, several events are raised when certain IdentityManager methods are invoked.
15.4.2.4.1. JpaIdentityStore.EVENT_PRE_PERSIST_USER
This event is raised in response to calling IdentityManager.createUser(). Just before the user entity is persisted to the database, this event is raised to pass the entity instance as an event parameter. The entity will be an instance of the user-class configured for JpaIdentityStore.
An observer can be useful, here, for setting entity field values that are not part of standard createUser() functionality.
15.4.2.4.2. JpaIdentityStore.EVENT_USER_CREATED
This event is also raised in response to calling IdentityManager.createUser(). However, it is raised after the user entity has already been persisted to the database. Like the EVENT_PRE_PERSIST_USER event, it also passes the entity instance as an event parameter. It may be useful to observe this event if you need to persist other entities that reference the user entity, such as contact detail records or other user-specific data.
15.4.2.4.3. JpaIdentityStore.EVENT_USER_AUTHENTICATED
This event is raised when calling IdentityManager.authenticate(). It passes the user entity instance as the event parameter, and is useful for reading additional properties from the user entity being authenticated.

15.4.3. LdapIdentityStore

This identity storage method is designed to work with user records stored in an LDAP directory. It is highly configurable, and allows very flexible directory storage of both users and roles. The following sections describe the configuration options for this identity store, and provide some configuration examples.

15.4.3.1. Configuring LdapIdentityStore

The following table describes the properties that can be configured in components.xml for LdapIdentityStore.

Table 15.3. LdapIdentityStore Configuration Properties

Property
Default Value
Description
server-address
localhost
The address of the LDAP server.
server-port
389
The port number that the LDAP server listens on.
user-context-DN
ou=Person,dc=acme,dc=com
The Distinguished Name (DN) of the context containing user records.
user-DN-prefix
uid=
This value is prefixed to the front of the username to locate the user's record.
user-DN-suffix
,ou=Person,dc=acme,dc=com
This value is appended to the end of the username to locate the user's record.
role-context-DN
ou=Role,dc=acme,dc=com
The DN of the context containing role records.
role-DN-prefix
cn=
This value is prefixed to the front of the role name to form the DN that locates the role record.
role-DN-suffix
,ou=Roles,dc=acme,dc=com
This value is appended to the role name to form the DN that locates the role record.
bind-DN
cn=Manager,dc=acme,dc=com
This is the context used to bind to the LDAP server.
bind-credentials
secret
These are the credentials (the password) used to bind to the LDAP server.
user-role-attribute
roles
The attribute name of the user record containing the list of roles that the user is a member of.
role-attribute-is-DN
true
This Boolean property indicates whether the role attribute of the user record is itself a distinguished name.
user-name-attribute
uid
Indicates the user record attribute containing the username.
user-password-attribute
userPassword
Indicates the user record attribute containing the user's password.
first-name-attribute
null
Indicates the user record attribute containing the user's first name.
last-name-attribute
sn
Indicates the user record attribute containing the user's last name.
full-name-attribute
cn
Indicates the user record attribute containing the user's full (common) name.
enabled-attribute
null
Indicates the user record attribute that determines whether the user is enabled.
role-name-attribute
cn
Indicates the role record attribute containing the name of the role.
object-class-attribute
objectClass
Indicates the attribute that determines the class of an object in the directory.
role-object-classes
organizationalRole
An array of the object classes that new role records should be created as.
user-object-classes
person,uidObject
An array of the object classes that new user records should be created as.

15.4.3.2. LdapIdentityStore Configuration Example

The following configuration example shows how LdapIdentityStore can be configured for an LDAP directory running on fictional host directory.mycompany.com. The users are stored within this directory under the ou=Person,dc=mycompany,dc=com context, and are identified by the uid attribute (which corresponds to their username). Roles are stored in their own context, ou=Roles,dc=mycompany,dc=com, and are referenced from the user's entry via the roles attribute. Role entries are identified by their common name (the cn attribute), which corresponds to the role name. In this example, users can be disabled by setting the value of their enabled attribute to false.
 
<security:ldap-identity-store
  server-address="directory.mycompany.com"
  bind-DN="cn=Manager,dc=mycompany,dc=com"
  bind-credentials="secret"
  user-DN-prefix="uid="
  user-DN-suffix=",ou=Person,dc=mycompany,dc=com"
  role-DN-prefix="cn="
  role-DN-suffix=",ou=Roles,dc=mycompany,dc=com"
  user-context-DN="ou=Person,dc=mycompany,dc=com"
  role-context-DN="ou=Roles,dc=mycompany,dc=com"
  user-role-attribute="roles"
  role-name-attribute="cn"
  user-object-classes="person,uidObject"
  enabled-attribute="enabled"
/>

15.4.4. Writing your own IdentityStore

Writing your own identity store implementation allows you to authenticate and perform identity management operations against security providers that are not supported out of the box by Seam. You only need a single class that implements the org.jboss.seam.security.management.IdentityStore interface to achieve this.
Refer to the JavaDoc about IdentityStore for a description of the methods that must be implemented.

15.4.5. Authentication with Identity Management

If you use Identity Management features in your Seam application, then you do not need to provide an authenticator component (see previous Authentication section) to enable authentication. Simply omit the authenticator-method from the identity configuration in components.xml, and the SeamLoginModule will use IdentityManager to authenticate your application's users without any special configuration.

15.4.6. Using IdentityManager

Access the IdentityManager either by injecting it into your Seam component, like so:

@In IdentityManager identityManager;
or, through its static instance() method:

IdentityManager identityManager = IdentityManager.instance();
The following table describes IdentityManager's API methods:

Table 15.4. Identity Management API

Method
Returns
Description
createUser(String name, String password)
boolean
Creates a new user account, with the specified name and password. Returns true if successful; otherwise, returns false.
deleteUser(String name)
boolean
Deletes the user account with the specified name. Returns true if successful; otherwise, returns false.
createRole(String role)
boolean
Creates a new role, with the specified name. Returns true if successful; otherwise, returns false.
deleteRole(String name)
boolean
Deletes the role with the specified name. Returns true if successful; otherwise, returns false.
enableUser(String name)
boolean
Enables the user account with the specified name. Accounts that are not enabled cannot authenticate. Returns true if successful; otherwise, returns false.
disableUser(String name)
boolean
Disables the user account with the specified name. Returns true if successful; otherwise, returns false.
changePassword(String name, String password)
boolean
Changes the password for the user account with the specified name. Returns true if successful; otherwise, returns false.
isUserEnabled(String name)
boolean
Returns true if the specified user account is enabled; otherwise, returns false.
grantRole(String name, String role)
boolean
Grants the specified role to the specified user or role. The role must already exist for it to be granted. Returns true if the role is successfully granted, or false if the user has already been granted the role.
revokeRole(String name, String role)
boolean
Revokes the specified role from the specified user or role. Returns true if the specified user is a member of the role and it is successfully revoked, or false if the user is not a member of the role.
userExists(String name)
boolean
Returns true if the specified user exists, or false if it does not.
listUsers()
List
Returns a list of all user names, sorted in alpha-numeric order.
listUsers(String filter)
List
Returns a list of all user names filtered by the specified filter parameter, sorted in alpha-numeric order.
listRoles()
List
Returns a list of all role names.
getGrantedRoles(String name)
List
Returns a list of all roles explicitly granted to the specified user name.
getImpliedRoles(String name)
List
Returns a list of all roles implicitly granted to the specified user name. Implicitly granted roles include those that are granted to the roles that the user is a member of, rather than granted directly to the user. For example, if the admin role is a member of the user role, and a user is a member of the admin role, then the implied roles for the user are both the admin, and user roles.
authenticate(String name, String password)
boolean
Authenticates the specified username and password using the configured Identity Store. Returns true if successful or false if authentication failed. Successful authentication implies nothing beyond the return value of the method. It does not change the state of the Identity component - to perform a proper Seam log in the Identity.login() must be used instead.
addRoleToGroup(String role, String group)
boolean
Adds the specified role as a member of the specified group. Returns true if the operation is successful.
removeRoleFromGroup(String role, String group)
boolean
Removes the specified role from the specified group. Returns true if the operation is successful.
listRoles()
List
Lists the names of all roles.
A calling user must have appropriate authorization to invoke methods on the Identity Management API. The following table describes the permission requirements for each of the methods in IdentityManager. The permission targets listed below are literal String values.

Table 15.5. Identity Management Security Permissions

Method
Permission Target
Permission Action
createUser()
seam.user
create
deleteUser()
seam.user
delete
createRole()
seam.role
create
deleteRole()
seam.role
delete
enableUser()
seam.user
update
disableUser()
seam.user
update
changePassword()
seam.user
update
isUserEnabled()
seam.user
read
grantRole()
seam.user
update
revokeRole()
seam.user
update
userExists()
seam.user
read
listUsers()
seam.user
read
listRoles()
seam.role
read
addRoleToGroup()
seam.role
update
removeRoleFromGroup()
seam.role
update
The following code listing provides an example set of security rules that grants all admin role members access to all Identity Management-related methods:
rule ManageUsers
  no-loop
  activation-group "permissions"
when
  check: PermissionCheck(name == "seam.user", granted == false)
  Role(name == "admin")
then
  check.grant();
end

rule ManageRoles
  no-loop
  activation-group "permissions"
when
  check: PermissionCheck(name == "seam.role", granted == false)
  Role(name == "admin")
then
  check.grant();
end

15.5. Error Messages

The security API produces a number of default Faces messages for various security-related events. The following table lists the message keys to specify in a message.properties resource file if you want to override the messages. To suppress a message, add the key (with an empty value) to the resource file.

Table 15.6. Security Message Keys

Message Key
Description
org.jboss.seam.loginSuccessful
This message is produced when a user successfully logs in via the security API.
org.jboss.seam.loginFailed
This message is produced when the log in process fails, either because the user provided an incorrect username or password, or because authentication failed in some other way.
org.jboss.seam.NotLoggedIn
This message is produced when a user attempts to perform an action or access a page that requires a security check, and the user is not currently authenticated.
org.jboss.seam.AlreadyLoggedIn
This message is produced when a user that is already authenticated attempts to log in again.

15.6. Authorization

This section describes the range of authorization mechanisms provided by the Seam Security API for securing access to components, component methods, and pages. If you wish to use any of the advanced features (for example, rule-based permissions), you may need to configure your components.xml file — see the Configuration section previous.

15.6.1. Core concepts

Seam Security operates on the principle that users are granted roles or permissions, or both, which allow them to perform operations that are not permissible for users without the required security privileges. Each authorization mechanism provided by the Seam Security API is built upon this core concept of roles and permissions, with an extensible framework to provide multiple ways to secure application resources.

15.6.1.1. What is a role?

A role is a type of user that may have been granted certain privileges for performing one or more specific actions within an application. They are simple constructs, consisting of a name (such as "admin", "user", "customer", etc.) applied to users, or other roles. They are used to create logical user groups so that specific application privileges can be easily assigned.

15.6.1.2. What is a permission?

A permission is a privilege (sometimes once-off) for performing a single, specific action. You can build an application that operates solely on permissions, but roles are more convenient when granting privileges to groups. Permissions are slightly more complex in structure than roles, consisting of three "aspects"; a target, an action, and a recipient. The target of a permission is the object (or an arbitrary name or class) for which a particular action is allowed to be performed by a specific recipient (or user). For example, the user "Bob" may have permission to delete customer objects. In this case, the permission target may be "customer", the permission action would be "delete" and the recipient would be "Bob".
In this documentation, permissions are usually represented in the form target:action, omitting the recipient. In reality, a recipient is always required.

15.6.2. Securing components

We will start with the simplest form of authorization: component security. First, we will look at the @Restrict annotation.

@Restrict vs Typesafe security annotations

While the @Restrict annotation is a powerful and flexible method for security components, it cannot support EL expressions. Therefore, we recommend using the typesafe equivalent (described later in this chapter) for its compile-time safety.

15.6.2.1. The @Restrict annotation

Seam components can be secured at either the method or the class level with the @Restrict annotation. If both a method and its declaring class are annotated with @Restrict, the method restriction will take precedence and the class restriction will not apply. If a method invocation fails a security check, an exception will be thrown as per the contract for Identity.checkRestriction(). (See the section following for more information on Inline Restrictions.) Placing @Restrict on the component class itself is the equivalent of adding @Restrict to each of its methods.
An empty @Restrict implies a permission check of componentName:methodName. Take for example the following component method:
@Name("account")
public class AccountAction {
  @Restrict 
  public void delete() {
    ...
  }
}
In this example, account:delete is the implied permission required to call the delete() method. This is equivalent to writing @Restrict("#{s:hasPermission('account','delete')}"). The following is another example:
@Restrict @Name("account")
public class AccountAction {
  public void insert() {
    ...
  }
  @Restrict("#{s:hasRole('admin')}")
  public void delete() {
    ...
  }
}
Here, the component class itself is annotated with @Restrict. This means that any methods without an overriding @Restrict annotation require an implicit permission check. In this case, the insert() method requires a permission of account:insert, while the delete() method requires that the user is a member of the admin role.
Before we go further, we will address the #{s:hasRole()} expression seen in the previous example. s:hasRole and s:hasPermission are EL functions that delegate to the correspondingly-named methods of the Identity class. These functions can be used within any EL expression throughout the entirety of the security API.
Being an EL expression, the value of the @Restrict annotation may refer to any object within a Seam context. This is extremely useful when checking permissions for a specific object instance. Take the following example:
@Name("account")
public class AccountAction {
  @In Account selectedAccount;
  @Restrict("#{s:hasPermission(selectedAccount,'modify')}")
  public void modify() {
    selectedAccount.modify();
  }
}
In this example, the hasPermission() function call refers to selectedAccount. The value of this variable will be looked up from within the Seam context, and passed to the hasPermission() method in Identity. This will determine whether the user has the required permissions to modify the specified Account object.

15.6.2.2. Inline restrictions

It is sometimes necessary to perform a security check in code, without using the @Restrict annotation. To do so, use Identity.checkRestriction() to evaluate a security expression, like this:
public void deleteCustomer() { 
  Identity.instance().checkRestriction("#{s:hasPermission(selectedCustomer,
                                                          'delete')}"); 
}
If the specified expression does not evaluate to true, one of two exceptions occurs. If the user is not logged in, a NotLoggedInException is thrown. If the user is logged in, an AuthorizationException is thrown.
You can also call the hasRole() and hasPermission() methods directly from Java code:
if (!Identity.instance().hasRole("admin"))
  throw new AuthorizationException("Must be admin to perform this action");

if (!Identity.instance().hasPermission("customer", "create"))
     throw new AuthorizationException("You may not create new customers");

15.6.3. Security in the user interface

A well-designed interface does not present a user with options they are not permitted to use. Seam Security allows conditional rendering of page sections or individual controls based on user privileges, using the same EL expressions that are used for component security.
In this section, we will go through some examples of interface security. Say we have a login form that we want rendered only if the user is not already logged in. We can write the following with the identity.isLoggedIn() property:
<h:form class="loginForm" rendered="#{not identity.loggedIn}">
If the user is not logged in, the login form will be rendered — very straightforward. Say we also have a menu on this page, and we want some actions to be accessed only by users in the manager role. One way you could write this is the following:
<h:outputLink action="#{reports.listManagerReports}" 
   rendered="#{s:hasRole('manager')}"> Manager Reports 
</h:outputLink>
This, too, is straightforward — if the user is not a member of the manager role, the outputLink will not be rendered. The rendered attribute can generally be used on the control itself, or on a surrounding <s:div> or <s:span> control.
A more complex example of conditional rendering might be the following situation: say you have a h:dataTable control on a page, and you want to render action links on its records only for users with certain privileges. The s:hasPermission EL function lets us use an object parameter to determine whether the user has the necessary permission for that object. A dataTable with secured links might look like this:
<h:dataTable value="#{clients}" var="cl">
  <h:column>
    <f:facet name="header">Name</f:facet>
    #{cl.name}
  </h:column>
  <h:column>
    <f:facet name="header">City</f:facet>
    #{cl.city}
  </h:column>
  <h:column>
    <f:facet name="header">Action</f:facet>
    <s:link value="Modify Client" action="#{clientAction.modify}"
            rendered="#{s:hasPermission(cl,'modify')"/>
    <s:link value="Delete Client" action="#{clientAction.delete}"
            rendered="#{s:hasPermission(cl,'delete')"/>
  </h:column>
</h:dataTable>

15.6.4. Securing pages

To use page security, you will need a pages.xml file. Page security is easy to configure: simply include a <restrict/> element in the page elements that you want to secure. If no explicit restriction is specified in the restrict element, access via a non-Faces (GET) request requires an implied /viewId.xhtml:render permission, and /viewId.xhtml:restore permission is required when any JSF postback (form submission) originates from the page. Otherwise, the specified restriction will be evaluated as a standard security expression. Some examples are:
<page view-id="/settings.xhtml"> 
  <restrict/> 
</page>
This page requires an implied permission of /settings.xhtml:render for non-Faces requests, and an implied permission of /settings.xhtml:restore for Faces requests.
<page view-id="/reports.xhtml"> 
  <restrict>#{s:hasRole('admin')}</restrict> 
</page>
Both Faces and non-Faces requests to this page require that the user is a member of the admin role.

15.6.5. Securing Entities

Seam Security also lets you apply security restrictions to certain actions (read, insert, update, and delete) for entities.
To secure all actions for an entity class, add a @Restrict annotation on the class itself:
@Entity
@Name("customer")
@Restrict
public class Customer {
  ...
}
If no expression is specified in the @Restrict annotation, the default action is a permission check of entity:action, where the permission target is the entity instance, and the action is either read, insert, update or delete.
You can also restrict certain actions by placing a @Restrict annotation on the relevant entity life cycle method (annotated as follows):
  • @PostLoad — Called after an entity instance is loaded from the database. Use this method to configure a read permission.
  • @PrePersist — Called before a new instance of the entity is inserted. Use this method to configure an insert permission.
  • @PreUpdate — Called before an entity is updated. Use this method to configure an update permission.
  • @PreRemove — Called before an entity is deleted. Use this method to configure a delete permission.
The following example shows how an entity can be configured to perform a security check for insert operations. Note that the method need not perform any action, but it must be annotated correctly.
@PrePersist 
@Restrict 
public void prePersist() {}

Using /META-INF/orm.xml

You can also specify the callback method in /META-INF/orm.xml:
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="
          http://java.sun.com/xml/ns/persistence/orm 
          http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
          version="1.0">

  <entity class="Customer">
    <pre-persist method-name="prePersist" />
  </entity>

</entity-mappings>
You will still need to annotate the prePersist() method on Customer with @Restrict.
The following configuration is based on the Seamspace example, and checks if the authenticated user has permission to insert a new MemberBlog record. The entity being checked is automatically inserted into the working memory (in this case, MemberBlog):
rule InsertMemberBlog
  no-loop
  activation-group "permissions"
when
  principal: Principal()
  memberBlog: MemberBlog(member : member -> 
                        (member.getUsername().equals(principal.getName())))
  check: PermissionCheck(target == memberBlog, 
                         action == "insert", granted == false)
then
  check.grant();
end;
This rule grants the permission memberBlog:insert if the name of the currently authenticated user (indicated by the Principal fact) matches that of the member for whom the blog entry is being created. The principal: Principal() structure is a variable binding. It binds the instance of the Principal object placed in the working memory during authentication, and assigns it to a variable called principal. Variable bindings let the variable be referenced in other places, such as the following line, which compares the member name to the Principal name. For further details, refer to the JBoss Rules documentation.
Finally, install a listener class to integrate Seam Security with your JPA provider.

15.6.5.1. Entity security with JPA

Security checks for EJB3 entity beans are performed with an EntityListener. Install this listener with the following META-INF/orm.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="
                   http://java.sun.com/xml/ns/persistence/orm 
                   http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
                 version="1.0">

  <persistence-unit-metadata>
    <persistence-unit-defaults>
      <entity-listeners>
        <entity-listener 
                class="org.jboss.seam.security.EntitySecurityListener"/>
      </entity-listeners>
    </persistence-unit-defaults>
  </persistence-unit-metadata>

</entity-mappings>

15.6.5.2. Entity security with a Managed Hibernate Session

If you use a Hibernate SessionFactory configured with Seam, and use annotations or orm.xml, you do not need to make any changes to use entity security.

15.6.6. Typesafe Permission Annotations

Seam provides a number of alternative annotations to @Restrict. These support arbitrary EL expressions differently, which gives them additional compile-time safety.
Seam comes with a set of annotations for standard CRUD-based permissions. The following annotations are provided in the org.jboss.seam.annotations.security package:
  • @Insert
  • @Read
  • @Update
  • @Delete
To use these annotations, place them on the method or parameter for which you wish to perform a security check. When placed on a method, they specify a target class for which the permission will be checked. Take the following example:

@Insert(Customer.class) 
public void createCustomer() { ... }
Here, a permission check will be performed for the user to ensure that they have permission to create new Customer objects. The target of the permission check is Customer.class (the actual java.lang.Class instance itself), and the action is the lower case representation of the annotation name, which in this example is insert.
You can annotate a component method's parameters in the same way, as follows. If you do this, you need not specify a permission target, since the parameter value itself will be the target of the permission check.

public void updateCustomer(@Update Customer customer) { 
  ... 
}
To create your own security annotation, just annotate it with @PermissionCheck. For example:
@Target({METHOD, PARAMETER})
@Documented
@Retention(RUNTIME)
@Inherited
@PermissionCheck
public @interface Promote {
  Class value() default void.class;
}
If you wish to override the default permission action name (the lower case version of the annotation name) with another value, you can specify this within the @PermissionCheck annotation:
@PermissionCheck("upgrade")

15.6.7. Typesafe Role Annotations

In addition to typsesafe permission annotation support, Seam Security provides typesafe role annotations that let you restrict access to component methods based on the role memberships of the currently authenticated user. Seam provides one such annotation (org.jboss.seam.annotations.security.Admin) out of the box. This restricts access of a particular method to users that belong to the admin role, as long as such a role is supported by your application. To create your own role annotations, meta-annotate them with org.jboss.seam.annotations.security.RoleCheck as in the following example:
@Target({METHOD})
@Documented
@Retention(RUNTIME)
@Inherited
@RoleCheck
public @interface User { }
Any methods subsequently annotated with the @User annotation will be automatically intercepted. The user will be checked for membership of the corresponding role name (the lower case version of the annotation name, in this case user).

15.6.8. The Permission Authorization Model

Seam Security provides an extensible framework for resolving application permissions. The following class diagram shows an overview of the main components of the permission framework:
The relevant classes are explained in more detail in the following sections.

15.6.8.1. PermissionResolver

An interface that provides methods for resolving individual object permissions. Seam provides the following built-in PermissionResolver implementations, which are described in greater detail later in the chapter:
  • RuleBasedPermissionResolver — Resolves rule-based permission checks with Drools.
  • PersistentPermissionResolver — Stores object permissions in a permanent store, such as a relational database.
15.6.8.1.1. Writing your own PermissionResolver
Implementing your own permission resolver is simple. The PermissionResolver interface defines two methods that must be implemented, as seen in the following table. If your PermissionResolver is deployed in your Seam project, it will be scanned automatically during deployment and registered with the default ResolverChain.

Table 15.7. PermissionResolver interface

Return type
Method
Description
boolean
hasPermission(Object target, String action)
This method resolves whether the currently authenticated user (obtained via a call to Identity.getPrincipal()) has the permission specified by the target and action parameters. It returns true if the user has the specified permission, or false if they do not.
void
filterSetByAction(Set<Object> targets, String action)
This method removes any objects from the specified set that would return true if passed to the hasPermission() method with the same action parameter value.

Note

Because they are cached in the user's session, any custom PermissionResolver implementations must adhere to several restrictions. Firstly, they cannot contain any state that is more fine-grained than the session scope, and the component itself should be either application- or session-scoped. Secondly, they must not use dependency injection, as they may be accessed from multiple threads simultaneously. For optimal performance, we recommend annotating with @BypassInterceptors to bypass Seam's interceptor stack altogether.

15.6.8.2. ResolverChain

A ResolverChain contains an ordered list of PermissionResolvers, to resolve object permissions for a particular object class or permission target.
The default ResolverChain consists of all permission resolvers discovered during application deployment. The org.jboss.seam.security.defaultResolverChainCreated event is raised (and the ResolverChain instance passed as an event parameter) when the default ResolverChain is created. This allows additional resolvers that were not discovered during deployment to be added, or for resolvers that are in the chain to be re-ordered or removed.
The following sequence diagram shows the interaction between the components of the permission framework during a permission check. A permission check can originate from a number of possible sources: the security interceptor, the s:hasPermission EL function, or via an API call to Identity.checkPermission:
  • 1. A permission check is initiated (either in code or via an EL expression), resulting in a call to Identity.hasPermission().
  • 1.1. Identity invokes PermissionMapper.resolvePermission(), passing in the permission to be resolved.
  • 1.1.1. PermissionMapper maintains a Map of ResolverChain instances, keyed by class. It uses this map to locate the correct ResolverChain for the permission's target object. Once it has the correct ResolverChain, it retrieves the list of PermissionResolvers it contains by calling ResolverChain.getResolvers().
  • 1.1.2. For each PermissionResolver in the ResolverChain, the PermissionMapper invokes its hasPermission() method, passing in the permission instance to be checked. If the PermissionResolvers return true, the permission check has succeeded and the PermissionMapper also returns true to Identity. If none of the PermissionResolvers return true, then the permission check has failed.

15.6.9. RuleBasedPermissionResolver

One of the built-in permission resolvers provided by Seam. This evaluates permissions based on a set of Drools (JBoss Rules) security rules. Some advantages to the rule engine are a centralized location for the business logic used to evaluate user permissions, and speed — Drools algorithms are very efficient for evaluating large numbers of complex rules involving multiple conditions.

15.6.9.1. Requirements

If you want to use the rule-based permission features provided by Seam Security, Drools requires the following JAR files to be distributed with your project:
  • drools-api.jar
  • drools-compiler.jar
  • drools-core.jar
  • janino.jar
  • antlr-runtime.jar
  • mvel2.jar

15.6.9.2. Configuration

The configuration for RuleBasedPermissionResolver requires that a Drools rule base is first configured in components.xml. By default, it expects the rule base to be named securityRules, as per the following example:

<components xmlns="http://jboss.org/schema/seam/components" xmlns:core="http://jboss.org/schema/seam/core" xmlns:security="http://jboss.org/schema/seam/security" xmlns:drools="http://jboss.org/schema/seam/drools" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jboss.org/schema/seam/core http://jboss.org/schema/seam/core-2.3.xsd http://jboss.org/schema/seam/components http://jboss.org/schema/seam/components-2.3.xsd
http://jboss.org/schema/seam/drools http://jboss.org/schema/seam/drools-2.3.xsd
http://jboss.org/schema/seam/security http://jboss.org/schema/seam/security-2.3.xsd">
     
 <drools:rule-base name="securityRules">
  <drools:rule-files>
   <value>/META-INF/security.drl</value>
  </drools:rule-files>
 </drools:rule-base>
</components>
The default rule base name can be overridden by specifying the security-rules property for RuleBasedPermissionResolver:
 
<security:rule-based-permission-resolver 
          security-rules="#{prodSecurityRules}"/>
Once the RuleBase component is configured, you must write the security rules.

15.6.9.3. Writing Security Rules

The first step to writing security rules is to create a new rule file in the /META-INF directory of your application's jar file. This file should be named security.drl or similar, but can be named anything as long as it is configured correspondingly in components.xml.
We recommend the Drools documentation when you write your rules file. A simple example of rules file contents is:
package MyApplicationPermissions;
  
import org.jboss.seam.security.permission.PermissionCheck;
import org.jboss.seam.security.Role;
  
rule CanUserDeleteCustomers
when
  c: PermissionCheck(target == "customer", action == "delete")
  Role(name == "admin")
then
  c.grant();
end
Here, the first thing we see is the package declaration. A package in Drools is a collection of rules. The package name does not relate to anything outside the scope of the rule base, so it can be given any name.
Next, we have several import statements for the PermissionCheck and Role classes. These imports inform the rules engine that our rules will refer to these classes.
Finally, we have the rule code. Each rule within a package should have a unique name, usually to describe the rule's purpose. In this case our rule is called CanUserDeleteCustomers and will be used to check whether a user is allowed to delete a customer record.
There are two distinct sections in the body of the rule definition. Rules have a left hand side (LHS) and a right hand side (RHS). The LHS is the conditional portion of the rule, that is, a list of conditions that must be satisfied for the rule to fire. The LHS is represented by the when section. The RHS is the consequence or action section of the rule, which will only be fired if all conditions in the LHS are met. The RHS is represented by the then section. The end of the rule is denoted by the end line.
There are two conditions listed in the example LHS. The first condition is:
c: PermissionCheck(target == "customer", action == "delete")
More plainly, this condition states that, to be fulfilled, there must be a PermissionCheck object with a target property equal to customer, and an action property equal to delete within the working memory.
Working memory is also known as a stateful session in Drools terminology. It is a session-scoped object containing the contextual information that the rules engine requires to make a decision about a permission check. Each time the hasPermission() method is called, a temporary PermissionCheck object, or Fact, is inserted into the working memory. This PermissionCheck corresponds exactly to the permission being checked, so if you call hasPermission("account", "create"), a PermissionCheck object with a target equal to "account" and action equal to "create" will be inserted into the working memory for the duration of the permission check.
Other than the PermissionCheck facts, there is also an org.jboss.seam.security.Role fact for each role that the authenticated user is a member of. These Role facts are synchronized with the user's authenticated roles at the beginning of every permission check. As a consequence, any Role object inserted into the working memory during the course of a permission check will be removed before the next permission check occurs, unless the authenticated user is actually a member of that role. The working memory also contains the java.security.Principal object created as a result of the authentication process.
You can insert additional long-lived facts into the working memory by calling RuleBasedPermissionResolver.instance().getSecurityContext().insert (), which passes the object as a parameter. Role objects are the exception, here, since they are synchronized at the start of each permission check.
To return to our simple example, the first line of our LHS is prefixed with c:. This is a variable binding, and is used to refer back to the object matching the condition (in this case, the PermissionCheck). The second line of the LHS is:
Role(name == "admin")
This condition states that there must be a Role object with a name of "admin" within the working memory. So, if you are checking for the customer:delete permission and the user is a member of the admin role, this rule will fire.
The RHS shows us the consequence of the rule firing:
c.grant()
The RHS consists of Java code. In this case it invokes the grant() method of the c object, which is a variable binding for the PermissionCheck object. Other than the name and action properties of the PermissionCheck object, there is also a granted property. This is initially set to false. Calling grant() on a PermissionCheck sets the granted property to true. This means the permission check succeeded, and the user has permission to carry out the action that prompted the permission check.

15.6.9.4. Non-String permission targets

So far, we have only looked at permission checks for String-literal permission targets. However, you can also write security rules for more complex permission targets. For example, say you want to write a security rule to allow your users to create blog comments. The following rule shows one way this can be expressed, by requiring that the target of the permission check be an instance of MemberBlog, and that the currently authenticated user be a member of the user role:
rule CanCreateBlogComment
  no-loop
  activation-group "permissions"
when
  blog: MemberBlog()
  check: PermissionCheck(target == blog, action == "create", 
                         granted == false)
  Role(name == "user")
then
  check.grant();
end

15.6.9.5. Wildcard permission checks

It is possible to implement a wildcard permission check (which allows all actions for a given permission target), by omitting the action constraint for the PermissionCheck in your rule, like so:
rule CanDoAnythingToCustomersIfYouAreAnAdmin
when
  c: PermissionCheck(target == "customer")
  Role(name == "admin")
then
  c.grant();
end;
This rule allows users with the admin role to perform any action for any customer permission check.

15.6.10. PersistentPermissionResolver

Another built-in permission resolver provided by Seam, PersistentPermissionResolver, allows permissions to be loaded from persistent storage, such as a relational database. This permission resolver provides Access Control List-style instance-based security, allowing specific object permissions to be assigned to individual users and roles. It also allows persistent, arbitrarily-named permission targets (which are not necessarily object/class based) to be assigned in the same way.

15.6.10.1. Configuration

To use PersistentPermissionResolver, you must configure a valid PermissionStore in components.xml. If this is not configured, the PersistentPermissionResolver will attempt to use the default permission store, Section 15.4.2.4, “JpaIdentityStore Events”. To use a permission store other than the default, configure the permission-store property as follows:
 
<security:persistent-permission-resolver 
          permission-store="#{myCustomPermissionStore}"/>

15.6.10.2. Permission Stores

PersistentPermissionResolver requires a permission store to connect to the back-end storage where permissions are persisted. Seam provides one PermissionStore implementation out of the box, JpaPermissionStore, which stores permissions inside a relational database. You can write your own permission store by implementing the PermissionStore interface, which defines the following methods:

Table 15.8. PermissionStore interface

Return type
Method
Description
List<Permission>
listPermissions(Object target)
This method should return a List of Permission objects representing all the permissions granted for the specified target object.
List<Permission>
listPermissions(Object target, String action)
This method should return a List of Permission objects representing all the permissions with the specified action granted for the specified target object.
List<Permission>
listPermissions(Set<Object> targets, String action)
This method should return a List of Permission objects representing all the permissions with the specified action granted for the specified set of target objects.
boolean
grantPermission(Permission)
This method should persist the specified Permission object to the back-end storage, and return true if successful.
boolean
grantPermissions(List<Permission> permissions)
This method should persist all of the Permission objects contained in the specified List, and return true if successful.
boolean
revokePermission(Permission permission)
This method should remove the specified Permission object from persistent storage.
boolean
revokePermissions(List<Permission> permissions)
This method should remove all of the Permission objects in the specified list from persistent storage.
List<String>
listAvailableActions(Object target)
This method should return a list of all available actions (as Strings) for the class of the specified target object. It is used in conjunction with permission management to build the user interface for granting specific class permissions.

15.6.10.3. JpaPermissionStore

The Seam-provided default PermissionStore implementation, which stores permissions in a relational database. It must be configured with either one or two entity classes for storing user and role permissions before it can be used. These entity classes must be annotated with a special set of security annotations to configure the entity properties that correspond to various aspects of the stored permissions.
If you want to use the same entity (that is, a single database table) to store both user and role permissions, then you only need to configure the user-permission-class property. To user separate tables for user and role permission storage, you must also configure the role-permission-class property.
For example, to configure a single entity class to store both user and role permissions:
 
<security:jpa-permission-store 
          user-permission-class="com.acme.model.AccountPermission"/>
To configure separate entity classes for storing user and role permissions:
<security:jpa-permission-store 
          user-permission-class="com.acme.model.UserPermission" 
          role-permission-class="com.acme.model.RolePermission"/>
15.6.10.3.1. Permission annotations
The entity classes that contain the user and role permissions must be configured with a special set of annotations in the org.jboss.seam.annotations.security.permission package. The following table describes these annotations:

Table 15.9. Entity Permission annotations

Annotation
Target
Description
@PermissionTarget
FIELD,METHOD
This annotation identifies the entity property containing the permission target. The property should be of type java.lang.String.
@PermissionAction
FIELD,METHOD
This annotation identifies the entity property containing the permission action. The property should be of type java.lang.String.
@PermissionUser
FIELD,METHOD
This annotation identifies the entity property containing the recipient user for the permission. It should be of type java.lang.String and contain the user's username.
@PermissionRole
FIELD,METHOD
This annotation identifies the entity property containing the recipient role for the permission. It should be of type java.lang.String and contain the role name.
@PermissionDiscriminator
FIELD,METHOD
This annotation should be used when the same entity/table stores both user and role permissions. It identifies the property of the entity being used to discriminate between user and role permissions. By default, if the column value contains the string literal user, then the record will be treated as a user permission. If it contains the string literal role, it will be treated as a role permission. You can also override these defaults by specifying the userValue and roleValue properties within the annotation. For example, to use u and r instead of user and role, write the annotation like so:

@PermissionDiscriminator(
  userValue = "u", 
  roleValue = "r")
15.6.10.3.2. Example Entity
The following is an example of an entity class that stores both user and role permissions, taken from the Seamspace example.
@Entity
public class AccountPermission implements Serializable {  
  private Integer permissionId;
  private String recipient;
  private String target;
  private String action;
  private String discriminator;
   
  @Id @GeneratedValue
  public Integer getPermissionId() {
    return permissionId;
  }
   
  public void setPermissionId(Integer permissionId) {
    this.permissionId = permissionId;
  }
   
  @PermissionUser @PermissionRole
  public String getRecipient() {
    return recipient;
  }
   
  public void setRecipient(String recipient) {
    this.recipient = recipient;
  }
   
  @PermissionTarget
  public String getTarget() {
    return target;
  }
   
  public void setTarget(String target) {
    this.target = target;
  }
   
  @PermissionAction
  public String getAction() {
    return action;
  }
   
  public void setAction(String action) {
    this.action = action;
  }
   
  @PermissionDiscriminator
  public String getDiscriminator() {
    return discriminator;
  }
   
  public void setDiscriminator(String discriminator) {
    this.discriminator = discriminator;
  }
}
Here, the getDiscriminator() method has been annotated with @PermissionDiscriminator, to allow JpaPermissionStore to determine which records represent user permissions and which represent role permissions. The getRecipient() method is annotated with both @PermissionUser and @PermissionRole. This means that the recipient property of the entity will either contain the name of the user or the name of the role, depending on the value of the discriminator property.
15.6.10.3.3. Class-specific Permission Configuration
The permissions included in the org.jboss.seam.annotation.security.permission package can be used to configure a specific set of allowable permissions for a target class.

Table 15.10. Class Permission Annotations

Annotation
Target
Description
@Permissions
TYPE
A container annotation, which can contain an array of @Permission annotations.
@Permission
TYPE
This annotation defines a single allowable permission action for the target class. Its action property must be specified, and an optional mask property may also be specified if permission actions are to be persisted as bitmasked values (see section following).
The following shows the above annotations in use. They can also be seen in the SeamSpace example.
@Permissions({
  @Permission(action = "view"),
  @Permission(action = "comment")
})

@Entity
public class MemberImage implements Serializable {...}
This example demonstrates how two allowable permission actions, view and comment can be declared for the entity class MemberImage.
15.6.10.3.4. Permission masks
By default, multiple permissions for the same target object and recipient will be persisted as a single database record, with the action property/column containing a list of granted actions, separated by commas. You can use a bitmasked integer value to store the list of permission actions — this reduces the amount of physical storage required to persist a large number of permissions.
For example, if recipient "Bob" is granted both the view and comment permissions for a particular MemberImage (an entity bean) instance, then by default the action property of the permission entity will contain "view,comment", representing the two granted permission actions. Or, if you are using bitmasked values defined as follows:
@Permissions({
  @Permission(action = "view", mask = 1),
  @Permission(action = "comment", mask = 2)
})

@Entity
public class MemberImage implements Serializable {...}
The action property will contain "3" (with both the 1 bit and 2 bit switched on). For a large number of allowable actions for any particular target class, the storage required for the permission records is greatly reduced by using bitmasked actions.

Important

The mask values specified must be powers of 2.
15.6.10.3.5. Identifier Policy
When storing or looking up permissions, JpaPermissionStore must be able to uniquely identify specific object instances. To achieve this, an identifier strategy may be assigned to each target class so that unique identifier values can be generated. Each identifier strategy implementation knows how to generate unique identifiers for a particular type of class, and creating new identifier strategies is simple.
The IdentifierStrategy interface is very simple, declaring only two methods:
public interface IdentifierStrategy { 
  boolean canIdentify(Class targetClass); 
  String getIdentifier(Object target); 
}
The first method, canIdentify(), returns true if the identifier strategy is capable of generating a unique identifier for the specified target class. The second method, getIdentifier(), returns the unique identifier value for the specified target object.
Seam also provides two IdentifierStrategy implementations, ClassIdentifierStrategy and EntityIdentifierStrategy, which are described in the sections following.
To explicitly configure a specific identifier strategy for a particular class, annotate the strategy with org.jboss.seam.annotations.security.permission.Identifier and set the value to a concrete implementation of the IdentifierStrategy interface. You may also specify a name property. (The effect of this depends upon the IdentifierStrategy implementation used.)
15.6.10.3.6. ClassIdentifierStrategy
This identifier strategy generates unique identifiers for classes, and uses the value of the name (if specified) in the @Identifier annotation. If no name property is provided, the identifier strategy attempts to use the component name of the class (if the class is a Seam component). It will create an identifier based on the class name (excluding the package name) as a last resort. For example, the identifier for the following class will be customer:
@Identifier(name = "customer") 
public class Customer {...}
The identifier for the following class will be customerAction:
@Name("customerAction") 
public class CustomerAction {...}
Finally, the identifier for the following class will be Customer:
public class Customer {...}
15.6.10.3.7. EntityIdentifierStrategy
This identifier strategy generates unique identifiers for entity beans. It concatenates the entity name (or otherwise configured name) with a string representation of the primary key value of the entity. The rules for generating the name section of the identifier are similar to those in ClassIdentifierStrategy. The primary key value (that is, the entity ID) is obtained with the PersistenceProvider component, which can determine the value regardless of the persistence implementation being used in the Seam application. For entities not annotated with @Entity, you must explicitly configure the identifier strategy on the entity class itself, like this:
@Identifier(value = EntityIdentifierStrategy.class) 
public class Customer {...}
Assume we have the following entity class:
@Entity
public class Customer {
  private Integer id;
  private String firstName;
  private String lastName;
  
  @Id 
  public Integer getId() { return id; }
  public void setId(Integer id) { this.id = id; }
  
  public String getFirstName() { return firstName; }
  public void setFirstName(String firstName) { 
    this.firstName = firstName; 
  }
  
  public String getLastName() { return lastName; }
  public void setLastName(String lastName) { this.lastName = lastName; }
}
For a Customer instance with an id value of 1, the value of the identifier would be Customer:1. If the entity class is annotated with an explicit identifier name, like so:
@Entity @Identifier(name = "cust") 
public class Customer {...}
Then a Customer with an id value of 123 would have an identifier value of "cust:123".

15.7. Permission Management

Just as Seam Security provides an Identity Management API to let you manage users and roles, it also provides a Permissions Management API to let you manage persistent user permissions — the PermissionManager component.

15.7.1. PermissionManager

The PermissionManager component is an application-scoped Seam component that provides a number of permission-management methods. It must be configured with a permission store before use. By default, it will attempt to use JpaPermissionStore. To configure a custom permission store, specify the permission-store property in components.xml:
 
<security:permission-manager permission-store="#{ldapPermissionStore}"/>
The following table describes each of the methods provided by PermissionManager:

Table 15.11. PermissionManager API methods

Return type
Method
Description
List<Permission>
listPermissions(Object target, String action)
Returns a list of Permission objects representing all of the permissions that have been granted for the specified target and action.
List<Permission>
listPermissions(Object target)
Returns a list of Permission objects representing all of the permissions that have been granted for the specified target and action.
boolean
grantPermission(Permission permission)
Persists (grants) the specified Permission to the back-end permission store. Returns true if the operation succeeds.
boolean
grantPermissions(List<Permission> permissions)
Persists (grants) the specified list of Permissions to the back-end permission store. Returns true if the operation succeeds.
boolean
revokePermission(Permission permission)