Red Hat JBoss Web Framework Kit 2.1

Seam Reference Guide

for use with JBoss Web Framework Kit 2

Edition 2.1.0

Gavin King

Pete Muir

Norman Richards

Shane Bryzak

Michael Yuan

Mike Youngstrom

Christian Bauer

Jay Balunas

Dan Allen

Max Rydahl Andersen

Emmanuel Bernard

Nicklas Karlsson

Daniel Roth

Matt Drees

Jacob Orshalick

Marek Novotny

James Cobb

Graphic Design

Cheyenne Weaver

Graphic Design

Mark Newton

Steve Ebersole

Michael Courcy

French Translation

Nicola Benaglia

Italian Translation

Stefano Travelli

Italian Translation

Francesco Milesi

Italian Translation

Japan JBoss User Group

Japanese Translation

Edited by

Samson Kittoli

Edited by

Laura Bailey

Edited by

Elspeth Thorne

Legal Notice

Copyright © 2012 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 JBoss Web Framework Kit 2 and its patch releases.
Preface
1. Document Conventions
1.1. Typographic Conventions
1.2. Pull-quote Conventions
1.3. Notes and Warnings
2. Getting Help and Giving Feedback
2.1. Do You Need Help?
2.2. Give us Feedback
1. Seam Tutorial
1.1. Using the Seam examples
1.1.1. Running the examples on JBoss Enterprise Application Platform
1.1.2. Running the example tests
1.2. Your first Seam application: the registration example
1.2.1. Understanding the code
1.2.2. How it works
1.3. Clickable lists in Seam: the messages example
1.3.1. Understanding the code
1.3.2. How it works
1.4. A complete Seam application: the Hotel Booking example
1.4.1. Introduction
1.4.2. Overview of the booking example
1.4.3. Understanding Seam conversations
1.4.4. The Seam Debug Page
1.5. Nested conversations: extending the Hotel Booking example
1.5.1. Introduction
1.5.2. Understanding Nested Conversations
1.6. Bookmarkable URLs with the Blog example
1.6.1. Using "pull"-style MVC
1.6.2. Bookmarkable search results page
1.6.3. Using "push"-style MVC in a RESTful application
2. Migration from 2.2 to 2.3
2.1. Migration of XML Schemas
2.1.1. Seam schema migration
2.1.2. Java EE 6 schema changes
2.2. Java EE 6 upgrade
2.2.1. Using Bean Validation standard instead of Hibernate Validator
2.2.2. Migration of JSF 1 to JSF 2 Facelets templates
2.2.3. Migration to JPA 2.0
2.2.4. Using compatible JNDI for resources
2.3. JBoss Enterprise Application Platform 6 deployment
2.3.1. Deployment changes
2.3.2. Datasource migration
2.4. Changes in the testing framework
2.5. Moving from JBoss Cache to Infinispan Tree
2.6. Dependency changes when using Maven
2.6.1. Seam Bill of Materials
3. Getting started with seam-gen
3.1. Before you start
3.2. Setting up a new project
3.3. Creating a new action
3.4. Creating a form with an action
3.5. Generating an application from an existing database
3.6. Generating an application from existing JPA/EJB3 entities
3.7. Deploying the application as an EAR
3.8. Seam and incremental hot deployment
4. Getting started with JBoss Developer Studio
4.1. Hot deployment with JBoss Developer Studio
5. The contextual component model
5.1. Seam contexts
5.1.1. Stateless context
5.1.2. Event context
5.1.3. Page context
5.1.4. Conversation context
5.1.5. Session context
5.1.6. Application context
5.1.7. Context variables
5.1.8. Context search priority
5.1.9. Concurrency model
5.2. Seam components
5.2.1. Stateless session beans
5.2.2. Stateful session beans
5.2.3. Entity beans
5.2.4. JavaBeans
5.2.5. Message-driven beans
5.2.6. Interception
5.2.7. Component names
5.2.8. Defining the component scope
5.2.9. Components with multiple roles
5.2.10. Built-in components
5.3. Bijection
5.4. Life cycle methods
5.5. Conditional installation
5.6. Logging
5.7. The Mutable interface and @ReadOnly
5.8. Factory and manager components
6. Configuring Seam components
6.1. Configuring components via property settings
6.2. Configuring components via components.xml
6.3. Fine-grained configuration files
6.4. Configurable property types
6.5. Using XML Namespaces
7. Events, interceptors and exception handling
7.1. Seam events
7.2. Page actions
7.3. Page parameters
7.3.1. Mapping request parameters to the model
7.4. Propagating request parameters
7.5. URL rewriting with page parameters
7.6. Conversion and Validation
7.7. Navigation
7.8. Fine-grained files for defining navigation, page actions and parameters
7.9. Component-driven events
7.10. Contextual events
7.11. Seam interceptors
7.12. Managing exceptions
7.12.1. Exceptions and transactions
7.12.2. Enabling Seam exception handling
7.12.3. Using annotations for exception handling
7.12.4. Using XML for exception handling
7.12.5. Some common exceptions
8. Conversations and workspace management
8.1. Seam's conversation model
8.2. Nested conversations
8.3. Starting conversations with GET requests
8.4. Requiring a long-running conversation
8.5. Using <s:link> and <s:button>
8.6. Success messages
8.7. Natural conversation IDs
8.8. Creating a natural conversation
8.9. Redirecting to a natural conversation
8.10. Workspace management
8.10.1. Workspace management and JSF navigation
8.10.2. The conversation switcher
8.10.3. The conversation list
8.10.4. Breadcrumbs
8.11. Conversational components and JSF component bindings
8.12. Concurrent calls to conversational components
8.12.1. How should we design our conversational AJAX application?
8.12.2. Dealing with errors
8.12.3. RichFaces (Ajax4jsf)
9. Seam and Object/Relational Mapping
9.1. Introduction
9.2. Seam managed transactions
9.2.1. Disabling Seam-managed transactions
9.2.2. Configuring a Seam transaction manager
9.2.3. Transaction synchronization
9.3. Seam-managed persistence contexts
9.3.1. Using a Seam-managed persistence context with JPA
9.3.2. Using a Seam-managed Hibernate session
9.3.3. Seam-managed persistence contexts and atomic conversations
9.4. Using the JPA "delegate"
9.5. Using EL in EJB-QL/HQL
9.6. Using Hibernate filters
10. JSF form validation in Seam
11. Groovy integration
11.1. Groovy introduction
11.2. Writing Seam applications in Groovy
11.2.1. Writing Groovy components
11.2.2. Seam component
11.2.3. seam-gen
11.3. Deployment
11.3.1. Deploying Groovy code
11.3.2. Native .groovy file deployment at development time
11.3.3. seam-gen
12. The Seam Application Framework
12.1. Introduction
12.2. Home objects
12.3. Query objects
12.4. Controller objects
13. Seam and JBoss Rules
13.1. Installing rules
13.2. Using rules from a Seam component
14. Security
14.1. Overview
14.2. Disabling Security
14.3. Authentication
14.3.1. Configuring an Authenticator component
14.3.2. Writing an authentication method
14.3.3. Writing a login form
14.3.4. Configuration Summary
14.3.5. Remember Me
14.3.6. Handling Security Exceptions
14.3.7. Login Redirection
14.3.8. HTTP Authentication
14.3.9. Advanced Authentication Features
14.4. Identity Management
14.4.1. Configuring IdentityManager
14.4.2. JpaIdentityStore
14.4.3. LdapIdentityStore
14.4.4. Writing your own IdentityStore
14.4.5. Authentication with Identity Management
14.4.6. Using IdentityManager
14.5. Error Messages
14.6. Authorization
14.6.1. Core concepts
14.6.2. Securing components
14.6.3. Security in the user interface
14.6.4. Securing pages
14.6.5. Securing Entities
14.6.6. Typesafe Permission Annotations
14.6.7. Typesafe Role Annotations
14.6.8. The Permission Authorization Model
14.6.9. RuleBasedPermissionResolver
14.6.10. PersistentPermissionResolver
14.7. Permission Management
14.7.1. PermissionManager
14.7.2. Permission checks for PermissionManager operations
14.8. SSL Security
14.8.1. Overriding the default ports
14.9. CAPTCHA
14.9.1. Configuring the CAPTCHA Servlet
14.9.2. Adding a CAPTCHA to a form
14.9.3. Customizing the CAPTCHA algorithm
14.10. Security Events
14.11. Run As
14.12. Extending the Identity component
14.13. OpenID
14.13.1. Configuring OpenID
14.13.2. Presenting an OpenIdLogin form
14.13.3. Logging in immediately
14.13.4. Deferring log in
14.13.5. Logging out
15. Internationalization, localization and themes
15.1. Internationalizing your application
15.1.1. Application server configuration
15.1.2. Translated application strings
15.1.3. Other encoding settings
15.2. Locales
15.3. Labels
15.3.1. Defining labels
15.3.2. Displaying labels
15.3.3. Faces messages
15.4. Timezones
15.5. Themes
15.6. Persisting locale and theme preferences via cookies
16. Seam Text
16.1. Basic formatting
16.2. Entering code and text with special characters
16.3. Links
16.4. Entering HTML
16.5. Using the SeamTextParser
17. iText PDF generation
17.1. Using PDF Support
17.1.1. Creating a document
17.1.2. Basic Text Elements
17.1.3. Headers and Footers
17.1.4. Chapters and Sections
17.1.5. Lists
17.1.6. Tables
17.1.7. Document Constants
17.2. Charting
17.3. Bar codes
17.4. Fill-in-forms
17.5. Rendering Swing/AWT components
17.6. Configuring iText
17.7. Further documentation
18. The Microsoft® Excel® spreadsheet application
18.1. Microsoft Excel support
18.2. Creating a simple workbook
18.3. Workbooks
18.4. Worksheets
18.5. Columns
18.6. Cells
18.6.1. Validation
18.6.2. Format masks
18.7. Formulas
18.8. Images
18.9. Hyperlinks
18.10. Headers and footers
18.11. Print areas and titles
18.12. Worksheet Commands
18.12.1. Grouping
18.12.2. Page breaks
18.12.3. Merging
18.13. Datatable exporter
18.14. Fonts and layout
18.14.1. Stylesheet links
18.14.2. Fonts
18.14.3. Borders
18.14.4. Background
18.14.5. Column settings
18.14.6. Cell settings
18.14.7. The datatable exporter
18.14.8. Limitations
18.15. Internationalization
18.16. Links and further documentation
19. Email
19.1. Creating a message
19.1.1. Attachments
19.1.2. HTML/Text alternative part
19.1.3. Multiple recipients
19.1.4. Multiple messages
19.1.5. Templating
19.1.6. Internationalization
19.1.7. Other Headers
19.2. Configuration
19.2.1. mailSession
19.3. Tags
20. Asynchronicity and messaging
20.1. Asynchronicity
20.1.1. Asynchronous methods
20.1.2. Asynchronous methods with the Quartz Dispatcher
20.1.3. Asynchronous events
20.1.4. Handling exceptions from asynchronous calls
20.2. Messaging in Seam
20.2.1. Configuration
20.2.2. Sending messages
20.2.3. Receiving messages using a message-driven bean
20.2.4. Receiving messages in the client
21. Caching
21.1. Using Caching in Seam
21.2. Page fragment caching
22. Web Services
22.1. Configuration and Packaging
22.2. Conversational Web Services
22.2.1. A Recommended Strategy
22.3. An example web service
22.4. RESTful HTTP web services with RESTEasy
22.4.1. RESTEasy configuration and request serving
22.4.2. Resources and providers as Seam components
22.4.3. Securing resources
22.4.4. Mapping exceptions to HTTP responses
22.4.5. Exposing entities via RESTful API
23. Remoting
23.1. Configuration
23.2. The Seam object
23.2.1. A Hello World example
23.2.2. Seam.Component
23.2.3. Seam.Remoting
23.3. Evaluating EL Expressions
23.4. Client Interfaces
23.5. The Context
23.5.1. Setting and reading the Conversation ID
23.5.2. Remote calls within the current conversation scope
23.6. Batch Requests
23.7. Working with Data types
23.7.1. Primitives / Basic Types
23.7.2. JavaBeans
23.7.3. Dates and Times
23.7.4. Enums
23.7.5. Collections
23.8. Debugging
23.9. Handling Exceptions
23.10. The Loading Message
23.10.1. Changing the message
23.10.2. Hiding the loading message
23.10.3. A Custom Loading Indicator
23.11. Controlling what data is returned
23.11.1. Constraining normal fields
23.11.2. Constraining Maps and Collections
23.11.3. Constraining objects of a specific type
23.11.4. Combining Constraints
23.12. Transactional Requests
23.13. JMS Messaging
23.13.1. Configuration
23.13.2. Subscribing to a JMS Topic
23.13.3. Unsubscribing from a Topic
23.13.4. Tuning the Polling Process
24. Seam and the Google Web Toolkit
24.1. Configuration
24.2. Preparing your component
24.3. Hooking up a GWT widget to the Seam component
24.4. GWT Ant Targets
24.5. GWT Maven plugin
25. Spring Framework integration
25.1. Injecting Seam components into Spring beans
25.2. Injecting Spring beans into Seam components
25.3. Making a Spring bean into a Seam component
25.4. Seam-scoped Spring beans
25.5. Using Spring PlatformTransactionManagement
25.6. Using a Seam-Managed Persistence Context in Spring
25.7. Using a Seam-Managed Hibernate Session in Spring
25.8. Spring Application Context as a Seam Component
25.9. Using a Spring TaskExecutor for @Asynchronous
26. Hibernate Search
26.1. Introduction
26.2. Configuration
26.3. Usage
27. Configuring Seam and packaging Seam applications
27.1. Basic Seam configuration
27.1.1. Integrating Seam with JSF and your servlet container
27.1.2. Seam Resource Servlet
27.1.3. Seam Servlet filters
27.1.4. Integrating Seam with your EJB container
27.1.5. Remember
27.2. Using Alternate JPA Providers
27.3. Configuring Seam in Java EE 6
27.3.1. Packaging
27.4. Configuring Seam without EJB
27.4.1. Boostrapping Hibernate in Seam
27.4.2. Boostrapping JPA in Seam
27.4.3. Packaging
27.5. Configuring Seam in Java SE
27.6. Deployment in JBoss Enterprise Application Platform 6
27.7. Configuring SFSB and Session Timeouts in JBoss Enterprise Application Platform 6
27.8. Running Seam in a Portlet
27.9. Deploying custom resources
28. Seam annotations
28.1. Annotations for component definition
28.2. Annotations for bijection
28.3. Annotations for component life cycle methods
28.4. Annotations for context demarcation
28.5. Annotations for use with Seam JavaBean components in a J2EE environment
28.6. Annotations for exceptions
28.7. Annotations for Seam Remoting
28.8. Annotations for Seam interceptors
28.9. Annotations for asynchronicity
28.10. Annotations for use with JSF
28.10.1. Annotations for use with dataTable
28.11. Meta-annotations for databinding
28.12. Annotations for packaging
28.13. Annotations for integrating with the Servlet container
29. Built-in Seam components
29.1. Context injection components
29.2. JSF-related components
29.3. Utility components
29.4. Components for internationalization and themes
29.5. Components for controlling conversations
29.6. Security-related components
29.7. JMS-related components
29.8. Mail-related components
29.9. Infrastructural components
29.10. Miscellaneous components
29.11. Special components
30. Seam JSF controls
30.1. Tags
30.1.1. Navigation Controls
30.1.2. Converters and Validators
30.1.3. Formatting
30.1.4. Seam Text
30.1.5. Form support
30.1.6. Other
30.2. Annotations
31. JBoss EL
31.1. Parameterized Expressions
31.1.1. Usage
31.1.2. Limitations and Hints
31.2. Projection
32. Performance Tuning
32.1. Bypassing Interceptors
33. Testing Seam applications
33.1. Unit testing Seam components
33.2. Integration testing Seam components
33.2.1. Configuration
33.2.2. Using JUnitSeamTest with Arquillian
33.2.3. Integration testing Seam application user interactions
34. Dependencies
34.1. JDK Dependencies
34.1.1. Oracle's JDK 6 Considerations
34.2. Project Dependencies
34.2.1. Core
34.2.2. RichFaces
34.2.3. Seam PDF
34.2.4. Seam Microsoft® Excel®
34.2.5. Drools
34.2.6. GWT
34.2.7. Spring
34.2.8. Groovy
34.3. Dependency Management using Maven
A. Revision History

Preface

1. Document Conventions

This manual uses several conventions to highlight certain words and phrases and draw attention to specific pieces of information.
In PDF and paper editions, this manual uses typefaces drawn from the Liberation Fonts set. The Liberation Fonts set is also used in HTML editions if the set is installed on your system. If not, alternative but equivalent typefaces are displayed. Note: Red Hat Enterprise Linux 5 and later include the Liberation Fonts set by default.

1.1. Typographic Conventions

Four typographic conventions are used to call attention to specific words and phrases. These conventions, and the circumstances they apply to, are as follows.
Mono-spaced Bold
Used to highlight system input, including shell commands, file names and paths. Also used to highlight keys and key combinations. For example:
To see the contents of the file my_next_bestselling_novel in your current working directory, enter the cat my_next_bestselling_novel command at the shell prompt and press Enter to execute the command.
The above includes a file name, a shell command and a key, all presented in mono-spaced bold and all distinguishable thanks to context.
Key combinations can be distinguished from an individual key by the plus sign that connects each part of a key combination. For example:
Press Enter to execute the command.
Press Ctrl+Alt+F2 to switch to a virtual terminal.
The first example highlights a particular key to press. The second example highlights a key combination: a set of three keys pressed simultaneously.
If source code is discussed, class names, methods, functions, variable names and returned values mentioned within a paragraph will be presented as above, in mono-spaced bold. For example:
File-related classes include filesystem for file systems, file for files, and dir for directories. Each class has its own associated set of permissions.
Proportional Bold
This denotes words or phrases encountered on a system, including application names; dialog-box text; labeled buttons; check-box and radio-button labels; menu titles and submenu titles. For example:
Choose SystemPreferencesMouse from the main menu bar to launch Mouse Preferences. In the Buttons tab, select the Left-handed mouse check box and click Close to switch the primary mouse button from the left to the right (making the mouse suitable for use in the left hand).
To insert a special character into a gedit file, choose ApplicationsAccessoriesCharacter Map from the main menu bar. Next, choose SearchFind… from the Character Map menu bar, type the name of the character in the Search field and click Next. The character you sought will be highlighted in the Character Table. Double-click this highlighted character to place it in the Text to copy field and then click the Copy button. Now switch back to your document and choose EditPaste from the gedit menu bar.
The above text includes application names; system-wide menu names and items; application-specific menu names; and buttons and text found within a GUI interface, all presented in proportional bold and all distinguishable by context.
Mono-spaced Bold Italic or Proportional Bold Italic
Whether mono-spaced bold or proportional bold, the addition of italics indicates replaceable or variable text. Italics denotes text you do not input literally or displayed text that changes depending on circumstance. For example:
To connect to a remote machine using ssh, type ssh username@domain.name at a shell prompt. If the remote machine is example.com and your username on that machine is john, type ssh john@example.com.
The mount -o remount file-system command remounts the named file system. For example, to remount the /home file system, the command is mount -o remount /home.
To see the version of a currently installed package, use the rpm -q package command. It will return a result as follows: package-version-release.
Note the words in bold italics above: username, domain.name, file-system, package, version and release. Each word is a placeholder, either for text you enter when issuing a command or for text displayed by the system.
Aside from standard usage for presenting the title of a work, italics denotes the first use of a new and important term. For example:
Publican is a DocBook publishing system.

1.2. Pull-quote Conventions

Terminal output and source code listings are set off visually from the surrounding text.
Output sent to a terminal is set in mono-spaced roman and presented thus:
books        Desktop   documentation  drafts  mss    photos   stuff  svn
books_tests  Desktop1  downloads      images  notes  scripts  svgs
Source-code listings are also set in mono-spaced roman but add syntax highlighting as follows:
package org.jboss.book.jca.ex1;

import javax.naming.InitialContext;

public class ExClient
{
   public static void main(String args[]) 
       throws Exception
   {
      InitialContext iniCtx = new InitialContext();
      Object         ref    = iniCtx.lookup("EchoBean");
      EchoHome       home   = (EchoHome) ref;
      Echo           echo   = home.create();

      System.out.println("Created Echo");

      System.out.println("Echo.echo('Hello') = " + echo.echo("Hello"));
   }
}

1.3. Notes and Warnings

Finally, we use three visual styles to draw attention to information that might otherwise be overlooked.

Note

Notes are tips, shortcuts or alternative approaches to the task at hand. Ignoring a note should have no negative consequences, but you might miss out on a trick that makes your life easier.

Important

Important boxes detail things that are easily missed: configuration changes that only apply to the current session, or services that need restarting before an update will apply. Ignoring a box labeled “Important” will not cause data loss but may cause irritation and frustration.

Warning

Warnings should not be ignored. Ignoring warnings will most likely cause data loss.

2. Getting Help and Giving Feedback

2.1. Do You Need Help?

If you experience difficulty with a procedure described in this documentation, visit the Red Hat Customer Portal at http://access.redhat.com. Through the customer portal, you can:
  • search or browse through a knowledgebase of technical support articles about Red Hat products.
  • submit a support case to Red Hat Global Support Services (GSS).
  • access other product documentation.
Red Hat also hosts a large number of electronic mailing lists for discussion of Red Hat software and technology. You can find a list of publicly available mailing lists at https://www.redhat.com/mailman/listinfo. Click on the name of any mailing list to subscribe to that list or to access the list archives.

2.2. Give us Feedback

If you find a typographical error, or know how this guide can be improved, we would love to hear from you. Submit a report in Bugzilla against the product JBoss Web Framework Kit 2 and the component doc-Seam_Reference_Guide. The following link will take you to a pre-filled bug report for this product: http://bugzilla.redhat.com/.
Fill out the following template in Bugzilla's Description field. Be as specific as possible when describing the issue; this will help ensure that we can fix it quickly.
Document URL:


Section Number and Name:


Describe the issue:


Suggestions for improvement:


Additional information:


Be sure to give us your name so that you can receive full credit for reporting the issue.

Chapter 1. Seam Tutorial

1.1. Using the Seam examples

Seam provides a number of example applications which demonstrate how to use a variety of Seam's features. This tutorial will guide you through a few of those examples to help you get started learning Seam. The Seam examples are located in the examples subdirectory of the Seam Technical Preview Demo file distributed with JBoss Web Framework Kit. The first example, on registration, is in the examples/registration directory.
Each example has the very similar directory structure which is based on Maven project structure defaults:
  • The <example>-ear directory contains enterprise application submodule files such as aggregator for web application files, 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.
  • The <example>-tests directory contains integration and 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

All the examples are built and run from the Maven pom.xml, so you'll need at least version 3.x of Maven installed before you get started.

1.1.1. Running the examples on JBoss Enterprise Application Platform

The examples are configured for use on JBoss Enterprise Application Platform. You will need to set jboss.home, in the shared build.properties file (in the root folder of your Seam installation) to the location of your JBoss Enterprise Application Platform installation.
Once you have set the location of JBoss Enterprise Application Platform and started the application server, you can build any example by typing mvn install in the example root directory. Any example is deployed by changing directory to *-ear or *-web directory and using the command mvn jboss-as:deploy. Any example that is packaged as an EAR (Enterprise Archive) deploys to a URL like /seam-example, where example is the name of the example folder, with one exception: if the example folder begins with "seam", the prefix "seam" is omitted. For instance, if JBoss Enterprise Application Platform is running on port 8080, the URL for the Registration example is http://localhost:8080/seam-registration/, whereas the URL for the SeamSpace example is http://localhost:8080/seam-space/.
If, on the other hand, the example is packaged as a WAR, then it deploys to a URL like /${name}-web.

Note

Several of the examples — groovybooking, hibernate, jpa, and spring — can only be deployed as a WAR.

1.1.2. Running the example tests

Most of the 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.

1.2. Your first Seam application: the registration example

The registration example is a simple application that lets a new user store their username, 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. Filling them in and submitting the form will save a user object in the database.

1.2.1. Understanding the code

The example is implemented with two Facelets templates: one entity bean, and one stateless session bean. This section will take you through the code in detail, starting from the base level.

1.2.1.1. The entity bean: User.java

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

Example 1.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 will instantiate that component, and bind the new instance to the context variable. In this case, Seam will instantiate 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 the persistent attributes of the entity bean. All of our persistent attributes define accessor methods. These are needed when this component is used by JSF in the render response and update model values phases.

6

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

7

The @NotNull and @Size annotations are part of the Hibernate Validator framework. Seam integrates Hibernate Validator and lets 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.

The most important things to notice in this example are the @Name and @Scope annotations. These annotations establish that this class is a Seam component.
In the next section, you will see 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 should not perform transaction management or database access, so this component should not be used as a JSF action listener. In this situation, a session bean is a better choice.

1.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.
We have exactly one JSF action in this application, and one session bean method attached to it. In this case, we will use a stateless session bean, since all the state associated with our action is held by the User bean.
The relevant code is shown below:

Example 1.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, since this is a session bean, a transaction is automatically begun when the register() method is called, and committed when it completes.

6

Notice that Seam lets you use a JSF EL expression inside EJB-QL. Under the covers, this results in an ordinary JPA setParameter() call on the standard JPA Query object.

7

The Log API lets us easily display templated log messages which can also make use of JSF EL expressions.

8

JSF action listener methods return a string-valued outcome that determines what page will be 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 lets you 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 will be used if 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 our 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 of the application code could have been eliminated by using Seam's application framework controllers.

1.2.1.3. The session bean local interface: Register.java

The session bean requires a local interface.

Example 1.3. Register.java

@Local
public interface Register
{
     public String register();
}

That's the end of the Java code. The next level to examine is the view.

1.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. This example was written with Facelets.

Example 1.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>
 <h:head>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/>
            <h:commandButton value="Register" action="#{register.register}"/>
         </h:form>
      </f:view>
   </h:body>

</html>

The only Seam-specific tag here 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 1.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.

1.2.1.5. The Seam component deployment descriptor: components.xml

Before looking at deployment descriptors, it is worth noting that Seam strongly values minimal configuration. These configuration files will be created for you when you create a Seam application, and there will rarely be any need to alter them. They are presented here solely to assist you in understanding the purpose and function of all of the example code.
If you have used Java frameworks previously, you will be used to declaring your component classes in an XML file. You have probably also noticed that as a project matures, these XML files tend to become unmanageable. Fortunately, Seam does not require application components to be accompanied by XML. Most Seam applications require only a small amount of XML, which does not tend to increase in size as projects expand.
However, it is often useful to be able to provide for some 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 how to find our EJB components in JNDI:

Example 1.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 a property named jndiPattern of a built-in Seam component named org.jboss.seam.core.init. More information about how this process works is available at: Section 6.2, “Configuring components via components.xml.

1.2.1.6. The web deployment description: web.xml

The presentation layer for our mini-application will be deployed in a WAR, so a web deployment descriptor is required:

Example 1.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. The configuration you see here changes very little between Seam applications.

1.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. In this case, Facelets is used to define our views, so we need to tell JSF to use Facelets as its templating engine.

Example 1.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">

  <application>
	...
  </application>
  
</faces-config>

Note that JSF managed bean declarations are unnecessary because the managed beans are annotated Seam components. In Seam applications, faces-config.xml is used much less often than in plain JSF. Here, we use it simply to enable Facelets (and not JSP) as the view handler.
Once you have set up all the basic descriptors, the only XML you need write to add functionality to a Seam application will be for orchestration: navigation rules. 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, since the view ID was embedded in our action code.

1.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 session beans in the archive.

Example 1.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>

1.2.1.9. The JPA persistence deployment descriptor: persistence.xml

The persistence.xml file tells the JPA persistence provider where to find the datasource, and contains some vendor-specific settings. In this case, enables automatic schema export at startup time.

Example 1.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>

1.2.1.10. The EAR deployment descriptor: application.xml

Finally, since our application is deployed as an EAR, we also require a deployment descriptor. This file can be generated by the Maven EAR plug-in if the registration application has it set up in the registration-ear/pom.xml file.

Example 1.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 of the files in the application.

1.2.2. How it works

When the form is submitted, JSF asks Seam to resolve the variable named user. Since no value is yet bound to that name (in any Seam context), Seam instantiates the user component, and returns the resulting User entity bean instance to JSF after storing it in the Seam session context.
The form input values are now 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 properties of the User entity bean.
Next, JSF asks Seam to resolve the variable named register. Seam uses the JNDI pattern mentioned earlier to locate the stateless session bean, wraps it as a Seam component, and returns it. Seam then presents this 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 with the entered username already exists. 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 with that username exists, 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.

1.3. Clickable lists in Seam: the messages example

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

1.3.1. Understanding the code

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

1.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 1.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;
   }
   
}

1.3.1.2. The stateful session bean: MessageManagerBean.java

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

Example 1.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 us to use the list in a JSF <h:dataTable> with clickable links for each row. In this case, the DataModel is made available in a session context variable named messageList.

2

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

3

The @Out annotation then 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 then subsequently outjected to the event context variable named message.

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 any subsequent method calls to the stateful bean can update them without needing to make any explicit call to the EntityManager.

5

The first time we navigate to the JSF page, there will be no value in the messageList context variable. The @Factory annotation tells Seam to create an instance of MessageManagerBean and invoke the findMessages() method to initialize the value. We call findMessages() 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 that Seam uses to remove the stateful bean when the Seam context ends, and clean up any server-side state.

Note

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

1.3.1.3. The session bean local interface: MessageManager.java

All session beans have a business interface:

Example 1.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 no longer shown in these code examples. Components.xml, persistence.xml, web.xml, ejb-jar.xml, faces-config.xml and application.xml operate in a similar fashion to the previous example, and go directly to the JSF.

1.3.1.4. The view: messages.xhtml

The JSF page is a straightforward use of the JSF <h:dataTable> component. Again, nothing specific to Seam.

Example 1.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>

1.3.2. How it works

The first time we navigate to the messages.xhtml page, the page will try to resolve the messageList context variable. Since this context variable is not initialized, Seam will call the factory method findMessages(), which performs a query against the database and results in a DataModel being outjected. This DataModel provides the row data needed for rendering the <h:dataTable>.
When the user clicks the <h:commandLink>, JSF calls the select() action listener. Seam intercepts this 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 context variable named message. Next, the EJB container commits the transaction, and the change to the Message is flushed to the database. Finally, the page is re-rendered, redisplaying the message list, and displaying the selected message below it.
If the user clicks the <h:commandButton>, JSF calls the delete() action listener. Seam intercepts this 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 also calling remove() on the EntityManager. At the end of the call, Seam refreshes the messageList context variable and clears the context variable named message. The EJB container commits the transaction, and deletes the Message from the database. Finally, the page is re-rendered, redisplaying the message list.

1.4. A complete Seam application: the Hotel Booking example

1.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.0 and Seam, together with Facelets for the view. There is also a port of this application to JSF, Facelets, Seam, JavaBeans and Hibernate 4.
One of the things you will notice about this application is that it is extremely robust. You can open multiple windows, use the back and browser refresh buttons, and enter nonsensical data, but the application is difficult to crash. Seam was designed to make building robust web applications straightforward, so robustness that would previously be hand-coded comes naturally and automatically with Seam.
As you browse the source code of the example application and learn how the application works, pay particular attention to the way the declarative state management and integrated validation has been used to achieve this robustness.

1.4.2. Overview of the booking example

The project structure here is identical to that of the previous project. To install and deploy this application, refer to Section 1.1, “Using the Seam examples”. Once you have successfully started the application, you can access it by pointing your 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 is implemented as a conversation, so this is one of the more important classes 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 representing a user who can make hotel bookings

1.4.3. Understanding Seam conversations

This tutorial concentrates upon 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 our perspective, it is important that searching remains separate so that users can 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 what cannot be transferred here is either added to the HttpSession or recorded to the database at the beginning and end of each request.
Since the database is the least-scalable tier, this drastically reduces scalability. The extra traffic to and from the database also increases latency. In order to reduce redundant traffic, Java applications often introduce a data cache to store commonly-accessed data between requests. However, since invalidation is based upon an LRU policy, rather than whether the user has finished using the data, this cache is inefficient. It is also shared between concurrent transactions, which introduces further issues associated with keeping the cached state consistent with that of the database.
State held in the HttpSession suffers similar issues. The HttpSession is fine for storing true session data — data common to all requests between user and application — but for data related to individual request series, it does not work so well. Conversations stored here quickly break down when dealing with multiple windows or the back button. Without careful programming, data in the HttpSession can also grow quite large, which makes 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 greatly improves conditions by introducing conversation context as a first class construct. Conversation state is stored safely in this context, with a well-defined life cycle. Even better, 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 are sometimes regarded as detrimental to scalability, and in the past, they may have been. 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. Used correctly, stateful session beans pose no scalability problems, but for those uncomfortable or unfamiliar with the use of stateful session beans, Seam also allows the use of POJOs.
The booking example shows one way that stateful components with different scopes can collaborate to achieve complex behaviors. The main page of the booking application allows the user to search for hotels. Search results are stored in the Seam session scope. When the user 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 1.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 this 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 conversation variable named hotels.

4

The EJB standard @Remove annotation specifies that a stateful session bean should be removed and its state destroyed after invocation of the annotated method. In Seam, all stateful session beans must define a parameterless method marked @Remove. This method will be called when Seam destroys the session context.

The main page of the application is a Facelets page. The fragment that relates to searching for hotels is shown below:

Example 1.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. Even better, the render attribute let's us 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 lets us display an animated image while we wait for asynchronous requests to return.

3

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

4

The Seam <s:link> tag lets us attach a JSF action listener to an ordinary (non-JavaScript) HTML link. The advantage of this over the standard JSF <h:commandLink> is that it preserves the operation of "open in new window" and "open in new tab". Also notice that we use 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 lets you use parameters on any method binding expression.
If you are wondering how navigation occurs, you can find all the rules in WEB-INF/pages.xml; this is discussed in Section 7.7, “Navigation”.

This page displays search results dynamically as the user types, and passes a selected hotel to the selectHotel() method of HotelBookingAction, where the real work occurs.
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 1.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

This bean uses an EJB3 extended persistence context, so that any entity instances remain managed for the whole 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 context variable named hotel will be 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 will not be destroyed at the end of the request. Instead, it will be reassociated 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 will be destroyed at the end of the request.

5

This EJB remove method will be 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 state related to this work in its instance variables. This code is much cleaner and simpler than getting and setting HttpSession attributes.
Even better, a user 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'll be able to work on creating two different hotel reservations at the same time. If you leave any one conversation inactive for long enough, 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.

1.4.4. The Seam Debug Page

The WAR also includes seam-debug.jar. To make the Seam debug page available, deploy this 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 lets you browse and inspect the Seam components in any of the Seam contexts associated with your current log in session. Just point your browser at http://localhost:8080/seam-booking/debug.seam .

1.5. Nested conversations: extending the Hotel Booking example

1.5.1. Introduction

Long-running conversations let you easily maintain state consistency in an application, even in the face of multi-window operation and back-buttoning. Unfortunately, simply beginning and ending a long-running conversation is not always enough. Depending on the requirements of the application, inconsistencies between user expectations and application state can still result.
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 the user can select. This requires the addition of a room selection page in the hotel reservation flow.
The user can now select any available room to be included in the booking. If room selection were left in the same conversation context, this could lead to issues with state consistency — if a conversation variable changes, it affects all windows operating within the same conversation context.
For example, suppose the user clones the room selection screen in a new window. The user then selects the Wonderful Room and proceeds to the confirmation screen. To check the cost of a more expensive room, the user returns to the original window, selects the Fantastic Suite for booking, and again proceeds to confirmation. After reviewing the total cost, the user returns to the window showing Wonderful Room to confirm.
In this scenario, if all state were stored in the conversation, flexibility for multi-window operation within the same conversation would be limited. Nested conversations allow us to achieve correct behavior even when context can vary within the same conversation.

1.5.2. Understanding Nested Conversations

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

Example 1.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 us to lazily load the availableRooms through an @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 state, but setting any 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 the nested conversation has an independent context, the roomSelection is only set into the new nested conversation. Should the user select a different preference in another window or tab a new nested conversation would be started.

4

The @End annotation pops the conversation stack and resumes the outer conversation. The roomSelection is destroyed along with the conversation context.

When we 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 1.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>
          <h:commandLink id="selectRoomPreference" 
            action="#{roomPreference.selectPreference}">Select</h:commandLink>      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 will only be executed 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 then outjected to the nested conversation context.

3

Revising the dates simply returns to the /book.xhtml. Note that we have not yet nested a conversation (no room preference has been selected), so the current conversation can simply be resumed. The <s:button> component simply propagates the current conversation when displaying the /book.xhtml view.

Now that you have seen how to nest a conversation, the following code shows how we can confirm the booking of a selected room by extending the behavior of the HotelBookingAction.

Example 1.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 any conversation is ended, its nested conversations are ended as well. As the root is the conversation that started it all, this is a simple way to destroy and release all state associated with a workspace once the booking is confirmed.

2

The roomSelection is only associated with the booking on user confirmation. While outjecting values to the nested conversation context will not impact the outer conversation, any objects injected from the outer conversation are injected by reference. This means that any changing to these objects will be reflected in the parent conversation as well as other concurrent nested conversations.

3

By simply annotating the cancellation action with @End(root=true, beforeRedirect=true) we can easily destroy and release all state associated with the workspace prior to redirecting the user back to the hotel selection view.

Feel free to deploy the application and test it yourself. Open many windows or tabs, and attempt combinations of various hotel and room preferences. Confirming a booking will always result in the correct hotel and room preference with the nested conversation model.

1.6. Bookmarkable URLs with the Blog example

Seam makes it easy to implement applications which keep 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 any 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.
Blog example
The blog example demonstrates the use of "pull"-style model view control (MVC), where 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.

1.6.1. Using "pull"-style MVC

This 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 we navigate to this page from a bookmark, the #{blog.recentBlogEntries} data used by the <h:dataTable> is retrieved lazily — "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 1.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

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

2

The @Unwrap annotation tells Seam to provide the return value of the method — the Blog — instead of the actual BlogService component to clients. This is the Seam manager component pattern.

This will store basic view content, but to bookmark form submission results like a search results page, there are several other required definitions.

1.6.2. Bookmarkable search results page

The blog example has a small form at the top right of each page that allows the user to search for blog entries. This is defined in menu.xhtml, which is included by the Facelets template template.xhtml:
<div id="search"> 
  <h:form> 
    <h:inputText value="#{searchAction.searchPattern}"/> 
    <h:commandButton value="Search" action="/search.xhtml"/> 
  </h:form> 
</div>
To implement a bookmarkable search results page, after the search form submission is processed, we must perform a browser redirect. Because the JSF view ID is used as the action outcome, Seam automatically redirects to the view ID when the form is submitted. We could also have defined 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>
In that case, the form would have looked like this:
<div id="search"> 
  <h:form> 
    <h:inputText value="#{searchAction.searchPattern}"/> 
    <h:commandButton 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. Both are defined here 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 would ordinarily be 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 instead 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 us 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;
    }

}

1.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 would 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

Note that the example uses page actions for post validation and the pageview counter. Also note the use of a parameter 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. Remember that, because of URL rewriting, the blogEntryId parameter name won't 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 the blog entry is not found in the database, the EntryNotFoundException exception is thrown. We want this exception to result in a 404 error, not a 505, so we annotate the exception class:
@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 very simple password authentication, posting to the blog, page fragment caching and atom feed generation.

Chapter 2. Migration from 2.2 to 2.3

This migration guide assumes you are using Seam 2.2, 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 guide as well.

Important

Seam 2.3 has been re-architected to support features of Java EE6 run on JBoss Enterprise Application Platform 6. jBPM3 was deemed unsuitable for EE6 and JBoss JBoss Enterprise Application Platform 6.

2.1. Migration of XML Schemas

2.1.1. Seam schema migration

XML schemas for validation Files that use the Seam 2.2 XSDs should be updated to refer to the 2.3 XSDs, notice the version change. Current namespace pattern is www.jboss.org/schema/seam/* and schemaLocation URL was changed to www.jboss.org/schema/seam/*_-2.3.xsd, where * is Seam module.
Following snippet is an example of component declaration for 2.2 version:

Example 2.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 2.3 version:

Example 2.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">

The remainning migration step is to update your pages.xml files and schemas.

Example 2.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 2.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>

2.1.2. Java EE 6 schema changes

Seam 2.3 technology upgrade includes an update to Java EE 6. The following descriptors will need to be updated:
  • 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 if you need to specify some advanced configuration for JSF 2 (this desciptor file is not mandatory, you do not have to include it in your application).
Examples of changed headers with correct versions are the following:

Example 2.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 2.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 2.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 2.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">

2.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. Various EE 6 technology upgrades require a change to the XML schema declaration. See Section 2.1.2, “Java EE 6 schema changes”.

2.2.1. Using Bean Validation standard instead of Hibernate Validator

Bean Validation is a standard included in Java EE 6 as new technology. Seam already 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 used 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.

2.2.2. Migration of JSF 1 to JSF 2 Facelets templates

Configuration file faces-config.xml is not required to be in your application, so for simple using of JSF 2 you need to migrate only web.xml. If you still wish to you the faces-config.xml file, see the associated section for migration information: Example 2.8, “faces-config.xml”.
All your application JSF templates should use only facelets technology as JSP is deprecated.
In facelet templates there are required to convert <head>/<body> tags to ><h:head>/<h:body>respectively.
Depending on the JSF components you use (for example, Richfaces or Icefaces), there will be differences when upgrading from JSF 1.x to JSF 2.x. You may need to upgrade libraries entirely. Consult any component framework documentation on those changes. Migration of these independent components is not covered here.

2.2.3. Migration to JPA 2.0

Using JPA 2 requires a change to version 2.0 in your persistence.xml file, see Example 2.5, “persistence.xml”. The version in your application.xml file should be 6 if you are using EAR (Example 2.6, “application.xml”), or version 3.0 in web.xml if you only use WAR archives (Example 2.7, “web.xml”).

Note

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

2.2.4. Using compatible JNDI for resources

Java EE 6 brings a new set of standardized global rules for creating portable JNDI syntax. You will need to 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}

2.3. JBoss Enterprise Application Platform 6 deployment

2.3.1. Deployment changes

The next level of migration is of your target runtime. Seam 2.3 uses the JBoss Enterprise Application Platform 6 as its default target runtime.
You need to change the JNDI datasource in your persistence.xml from java:/DefaultDS to java:jboss/datasources/ExampleDS.
Refactored classloading can now be utilized. Classloading of bundled or provided libraries can be managed in jboss-deployment-structure.xml or in META-INF/MANIFEST.MF file, in the Dependencies section. We recommend use of the jboss-deployment-structure.xml file, which should be placed in the META-INF directory of your WAR or EAR application, according to your application type.
For EAR projects, the jboss-deployment-structure.xml will be located in the _your_ear_/META-INF directory.
For Web (non-ear) projects, the jboss-deployment-structure.xml will be 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.

2.3.2. Datasource migration

You can also include any database descriptor (*-ds.xml) files into your project in the META-INF directory, and the data source will be deployed automatically when the application is. The structure of this file has changed to be based on IronJacamar (IronJacamar). Iron-Jacamar is the JBoss JCA (Java Connector Architecture) project. Below (Example 2.9, “Sample Seam 2.2 Datasource Descriptor File”) is the former datasource for JBoss followed by the conversion to IronJacamar using the same driver, url, and credentials (Example 2.10, “Ironjacamar Datasource Descriptor File”).

Example 2.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 2.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>

2.4. Changes in the testing framework

SeamTest and JBoss Embedded are legacy components and are no longer supported as they were in Seam 2.2.
Arquillian is the replacement for JBoss Embedded and you should 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. Migration to Junit and Arquillian can be completed as follows:
  1. Add
    @RunWith(Arquillian.class)
    annotation to your test class.
  2. Your test class should extend org.jboss.seam.mock.JUnitSeamTest instead of org.jboss.seam.mock.SeamTest.
  3. You can add a method for creating ShrinkWrap deployment; Seam examples and integration testsuite uses helper class for this purpose. For an example see the Booking sample test modules jboss-seam-x.y.z.Final/examples/booking/booking-tests/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.shrinkwrap.api.ShrinkWrap;
    	import org.jboss.shrinkwrap.api.spec.EnterpriseArchive;
    	import org.jboss.shrinkwrap.api.importer.ZipImporter;
    	public class Deployments {
    		public static EnterpriseArchive bookingDeployment() {
    			return ShrinkWrap.create(ZipImporter.class, "seam-booking.ear").importFrom(new File("../booking-ear/target/seam-booking.ear")).as(EnterpriseArchive.class);
    			}
    
  4. Add a method like:
    @Deployment(name="_your_test_name_")
    @OverProtocol("Servlet 3.0")
    public static org.jboss.shrinkwrap.api.Archive<?> createDeployment(){}
    
    for creating test deployment archive. The following example is taken from Booking example testsuite:
    @Deployment(name="BookingTest")
    @OverProtocol("Servlet 3.0") 
    public static Archive<?> createDeployment()
    	{
    		EnterpriseArchive er = Deployments.bookingDeployment();
    		WebArchive web = er.getAsType(WebArchive.class, "booking-web.war");
    		web.addClasses(BookingTest.class);
    		return er;
    	}
    
  5. Add the arquillian.xml file into 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 can be seen at jboss-seam-x.y.z.Final/examples/booking/booking-tests/src/test/resources-integration/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=512m</property>
    		</configuration>
    	</container>
    </arquillian>
    

2.5. Moving from JBoss Cache to Infinispan Tree

In the Seam components.xml file you will need to change all occurrences of<cache:jboss-cache3-provider/> to <cache:infinispan-cache-provider/> .
You will then need to migrate your JBoss Cache XML configuration to Infinispan XML. This can be done by using the migration tool provided with Infinispan. For more information see: https://docs.jboss.org/author/display/ISPN/Configuration+Migration+Tools.

2.6. Dependency changes when using Maven

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

2.6.1. Seam Bill of Materials

A Bill of materials is a set of dependeny elements in the <dependencyManagement> section that is used to declare which dependencies and their versions to use in your application. The usage of Seam BOM is shown in Example 2.11, “Seam BOM usage”. The Seam BOM is deployed in JBoss Maven repository. See the JBoss Web Framework Kit Maven Repository User Guide for further information.

Example 2.11. Seam BOM usage

<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.jboss.seam</groupId>
			<artifactId>bom</artifactId>
			<version>2.3.0.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>

You can refer to the updated Seam examples distributed with Seam if you require further assistance.

Chapter 3. Getting started with seam-gen

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

3.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 running 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.

3.2. Setting up a new project

You will need to configure seam-gen for your environment:
cd jboss-seam-2.3.0
				  seam setup
A prompt will appear for the needed 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 sensible defaults, which you can accept by just pressing enter at the prompt.
The most important choice you need to make is between EAR deployment and WAR deployment of your project. EAR projects support EJB 3.0 and require Java EE 5. WAR projects do not support EJB 3.0, but may be deployed to a J2EE environment. The packaging of a WAR is also simpler to understand. If you installed an EJB3-ready application server like JBoss, choose ear. Otherwise, choose war. We'll assume that you've chosen an EAR deployment for the rest of the tutorial, but you can follow exactly the same steps for a WAR deployment.
If you are working with an existing data model, make sure you tell 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 simply by running seam setup a second time.
Now we can create a new project in our 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, and generates all needed resources and configuration files, a facelets template file and stylesheet, along with Eclipse metadata and an Ant build script. The Eclipse project will be automatically deployed to an exploded directory structure in JBoss AS as soon as 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, you will need to select a Java SE 6 compliant JDK using Project -> Properties -> Java Compiler.
Alternatively, you can 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 your 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.)

3.3. Creating a new action

Task:

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

Procedure 3.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. Since 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. Now 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.

3.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.)

3.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.

3.6. Generating an application from existing JPA/EJB3 entities

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

3.7. Deploying the application as an EAR

Several changes are required before we can deploy the application with standard Java EE 5 packaging. First, 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. Just 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.

3.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 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
If you want to change any Java code, you will still need to 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 make use of this, the JavaBean components must be deployed into the WEB-INF/dev directory. Here, they will be loaded by a special Seam classloader instead of the WAR or EAR classloader.
This function has some 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 will not be visible to any 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 4. Getting started with JBoss Developer Studio

JBoss Developer Studio is a collection of Eclipse plug-ins: a project creation wizard for Seam, a content assistant for the Unified Expression Language (EL) in both Facelets and Java, a graphical editor for jPDL, a graphical editor for Seam configuration files, support for running Seam integration tests from within Eclipse, and much more. For more information, review the JBoss Developer Studio Getting Started Guide available at http://docs.redhat.com/docs/en-US/JBoss_Developer_Studio/index.html.
Procedures to execute the following Seam tasks in JBoss Developer Studio are in the Seam Developer Tools Reference Guide:
  • Setting up a Seam project.
  • Creating a new Seam action.
  • Creating a new form with an action.
  • Generating an application from an existing database.
The Seam Developer Tools Reference Guide is available at http://docs.redhat.com/docs/en-US/JBoss_Developer_Studio/index.html.

4.1. Hot deployment with JBoss Developer Studio

JBoss Developer Studio supports incremental hot deployment of any Facelets page and any pages.xml file out of the box. But if we want to change any Java code, we still need to do a full restart of the application by doing a Full Publish.
Seam supports incremental redeployment of JavaBean components for a fast edit/compile/test cycle. To make use of this, the JavaBean components must be deployed into the WEB-INF/dev directory. Here, they will be loaded by a special Seam classloader instead of the WAR or EAR classloader.
This function has some 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 via components.xml may not be hot-deployed
  • the hot-deployable components will not be visible to any classes deployed outside of WEB-INF/dev
  • Seam debug mode must be enabled and jboss-seam-debug.jar must be 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 JBoss Developer Studio, incremental hot deployment is available out of the box. However, JBoss Developer Studio does not support incremental hot deployment for EAR projects.

Chapter 5. The contextual component model

Seam's two core concepts 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, which allows component trees to be dynamically assembled and reassembled by Seam.

5.1. Seam contexts

Seam has several built-in contexts, which are created and destroyed by the framework. The application does not control context demarcation via explicit Java API calls. Contexts are usually implicit. In some cases, however, contexts are demarcated with annotations.
There are a number of basic contexts:
  • Stateless context
  • Event (for instance, a request) context
  • Page context
  • Conversation context
  • Session context
  • Application context
Some of these contexts serve similar purposes in Servlet and related specifications. One you may not have encountered previously is the conversation context. One reason that state management in web applications is so fragile and error-prone is that the three built-in contexts (request, session, and application) are not especially 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, since these are the most meaningful contexts in terms of the application.

5.1.1. Stateless context

Components which 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.

5.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.

5.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 actually serialized to the client, so this construct is extremely robust with respect to multi-window operation and the back button.

5.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 several 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 state from the different conversations does 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.
Some conversations are also tasks. A task is a conversation that is significant to a long-running business process, and can trigger a business process state transition when completed successfully. Seam provides a special set of annotations for task demarcation.
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.

5.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.

5.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.

5.1.7. Context variables

A context defines a namespace through a set of context variables. These work similarly to 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 via injection. Component instances are subsequently given to contexts via outjection.

5.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.

5.1.9. Concurrency model

Neither the Servlet, nor EJB specifications, define facilities for managing concurrent requests from the same client. The Servlet container lets all threads run concurrently, without ensuring thread-safeness. The EJB container allows concurrent access of stateless components, and throws 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.
Because 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.

Note

Seam 2.3 removed the serialization of Stateful session beans by Seam synchronization interceptor because stateful session beans are serialized by EJB 3.1 container by default.
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 Stateful session Beans are not serialized by Seam anymore. Serialization of 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.

5.2. Seam components

Seam components are Plain Old Java Objects (POJOs). Specifically, they are JavaBeans, or Enterprise JavaBean 3.0 (EJB3) enterprise beans. 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

5.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.

5.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 in the database should be 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 lets Seam manage state life cycle, and ensures there are no collisions between state relating to different 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.

5.2.3. Entity beans

Entity beans can function as a Seam component when bound to a context variable. Because entities have a persistent identity in addition to their contextual identity, entity instances are usually bound explicitly in Java code, rather than being instantiated implicitly by Seam.
Entity bean components do not support bijection or context demarcation. Nor does 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.

5.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, etc.
In a later chapter, we show you how to use 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- 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.

5.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.

5.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, since 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>

5.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 lets you 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}"/>
Since 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 will check each of these namespaces, in order. Additional application-specific namespaces can be included in your application's components.xml file.

5.2.8. Defining the component scope

The @Scope annotation lets us 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.

5.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 becomes a conversation-scoped component. The @Role annotation lets us define an additional named role for a component, with a different scope — it lets us bind the same component class to different context variables. (Any Seam component instance can be bound to multiple context variables, but this lets us 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 lets us specify additional roles as required.
@Name("user") 
@Entity 
@Scope(CONVERSATION) 
@Roles({ @Role(name="currentUser", scope=SESSION), 
         @Role(name="tempUser", scope=EVENT)}) 
public class User { ... }

5.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}!");

5.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 various 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
Since the value of contextual variables changes over time, and since Seam components are stateful, bijection takes place every time a component is invoked.
In essence, bijection lets you 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 may wish to specify the context variable name explicitly, using, 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 way, it will always 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; } 
    ... 
}

5.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, since 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- 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={....}).

5.5. Conditional installation

The @Install annotation controls conditional installation of components that are required in some deployment scenarios and not 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, or
  • install some components only if their dependencies are available. (This is useful for framework authors.)
@Install lets you 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 will choose 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 our unit tests, there is no available JMS queue, so we would like to stub out this method. We'll 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 we are able to control precisely which classes are in the classpath, this works well. But if we are writing a reuseable framework with many dependencies, we do not want to have to break that framework across multiple jars. We 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.

5.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 will work 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, we would not even need to explicitly specify the log category, since 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, it will be used; 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.

5.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, since 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- and conversation-scoped JavaBean and entity bean components.
For session- 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- 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 scope 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 that the EntityHome class in the Seam Application Framework is an excellent example of managing an entity bean instance using a Seam component.

5.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, use them in value- 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 lets a Seam component act as the instantiator for a non-component object. A factory method will be 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 6. 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 reuseable frameworks, to configure Seam's built-in functionality, etc. Seam provides two basic approaches to component configuration: via property settings in a properties file or web.xml, and via components.xml.

6.1. Configuring components via 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(), we 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 via -D at start up, or
  • the same system property as a Servlet context parameter.
Any of these will 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, we provide a value for org.jboss.seam.core.manager.conversationTimeout in web.xml, seam.properties, or via 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().)

6.2. Configuring components via components.xml

The components.xml file is more powerful than property settings. It lets you:
  • 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 do have a @Name annotation but are not installed by default because of an @Install annotation that indicates the component should not be installed.
  • override the scope of a component.
A components.xml file appears in one of 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.
This 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>
This 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 create 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- or method-binding expression that will initialize the value of a context variable when it 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 like so:
<components> 
  <factory name="user" value="#{actor}" scope="STATELESS"/> 
</components>
You can even create an alias for a commonly used expression:
<components> 
  <factory name="contact" value="#{contactManager.contact}" 
           scope="STATELESS"/>
</components>
auto-create="true" is often used with the <factory> declaration:
<components> 
  <factory name="session" value="#{entityManager.delegate}" 
           scope="STATELESS" auto-create="true"/> 
</components>
The same 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 components.xml, which can be replaced at deployment time by either your Ant build script, or providing a file named components.properties in the classpath. (The latter approach appears in the Seam examples.)

6.3. Fine-grained configuration files

If a large number of components require XML configuration, it is sensible to split components.xml 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.
<components> lets you 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>
<component> only lets you configure 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 may put configuration for all classes in the com.helloworld package in com/helloworld/components.xml.

6.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 components.xml by default, unless SortedSet/SortedMap are used, in which case Seam refers to TreeMap/TreeSet. If the property has a concrete type (LinkedList, for example) Seam will use 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>
Finally, you can link components with a value-binding expression. Note that since 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.

6.5. Using XML Namespaces

Previous examples have alternated between two component declaration methods: with and without using XML namespaces. The following 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 us:
<?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.2.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 lets you 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;
Using the namespaced style in components.xml is that simple. Now we 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 7. 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.

7.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}"/>



7.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. We can define a page action for a particular JSF view ID:
<pages> 
	<page view-id="/hello.xhtml" action="#{helloWorld.sayHello}"/>
</pages>
Or we 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 wildcarded page actions match the current view-id, Seam will call all the actions, in 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, we 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 backwards compatibility. However, you are more likely to use false more often.

7.3. Page parameters

A Faces request (a JSF form submission) encapsulates both an action (a method binding) and parameters (input value bindings). A page action can also require parameters.
Since 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.

7.3.1. Mapping request parameters to the model

Seam lets us 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).

7.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. They are the most elegant method of propagating state across non-Faces requests. They are particularly useful in certain situations. For example, if we have search screens with bookmarkable results pages, page parameters let us 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.

7.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 were 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 more specific rules before more 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 27.1.3.3, “URL rewriting”.

7.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.

7.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 lets us 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, we probably want to end the current conversation, like so:
<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>
Since the conversation has ended, any subsequent requests will not know which document we are interested in. We 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>

7.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 sensible to 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>

7.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.
We 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 will be 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 so:
@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. If you prefer, you can also do this with annotations:
@Name("helloListener") 
public class HelloListener { 
    @Observer("hello") 
    public void sayHelloBack() { 
        FacesMessages.instance().add("Hello to you too!"); 
    } 
}
If you are familiar with component-driven events, you may be wondering about event objects. 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, if you do want to pass an event object, you can do so:
@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); 
    } 
}

7.10. Contextual events

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

Table 7.1. Contextual Events

Event Description
org.jboss.seam.validationFailed
Called when JSFvalidation fails.
org.jboss.seam.noConversation
Called when there is nolong-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 along-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 up all components.
org.jboss.seam.postReInitialization
Called when Seam has re-initialized and started up 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 was 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.

7.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 allowing 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, we 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, we would create an @LoggedIn annotation, as follows:
@Target(TYPE) 
@Retention(RUNTIME) 
@Interceptors(LoggedInInterceptor.class) 
    public @interface LoggedIn {}
We can now annotate our 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 a particular order of interceptors.
@Interceptor(around={BijectionInterceptor.class, 
                     ValidationInterceptor.class, 
                     ConversationInterceptor.class}, 
    within=RemoveInterceptor.class) 
public class LoggedInInterceptor { 
    ... 
}
You can even 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 that 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 annotation.
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).

7.12. Managing exceptions

JSF has a limited ability to handle exceptions. To work around this problem, Seam lets you 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.

7.12.1. Exceptions and transactions

EJB specifies well-defined rules to control whether an exception immediately marks the current transaction for rollback, when thrown 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 will 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 be marked rollback only, but it may still be active after the exception is thrown.
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 any active transaction.

7.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 fire, you must disable Facelets development mode in web.xml and Seam debug mode in components.xml.

7.12.3. Using annotations for exception handling

The following exception results in a HTTP 404 error whenever it propagates outside the Seam component layer. It does not roll back the current transaction immediately when thrown, but the transaction will be rolled back if it 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", end=true) 
@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 { 
    ... 
}

7.12.4. Using XML for exception handling

Since annotations cannot be added to all exception classes, Seam also lets us 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 via 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 it 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>
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.

7.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 will be logged, or to suppress exception logging altogether. The log and log-level attributes are used to control exception logging. No log message will be generated when 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 will be 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 will default to error.

7.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 the user posts to a page after their session has expired. The conversation-required and no-conversation-view-id settings in the Seam page descriptor, discussed in Section 8.4, “Requiring a long-running conversation”, allow finer-grained control over session expiration while accessing a page used within a conversation.

Chapter 8. Conversations and workspace management

Now we will take you through Seam's conversation model in finer 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 for richer and more efficient applications, using less verbose code.

8.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.
  • Any Faces request (a JSF postback) will propagate the conversation context. By default, non-Faces requests (GET requests, for example) 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 via @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) will not propagate the conversation context, and will be 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.

8.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 will be 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.
  • Any 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 look up from the conversation context, will first look up the value in the current conversation context. If no value is found, look up will continue down the conversation stack, if the conversation is nested. This behavior can be overridden.
When an @End is subsequently encountered, the nested conversation will be destroyed, and the outer conversation will resume, popping the conversation stack. Conversations may 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 will always destroy 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, thus ensuring truly correct behavior in the face of backbuttoning and workspace management.
As mentioned previously, if a component exists in a parent conversation of the current nested conversation, the nested conversation will use the same instance. Occasionally, it is useful to have a different instance in each nested conversation, so that the component instance that of the parent conversation is invisible to its child conversations. You can achieve this behavior by annotating the component @PerNestedConversation.

8.3. Starting conversations with GET requests

JSF does not define any action listener triggered when a page is accessed via a non-Faces request (a HTTP GET request, for example). This can occur when a user bookmarks the page, or navigates to the page via an <h:outputLink> .
Sometimes we want a conversation to begin immediately the page is accessed. Since there is no JSF action method, we cannot annotate the action with @Begin.
Further problems arise when the page requires state to be fetched into a context variable. We have already seen two methods of solving this problem. If the state is held in a Seam component, we can fetch the state in a @Create method. If not, we can define a @Factory method for the context variable.
If neither option works for you, Seam lets you 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 will process any 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 that #{conversation.end} similarly ends conversations.
The <begin-conversation> element can be used as follows for further control over joining existing conversations, or beginning a nested conversation, a pageflow, or an atomic conversation.
<pages> 
  <page view-id="/messageList.xhtml"> 
    <begin-conversation nested="true" /> 
  <page> 
  ... 
</pages>
There is also an <end-conversation> element.
<pages> 
  <page view-id="/home.xhtml"> 
    <end-conversation/> 
  <page> 
  ...
</pages>
We now have five options to begin a conversation immediately the 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

8.4. Requiring a long-running conversation

Certain pages are only relevant 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 lets you indicate that the current conversation must be long-running (or nested) in order for a page to be rendered, like so:
<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 while 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, like so:
    <pages no-conversation-view-id="/main.xhtml"/>
    This page will be used across the entire application; at present, multiple alternative pages cannot be defined.

8.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. If you require this functionality in plain JSF, you need to use an <h:outputLink> , but there are two major limitations to this method:
  • JSF provides no way to attach an action listener to an <h:outputLink> , and
  • JSF does not propagate the selected row of a DataModel, since there is no actual form submission.
To solve the first problem, Seam implements the notion of a page action, but this does not solve the second. It is possible to work around this by passing a request parameter and requerying for the selected object on the server-side, and in some cases (like the Seam blog example application), this is the best approach. Since it is RESTful and does not require server-side state, bookmarking is supported. In other cases, where bookmarking is unnecessary, @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> if you want the "link" rendered as a button:
<s:button action="#{login.logout}" value="Logout"/>

8.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 nextg render response phase for the current conversation. Since Seam preserves even temporary conversation contexts across redirects, this works even without a long-running conversation.
You can even include JSF EL expressions in a Faces message summary:
facesMessages.add("Document #{document.title} was updated");
Messages are displayed as usual:
<h:messages globalOnly="true"/>

8.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. Take the following situation, for 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 payment for the item.
With a natural conversation, the user can easily rejoin the previous conversation and pick up where they left off. In this case, they 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 heirarchy (that is, the user can navigate by editing the URL).
With a natural conversation, applications can generate long, complex URLs, but display simple, memorable URLs to users by using URLRewrite. In the case of our hotel booking example, http://seam-hotels/book.seam?hotel=BestWesternAntwerpen is rewritten as http://seam-hotels/book/BestWesternAntwerpen — much clearer. Note that URLRewrite relies upon parameters: hotel in the previous example must map to a unique parameter on the domain model.

8.8. Creating a natural conversation

Natural conversations are defined in pages.xml:
<conversation name="PlaceBid" parameter-name="auctionId" 
              parameter-value="#{auction.auctionId}"/>
The first thing to note in the above definition is the conversation name, in this case PlaceBid. The conversation name identifies this particular named conversation uniquely, and is used by the page definition to identify a named conversation in which to participate.
The parameter-name attribute defines the request parameter that will hold the natural conversation ID, and replace 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 use 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>

8.9. Redirecting to a natural conversation

When initiating or redirecting to a natural conversation, there are several ways to specify the natural conversation name. We will 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 we see that invoking #{bidAction.placeBid} redirects us to /bid.xhtml, which is configured with the natural conversation ID PlaceBid. Our 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, since 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"/>

8.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 to the user.
  • 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".

8.10.1. Workspace management and JSF navigation

With JSF or Seam navigation rules in place, Seam switches to a conversation by restoring the current view-id for that conversation. The descriptive text for the workspace is defined in a file called pages.xml, which Seam expects to find in the WEB-INF directory alongside faces-config.xml:
<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 will still work if this file is not present. However, workplace switching will not be available.

8.10.2. The conversation switcher

Including the following fragment in your JSF or Facelets page will include a drop-down menu that lets you 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, plus two additional items that let the user begin an additional conversation.
Only conversations with a description (specified in pages.xml) will be included in the drop-down menu.

8.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>
This can be customized for your own applications.
Only conversations with a description will be included in the list.
Note that the conversation list also lets the user destroy workspaces.

8.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>

8.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, since 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; 
    ... 
}
You are also limited in that 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.

8.12. Concurrent calls to conversational components

Section 5.1.9, “Concurrency model” contains a general discussion of concurrent calls to Seam components. In this section, we discuss the most common situation in which you will encounter concurrency — when accessing conversational components from AJAX requests. We will also look at 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.
We can set a sensible default for the concurrent request timeout (in milliseconds) in components.xml:
<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" />
So far we have discussed AJAX requests that appear serial to the user, where the client tells the server than 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 is a sensible approach when it is important that no action in a long-running action sequence times out.

8.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 queueing into your request delay.
Finally, the client library may provide an option to abort unfinished duplicate requests in favor of the most recent.
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; 
}

8.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 will become overloaded and be unable to process all the requests before the request will have to wait longer than the concurrent-request-timeout. In this case Seam will throw a ConcurrentRequestTimeoutException which can be handled in pages.xml. 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.

8.12.3. RichFaces (Ajax4jsf)

RichFaces (Ajax4jsf) is the most common AJAX library used with Seam, and provides all of the controls discussed in the previous section.
eventsQueue
Provides a queue in which events are placed. All events are queued, and requests are sent to the server serially. This is useful if the request to the server can take some time to execute (for example, in heavy computation, retrieving information from a slow source) since it prevents server flooding.
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, since it allows multiple concurrent requests.
requestDelay
Defines the time in milliseconds that the request will remain on the queue. If, at this time, the request has not been processed, the request will either be 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 action will take to execute.
<a:poll reRender="total" interval="1000" />
Polls the server and rerenders an area, as required.

Chapter 9. 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.

9.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.

9.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.

9.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 />

9.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 9.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 9.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 25.5, “Using Spring PlatformTransactionManagement”.

9.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.

9.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.)

9.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.

9.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;

9.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.

9.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 27.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");
}

9.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!

9.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 10. 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.com/products/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 11. 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.

11.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.

11.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.

11.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.

11.2.1.1. Entity

Example 11.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.

11.2.2. Seam component

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

Example 11.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])
    }
}

11.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.

11.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.

11.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.

11.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 3.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.

11.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.

Chapter 12. 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.

12.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.

12.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.

12.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:

12.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 13. Seam and JBoss Rules

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

13.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>

13.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 14. Security

14.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.

14.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.

14.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.

14.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.

14.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 14.10, “Security Events” later in this chapter for more information about events raised by Seam Security.

14.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.

14.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.

14.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.

14.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.

14.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>

14.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>

14.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.

14.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.

14.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.

14.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;
  }
}

14.3.9. Advanced Authentication Features

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

14.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 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.

14.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.

14.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.

14.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.

14.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"/> 

14.4.2.2. Configuring the Entities

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

Table 14.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 14.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.

14.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.
14.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; }
}
14.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; }  
  
}

14.4.2.4. JpaIdentityStore Events

When using JpaIdentityStore with IdentityManager, several events are raised when certain IdentityManager methods are invoked.
14.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.
14.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.
14.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.

14.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.

14.4.3.1. Configuring LdapIdentityStore

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

Table 14.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.

14.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"
/>

14.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.

14.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.

14.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 14.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 14.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

14.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 14.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.

14.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.

14.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.

14.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.

14.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.

14.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.

14.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.

14.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");

14.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>

14.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.

14.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 any insert operations. Note that the method need not perform any action; it is only important that it 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.

14.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>

14.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.

14.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")

14.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).

14.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.

14.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.
14.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 14.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.

14.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.

14.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.

14.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

14.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.

14.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.

14.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

14.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.

14.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.

14.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 14.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}"/>

14.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 14.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.

14.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"/>
14.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 14.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")

14.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.
14.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 14.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.
14.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.
14.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.)
14.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 {...}
14.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".

14.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.

14.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 14.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)
Removes (revokes) the specified Permission from the back-end permission store. Returns true if the operation succeeds.
boolean
revokePermissions(List<Permission> permissions)
Removes (revokes) the specified list of Permissions from the back-end permission store. Returns true if the operation succeeds.
List<String>
listAvailableActions(Object target)
Returns a list of the available actions for the specified target object. The actions that this method returns are dependent on the @Permission annotations configured on the target object's class.

14.7.2. Permission checks for PermissionManager operations

To invoke PermissionManager methods, the currently authenticated user must be authorized to perform that management operation. The following table lists the permissions required to invoke a particular method.

Table 14.12. Permission Management Security Permissions

Method
Permission Target
Permission Action
listPermissions()
The specified target.
seam.read-permissions
grantPermission()
The target of the specified Permission, or each of the targets for the specified list of Permissions (depending on the method called).
seam.grant-permission
grantPermission()
The target of the specified Permission.
seam.grant-permission
grantPermissions()
Each of the targets of the specified list of Permissions.
seam.grant-permission
revokePermission()
The target of the specified Permission.
seam.revoke-permission
revokePermissions()
Each of the targets of the specified list of Permissions.
seam.revoke-permission

14.8. SSL Security

Seam includes basic support for serving sensitive pages via the HTTPS protocol. To configure this, specify a scheme for the page in pages.xml. The following example shows how the view /login.xhtml can be configured to use HTTPS:
<page view-id="/login.xhtml" scheme="https"/>
This configuration automatically extends to both s:link and s:button JSF controls, which (when specifying the view) will render the link under the correct protocol. Based on the previous example, the following link will use the HTTPS protocol because /login.xhtml is configured to use it:
<s:link view="/login.xhtml" value="Login"/>
If a user browses directly to a view with the incorrect protocol, a redirect is triggered, and the same view will be reloaded with the correct protocol. For example, browsing to a scheme="https" page with HTTP triggers a redirect to the same page using HTTPS.
You can also configure a default scheme for all pages. This is useful if you only want to use HTTPS for a few pages. If no default scheme is specified, the current scheme will be used. So, once the user accesses a page requiring HTTPS, then HTTPS continues to be used after the user has navigated to other non-HTTPS pages. This is good for security, but not for performance. To define HTTP as the default scheme, add this line to pages.xml:
<page view-id="*" scheme="http" />
If none of the pages in your application use HTTPS, you need not define a default scheme.
You can configure Seam to automatically invalidate the current HTTP session each time the scheme changes. To do so, add this line to components.xml:
<web:session invalidate-on-scheme-change="true"/>
This option offers more protection from session ID sniffing and sensitive data leakage from pages using HTTPS to pages using HTTP.

14.8.1. Overriding the default ports

If you wish to configure the HTTP and HTTPS ports manually, you can do so in pages.xml by specifying the http-port and https-port attributes on the pages element:
 
<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"
no-conversation-view-id="/home.xhtml"
login-view-id="/login.xhtml"
http-port="8080"
https-port="8443">

14.9. CAPTCHA

Though not strictly part of the security API, Seam provides a built-in CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart) algorithm to prevent automated processes from interacting with your application.

14.9.1. Configuring the CAPTCHA Servlet

To use CAPTCHA, you need to configure the Seam Resource Servlet, which provides your pages with CAPTCHA challenge images. Add the following to web.xml:
<servlet>
  <servlet-name>Seam Resource Servlet</servlet-name>
  <servlet-class>org.jboss.seam.servlet.SeamResourceServlet</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>Seam Resource Servlet</servlet-name>
  <url-pattern>/seam/resource/*</url-pattern>
</servlet-mapping>

14.9.2. Adding a CAPTCHA to a form

It is easy to add a CAPTCHA challenge to a form:
<h:graphicImage value="/seam/resource/captcha"/>
  <h:inputText id="verifyCaptcha" value="#{captcha.response}" 
     required="true">
    <s:validate />
  </h:inputText>
<h:message for="verifyCaptcha"/>
That is all you need to do. The graphicImage control displays the CAPTCHA challenge, and the inputText receives the user's response. The response is automatically validated against the CAPTCHA when the form is submitted.

14.9.3. Customizing the CAPTCHA algorithm

You can customize the CAPTCHA algorithm by overriding the built-in component:
@Name("org.jboss.seam.captcha.captcha")
@Scope(SESSION)
public class HitchhikersCaptcha extends Captcha
{
  @Override @Create
  public void init() {
    setChallenge("What is the answer to life, the universe and everything?");
    setCorrectResponse("42");
  }

  @Override
  public BufferedImage renderChallenge() {
    BufferedImage img = super.renderChallenge();
    img.getGraphics().drawOval(5, 3, 60, 14); //add an obscuring decoration
    return img;
  }
}

14.10. Security Events

The following table describes a number of events (see Chapter 7, Events, interceptors and exception handling) raised by Seam Security in response to certain security-related events.

Table 14.13. Security Events

Event Key
Description
org.jboss.seam.security.loginSuccessful
Raised when a log in attempt is successful.
org.jboss.seam.security.loginFailed
Raised when a log in attempt fails.
org.jboss.seam.security.alreadyLoggedIn
Raised when a user that is already authenticated attempts to log in again.
org.jboss.seam.security.notLoggedIn
Raised when a security check fails when the user is not logged in.
org.jboss.seam.security.notAuthorized
Raised when a security check fails because the user is logged in, but does not have sufficient privileges.
org.jboss.seam.security.preAuthenticate
Raised just prior to user authentication.
org.jboss.seam.security.postAuthenticate
Raised just after user authentication.
org.jboss.seam.security.loggedOut
Raised after the user has logged out.
org.jboss.seam.security.credentialsUpdated
Raised when the user's credentials have been changed.
org.jboss.seam.security.rememberMe
Raised when the Identity's rememberMe property is changed.

14.11. Run As

Users sometimes need to perform certain operations with elevated privileges — for example, an unauthenticated user may need to create a new user account. Seam Security provides support in this situation with the RunAsOperation class. This class allows either the Principal or Subject, or the user's roles, to be overridden for a single set of operations.
The following code demonstrates RunAsOperation usage. The addRole() method is called to provide a set of roles to 'borrow' for the duration of the operation. The execute() method contains the code that will be executed with the elevated privileges.
new RunAsOperation() {       
  public void execute() {
    executePrivilegedOperation();
  }         
}.addRole("admin")
 .run();
Similarly, the getPrincipal() or getSubject() methods can also be overidden to specify the Principal and Subject instances to use for the duration of the operation. Finally, the run() method is used to carry out the RunAsOperation.

14.12. Extending the Identity component

If your application has special security requirements, you may need to extend your Identity component. The following example shows an Identity component extended with an additional companyCode field to handle credentials. (Usually this would be handled by a Credentials component.) The install precedence of APPLICATION ensures that this extended Identity is installed instead of the built-in Identity.
@Name("org.jboss.seam.security.identity")
@Scope(SESSION)
@Install(precedence = APPLICATION)
@BypassInterceptors
@Startup
public class CustomIdentity extends Identity {
  private static final LogProvider log = 
                       Logging.getLogProvider(CustomIdentity.class);

  private String companyCode;

  public String getCompanyCode() {
    return companyCode;
  }

  public void setCompanyCode(String companyCode) {
    this.companyCode = companyCode;
  }

  @Override
  public String login() {
    log.info("###### CUSTOM LOGIN CALLED ######");
    return super.login();
  }
}

Warning

An Identity component must be marked @Startup so that it is available immediately after the SESSION context begins. Failing to do this may render certain Seam functionality inoperable in your application.

14.13. OpenID

OpenID integration is a Technology Preview

Technology Preview features are not fully supported under Red Hat subscription level agreements (SLAs), may not be functionally complete, and are not intended for production use. However, these features provide early access to upcoming product innovations, enabling customers to test functionality and provide feedback during the development process. As Red Hat considers making future iterations of Technology Preview features generally available, we will provide commercially reasonable efforts to resolve any reported issues that customers experience when using these features.
OpenID is a community standard for external web-based authentication. Any web application can supplement (or replace) its local authentication handling by delegating responsibility to an external OpenID server selected by the user. This benefits both user and developer — the user (who no longer needs to remember login details for multiple web applications), and the developer (who need not maintain an entire complex authentication system).
When using OpenID, the user selects an OpenID provider, and the provider assigns the user an OpenID. The ID takes the form of a URL — http://maximoburrito.myopenid.com, for example. (The http:// portion of the identifier can be omitted when logging into a site.) The web application (known as a relying party) determines which OpenID server to contact and redirects the user to the remote site for authentication. When authentication succeeds, the user is given the (cryptographically secure) token proving his identity and is redirected back to the original web application. The local web application can then assume that the user accessing the application owns the OpenID presented.
However, authentication does not imply authorization. The web application must still determine how to treat the OpenID authentication. The web application can choose to treat the user as instantly logged in and grant full access to the system, or it can attempt to map the OpenID to a local user account and prompt unregistered users to register. This is a design decision for the local application.

14.13.1. Configuring OpenID

Seam uses the openid4java package, and requires four additional JARs to make use of Seam integration. These are htmlparser.jar, openid4java.jar, openxri-client.jar and openxri-syntax.jar.
OpenID processing requires the OpenIdPhaseListener, which should be added to your faces-config.xml file. The phase listener processes the callback from the OpenID provider, allowing re-entry into the local application.
<lifecycle> 
  <phase-listener>
    org.jboss.seam.security.openid.OpenIdPhaseListener
  </phase-listener> 
</lifecycle>
This configuration makes OpenID support available to your application. The OpenID support component, org.jboss.seam.security.openid.openid, is installed automatically if the openid4java classes are on the classpath.

14.13.2. Presenting an OpenIdLogin form

To initiate an OpenID log in, present a form to the user requesting the user's OpenID. The #{openid.id} value accepts the user's OpenID and the #{openid.login} action initiates an authentication request.
<h:form> 
  <h:inputText value="#{openid.id}" /> 
  <h:commandButton action="#{openid.login}" value="OpenID Login"/> 
</h:form>
When the user submits the login form, they are redirected to their OpenID provider. The user eventually returns to your application through the Seam pseudo-view /openid.xhtml, provided by the OpenIdPhaseListener. Your application can handle the OpenID response with a pages.xml navigation from that view, just as if the user had never left your application.

14.13.3. Logging in immediately

The simplest strategy is to simply log the user in immediately. The following navigation rule shows how to handle this using the #{openid.loginImmediately()} action.
<page view-id="/openid.xhtml">
  <navigation evaluate="#{openid.loginImmediately()}">
    <rule if-outcome="true">
      <redirect view-id="/main.xhtml">
        <message>OpenID login successful...</message>
      </redirect>
    </rule>
    <rule if-outcome="false">
      <redirect view-id="/main.xhtml">
        <message>OpenID login rejected...</message>
      </redirect>
    </rule>
  </navigation>
</page>
The loginImmediately() action checks whether the OpenID is valid. If it is valid, an OpenIdPrincipal is added to the identity component, and the user is marked as logged in (that is, #{identity.loggedIn} is marked true), and the loginImmediately() action returns true. If the OpenID is not validated, the method returns false, and the user re-enters the application un-authenticated. If the user's OpenID is valid, it will be accessible using the expression #{openid.validatedId} and #{openid.valid} will be true.

14.13.4. Deferring log in

If you do not want the user to be immediately logged in to your application, your navigation should check the #{openid.valid} property, and redirect the user to a local registration or processing page. Here, you can ask for more information and create a local user account, or present a CAPTCHA to avoid programmatic registrations. When your process is complete, you can log the user in by calling the loginImmediately method, either through EL (as shown previously), or direct interaction with the org.jboss.seam.security.openid.OpenId component. You can also write custom code to interact with the Seam identity component to create even more customized behavior.

14.13.5. Logging out

Logging out (forgetting an OpenID association) is done by calling #{openid.logout}. You can call this method directly if you are not using Seam Security. If you are using Seam Security, you should continue to use #{identity.logout} and install an event handler to capture the log out event, calling the OpenID log out method.
<event type="org.jboss.seam.security.loggedOut"> 
  <action execute="#{openid.logout}" /> 
</event>
It is important to include this, or the user will not be able to log in again in the same session.

Chapter 15. Internationalization, localization and themes

There are several stages required to internationalize and localize your application.

Note

Internationalization features are available only in the JSF context.

15.1. Internationalizing your application

A Java EE 5 application involves many components, all of which must be configured properly to localize your application.
Before you begin, ensure that your database server and client use the correct character encoding for your locale. Normally, you will want UTF-8 encoding. (Setting the correct encoding falls outside the scope of this tutorial.)

15.1.1. Application server configuration

To ensure that the application server receives the request parameters in the correct encoding from client requests you have to configure the tomcat connector. If you use JBoss Enterprise Application Platform, add the system properties org.apache.catalina.connector.URI_ENCODING and org.apache.catalina.connector.USE_BODY_ENCODING_FOR_QUERY_STRING to the server configuration. For JBoss Enterprise Application Platform change ${JBOSS_HOME}/standalone/configuration/standalone.xml:
        
<system-properties>
	<property name="org.apache.catalina.connector.URI_ENCODING" value="UTF-8"/>
	<property name="org.apache.catalina.connector.USE_BODY_ENCODING_FOR_QUERY_STRING" value="true"/>
</system-properties>

15.1.2. Translated application strings

You will also need localized strings for all of the messages in your application (for example, field labels on your views). First, ensure that your resource bundle is encoded with the desired character encoding. ASCII is used by default. Although ASCII is enough for many languages, it does not provide characters for all languages.
Resource bundles must either be created in ASCII, or use Unicode escape codes to represent Unicode characters. Since you do not compile a property file to byte code, there is no way to tell JVM which character set to use. Therefore, you must use either ASCII characters or escape characters not in the ASCII character set. You can represent a Unicode character in any Java file with \uXXXX, where XXXX is the hexadecimal representation of the character.
You can write your translation of labels (Section 15.3, “Labels”) to your message resource bundle in the native coding. The native2ascii tool provided in the JDK lets you convert the contents of a file written in your native encoding into one that represents non-ASCII characters as Unicode escape sequences.
Usage of this tool is described here for Java 6. For example, to convert a file from UTF-8:
 $ native2ascii -encoding UTF-8 messages_cs.properties > messages_cs_escaped.properties 

15.1.3. Other encoding settings

We need to make sure that the view displays your localized data and messages in the correct character set, and that any data submitted uses the correct encoding.
Use the <f:view locale="cs_CZ"/> tag to set the display character encoding. (Note that this locale value sets JSF to use the Czech locale.) If you want to embed localized strings in the XML, you may want to change the XML document's encoding. To do so, alter the encoding attribute value in the XML declaration <?xml version="1.0" encoding="UTF-8"?> .
JSF/Facelets should submit any requests with the specified character encoding, but to ensure that requests that do not specify an encoding are submitted, you can force the request encoding using a servlet filter. Configure this in components.xml:
<web:character-encoding-filter encoding="UTF-8" override-client="true" 
     url-pattern="*.seam" />

15.2. Locales

Each user log in session has an associated instance of java.util.Locale, which is available to the application as a component named locale. Under normal circumstances, setting the locale requires no special configuration. Seam delegates to JSF to determine the active locale as follows:
  • If a locale is associated with the HTTP request (the browser locale), and that locale is in the list of supported locales from faces-config.xml, use that locale for the rest of the session.
  • Otherwise, if a default locale was specified in the faces-config.xml, use that locale for the rest of the session.
  • Otherwise, use the default locale of the server.
You can set the locale manually through the Seam configuration properties org.jboss.seam.international.localeSelector.language, org.jboss.seam.international.localeSelector.country and org.jboss.seam.international.localeSelector.variant, but there is no good reason to use this method over those described above.
It is useful to allow the user to set the locale manually via the application user interface. Seam provides built-in functionality to override the locale determined by the default algorithm. Do this by adding the following fragment to a form in your JSP or Facelets page:
<h:selectOneMenu value="#{localeSelector.language}"> 
  <f:selectItem itemLabel="English" itemValue="en"/> 
  <f:selectItem itemLabel="Deutsch" itemValue="de"/> 
  <f:selectItem itemLabel="Francais" itemValue="fr"/> 
</h:selectOneMenu> 
<h:commandButton action="#{localeSelector.select}" 
                 value="#{messages['ChangeLanguage']}"/>
Or, if you want a list of all supported locales from faces-config.xml, use:
<h:selectOneMenu value="#{localeSelector.localeString}"> 
  <f:selectItems value="#{localeSelector.supportedLocales}"/> 
</h:selectOneMenu> 
<h:commandButton action="#{localeSelector.select}" 
                 value="#{messages['ChangeLanguage']}"/>
When the user selects an item from the drop-down, then clicks the command button, the Seam and JSF locales will be overridden for the rest of the session.
You can configure the supported locales and the default locale of the server with the built-in org.jboss.seam.international.localeConfig component. First, declare an XML namespace for Seam's international package in the Seam component descriptor. Then, define the default locale and supported locales as follows:
<international:locale-config default-locale="fr_CA" 
               supported-locales="en fr_CA fr_FR"/>
Remember that supported locales must have matching resource bundles. Next, define your language-specific labels.

15.3. Labels

JSF supports the internationalization of user interface labels and descriptive text with the <f:loadBundle /> . In Seam applications, you can either take this approach, or use the Seam messages component to display templated labels with embedded EL expressions.

15.3.1. Defining labels

Make your internationalized labels available with Seam's java.util.ResourceBundle, available to the application as a org.jboss.seam.core.resourceBundle. By default, Seam uses a resource bundle named messages, so you will need to define your labels in files named messages.properties, messages_en.properties, messages_en_AU.properties, etc. These files usually belong in the WEB-INF/classes directory.
So, in messages_en.properties:
Hello=Hello
And in messages_en_AU.properties:
Hello=G'day
You can select a different name for the resource bundle by setting the Seam configuration property named org.jboss.seam.core.resourceLoader.bundleNames. You can even specify a list of resource bundle names to be searched (depth first) for messages.
<core:resource-loader> 
  <core:bundle-names> 
    <value>mycompany_messages</value> 
    <value>standard_messages</value>       
  </core:bundle-names> 
</core:resource-loader>
To define a message for one particular page, specify it in a resource bundle with the same name as the JSF view ID, with the leading / and trailing file extension removed. So, we could put our message in welcome/hello_en.properties if we only needed to display the message on /welcome/hello.xhtml.
You can even specify an explicit bundle name in pages.xml:
<page view-id="/welcome/hello.xhtml" bundle="HelloMessages"/>
Then we could use messages defined in HelloMessages.properties on /welcome/hello.xhtml.

15.3.2. Displaying labels

If you define your labels with the Seam resource bundle, you can use them without having to type <f:loadBundle... /> on each page. Instead, you can type:
<h:outputText value="#{messages['Hello']}"/>
or:
<h:outputText value="#{messages.Hello}"/>
Even better, the messages themselves may contain EL expressions:
Hello=Hello, #{user.firstName} #{user.lastName}
Hello=G'day, #{user.firstName}
You can even use the messages in your code:
@In private Map<String, String> messages;
@In("#{messages['Hello']}") private String helloMessage;

15.3.3. Faces messages

The facesMessages component is a convenient way to display success or failure messages to the user. The functionality we just described also works for Faces messages:
@Name("hello") 
@Stateless 
public class HelloBean implements Hello { 
  @In FacesMessages facesMessages; 
  public String sayIt() { 
    facesMessages.addFromResourceBundle("Hello"); 
  } 
}
This will display Hello, Gavin King or G'day, Gavin, depending upon the user's locale.

15.4. Timezones

There is also a session-scoped instance of java.util.Timezone, named org.jboss.seam.international.timezone, and a Seam component for changing the timezone named org.jboss.seam.international.timezoneSelector. By default, the timezone is the default timezone of the server. Unfortunately, the JSF specification assumes all dates and times are UTC, and displayed as UTC, unless a different timezone is explicitly specified with <f:convertDateTime> .
Seam overrides this behavior, and defaults all dates and times to the Seam timezone. In addition, Seam provides the <s:convertDateTime> tag, which always performs conversions in the Seam timezone.
Seam also provides a default date converter to convert a string value to a date. This saves you from having to specify a converter on input fields that capture dates. The pattern is selected according to the user's locale and the time zone is selected as described above.

15.5. Themes

Seam applications are also very easily skinnable. The theme API is very similar to the localization API, but of course these two concerns are orthogonal, and some applications support both localization and themes.
First, configure the set of supported themes:
<theme:theme-selector cookie-enabled="true">
  <theme:available-themes>
    <value>default</value>
    <value>accessible</value>
    <value>printable</value>
  </theme:available-themes>
</theme:theme-selector>
The first theme listed is the default theme.
Themes are defined in a properties file with the same name as the theme. For example, the default theme is defined as a set of entries in default.properties, which might define:
css ../screen.css template /template.xhtml
The entries in a theme resource bundle are usually paths to CSS styles or images and names of Facelets templates (unlike localization resource bundles which are usually text).
Now we can use these entries in our JSF or Facelets pages. For example, to theme the stylesheet in a Facelets page:
<link href="#{theme.css}" rel="stylesheet" type="text/css" />
Or, where the page definition resides in a subdirectory:
<link href="#{facesContext.externalContext.requestContextPath}#{theme.css}" 
      rel="stylesheet" type="text/css" />
Most powerfully, Facelets lets us theme the template used by a <ui:composition> :
<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"
    template="#{theme.template}">
Just like the locale selector, there is a built-in theme selector to allow the user to freely switch themes:
<h:selectOneMenu value="#{themeSelector.theme}"> 
  <f:selectItems value="#{themeSelector.themes}"/> 
</h:selectOneMenu> 
<h:commandButton action="#{themeSelector.select}" value="Select Theme"/>

15.6. Persisting locale and theme preferences via cookies

The locale selector, theme selector and timezone selector all support persistence of locale and theme preference to a cookie. Simply set the cookie-enabled property in components.xml:
<theme:theme-selector cookie-enabled="true">
  <theme:available-themes>
    <value>default</value>
    <value>accessible</value>
    <value>printable</value>
  </theme:available-themes>
</theme:theme-selector>

<international:locale-selector cookie-enabled="true"/>

Chapter 16. Seam Text

Collaboration-oriented websites require a human-friendly markup language so that formatted text can be entered easily in forum posts, wiki pages, blogs, comments, etc. Seam provides the <s:formattedText/> control to display formatted text that conforms to the Seam Text language. Seam Text is implemented with an ANTLR-based parser. (Experience with ANTLR is not required.)

16.1. Basic formatting

Here is a simple example: It's easy to make *emphasized*, |monospaced|, ~deleted~, super^scripted^ or _underlined_ text.
If we display this using <s:formattedText/> , the following HTML will be produced:
<p> 
  It's easy to make <i>emphasized</i>, <tt>monospaced</tt>, 
  <del>deleted</del>, super<sup>scripted</sup> or 
  <u>underlined</u> text.
</p>
We can use a blank line to indicate a new paragraph, and + to indicate a heading:
        +This is a big heading
        You /must/ have some text following a heading!
        
        ++This is a smaller heading
        This is the first paragraph. We can split it across multiple 
        lines, but we must end it with a blank line.

        This is the second paragraph.
A simple new line is ignored — you need an additional blank line to wrap text into a new paragraph. This is the HTML that results:
<h1>This is a big heading</h1>
<p>
  You <i>must</i> have some text following a heading!
</p>
 
<h2>This is a smaller heading</h2>
<p>
  This is the first paragraph. We can split it across multiple 
  lines, but we must end it with a blank line.
</p>

<p>
  This is the second paragraph.
</p>
The # character creates items in an ordered list. Unordered lists use the = character:
        An ordered list:
        
        #first item
        #second item
        #and even the /third/ item

        An unordered list:

        =an item
        =another item
<p>
  An ordered list:
</p>
 
<ol>       
  <li>first item</li>
  <li>second item</li>
  <li>and even the <i>third</i> item</li>
</ol>

<p>
  An unordered list:
</p>

<ul>
  <li>an item</li>
  <li>another item</li>
</ul>
Quoted sections should be surrounded in double quotes:
        He said:
        
        "Hello, how are /you/?"

        She answered, "Fine, and you?"
<p>
  He said:
</p>
        
<q>Hi, how are
<i>you</i>?</q>

<p>
  She answered, <q>Fine, and you?</q>
</p>

16.2. Entering code and text with special characters

Special characters such as *, | and #, and HTML characters such as <, > and & can be escaped with \:
You can write down equations like 2\*3\=6 and HTML tags like \<body\> using the escape character: \\.
<p> 
  You can write down equations like 2*3=6 and HTML tags
  like &lt;body&gt; using the escape character: \. 
</p>
And we can quote code blocks with backticks:
My code does not work:
  `for (int i=0; i<100; i--)
       {
       doSomething();
       }`
Any ideas?
<p>
  My code does not work:
</p>

<pre>for (int i=0; i&lt;100; i--)
{
    doSomething();
}</pre>

<p>
  Any ideas?
</p>
Since most monospace-formatted text is either code, or involves special characters, inline monospace formatting always escapes. So, you can write:
This is a |&lt;tag attribute="value"/&gt;| example.
without escaping any of the characters inside the monospace bars. This also means that inline monospace text cannot be formatted in any other way.

16.3. Links

You can create a link like so:
Go to the Seam website at [=>http://jboss.com/products/seam].
If you want to specify the link text:
Go to [the Seam website=>http://jboss.com/products/seam].
For advanced users, you can also customize the Seam Text parser to understand wikiword links written in this syntax.

16.4. Entering HTML

Text can include a certain limited subset of HTML. (The subset was selected to remain safe from cross-site scripting attacks.) This is useful for creating links:
You might want to link to 
      <a href="http://jboss.com/products/seam">something cool</a>, 
      or even include an image: <img src="/logo.jpg"/>
And for creating tables:
<table> 
   <tr><td>First name:</td><td>Gavin</td></tr>
   <tr><td>Last name:</td><td>King</td></tr>
</table>

16.5. Using the SeamTextParser

The <s:formattedText/> JSF component uses the org.jboss.seam.text.SeamTextParser internally. You can use this class directly to implement your own text parsing, rendering, and HTML sanitation procedures. If you have a custom front-end interface for entering rich text, such as a JavaScript-based HTML editor, this can be useful for validating user input in order to defend against Cross-Site Scripting (XSS) attacks. You could also use it as a custom Wiki text-parsing and rendering engine.
The following example defines a custom text parser, which overrides the default HTML sanitizer:
public class MyTextParser extends SeamTextParser {

    public MyTextParser(String myText) {
        super(new SeamTextLexer(new StringReader(myText)));

        setSanitizer(
                     new DefaultSanitizer() {
                         @Override
                         public void validateHtmlElement(Token element) throws SemanticException {
                             // TODO: I want to validate HTML elements myself!
                         }
                     }
                     );
    }

    // Customizes rendering of Seam text links such as [Some Text=&gt;http://example.com]
    @Override
    protected String linkTag(String descriptionText, String linkText) {
        return "&lt;a href=\"" + linkText + "\"&gt;My Custom Link: " + 
            descriptionText + "&lt;/a&gt;";
    }

    // Renders a &lt;p&gt; or equivalent tag
    @Override
    protected String paragraphOpenTag() {
        return "&lt;p class=\"myCustomStyle\"&gt;";
    }

    public void parse() throws ANTLRException {
        startRule();
    }
    
}
linkTag() and paragraphOpenTag() methods are two of the methods you can override in order to customize rendered output. These methods usually return String output. For further details, refer to the Java Documentation. The org.jboss.seam.text.SeamTextParser.DefaultSanitizer Java Documentation also contains more information about the HTML elements, attributes, and attribute values that are filtered by default.

Chapter 17. iText PDF generation

Seam now includes a component set for generating documents with iText. The primary focus of Seam's iText document support is for the generation of PDF documents, but Seam also offers basic support for RTF document generation.

17.1. Using PDF Support

iText support is provided by jboss-seam-pdf.jar. This JAR contains the iText JSF controls (which construct views that can render to PDF) and the DocumentStore component (which serves the rendered documents to the user). To include PDF support in your application, place jboss-seam-pdf.jar in your WEB-INF/lib directory along with the iText JAR file. No further configuration is required to use Seam's iText support.
The Seam iText module requires that Facelets be used as the view technology. It also requires the use of the seam-ui package.
The examples/itext project contains an example of the PDF support in action. It demonstrates proper deployment packaging, and contains several examples demonstrating the key PDF-generation features currently supported.

17.1.1. Creating a document

<p:document>
Description
Documents are generated by Facelet XHTML files using tags in the http://jboss.org/schema/seam/pdf namespace. Documents should always have the document tag at the root of the document. The document tag prepares Seam to generate a document into the DocumentStore and renders an HTML redirect to that stored content.
Attributes
type
The type of the document to be produced. Valid values are PDF, RTF and HTML. Seam defaults to PDF generation, and many features only work correctly when generating PDF documents.
pageSize
The size of the page to be generated. The most commonly used values are LETTER and A4. A full list of supported pages sizes can be found in the com.lowagie.text.PageSize class. Alternatively, pageSize can provide the width and height of the page directly. The value "612 792", for example, is equivalent to the LETTER page size.
orientation
The orientation of the page. Valid values are portrait and landscape. Landscape mode swaps the height and width page values.
margins
The left, right, top and bottom margin values.
marginMirroring
Indicates that margin settings should be reversed on alternating pages.
disposition
When generating PDFs in a web browser, this determines the HTTP Content-Disposition of the document. Valid values are inline, which indicates the document should be displayed in the browser window if possible, and attachment, which indicates that the document should be treated as a download. The default value is inline.
fileName
For attachments, this value overrides the downloaded file name.
Metadata Attributes
  • title
  • subject
  • keywords
  • author
  • creator
Usage
       <p:document xmlns:p="http://jboss.org/schema/seam/pdf"> 
  The document goes here. 
</p:document>

17.1.2. Basic Text Elements

Seam provides special UI components for generating content suitable to PDF. <p:image> and <p:paragraph> tags form the foundations of simple documents. Tags like <p:font> provide style information.
<p:paragraph>
Description
For most purposes, text should be sectioned into paragraphs so that text fragments can be assigned a logical flow, format and style.
Attributes
  • firstLineIndent
  • extraParagraphSpace
  • leading
  • multipliedLeading
  • spacingBefore — The blank space to be inserted before the element.
  • spacingAfter — The blank space to be inserted after the element.
  • indentationLeft
  • indentationRight
  • keepTogether
Usage
<p:paragraph alignment="justify">
  This is a simple document. It is not very fancy. 
</p:paragraph>
<p:text>
Description
The text tag lets application data produce text fragments using normal JSF converter mechanisms. It is very similar to the outputText tag used when rendering HTML documents.
Attributes
  • value — The value to be displayed. This is typically a value binding expression.
Usage
<p:paragraph> 
  The item costs <p:text value="#{product.price}"> 
    <f:convertNumber type="currency" 
                     currencySymbol="$"/> 
  </p:text> 
</p:paragraph>
<p:html>
Description
The html tag renders HTML content into the PDF.
Attributes
  • value — The text to be displayed.
Usage
<p:html value="This is HTML with <b>some markup</b>" />
<p:html>
  <h1>This is more complex HTML</h1>
  <ul>
    <li>one</li>
    <li>two</li>
    <li>three</li>
  </ul>
</p:html>

<p:html>
    <s:formattedText value="*This* is |Seam Text| as 
                            HTML.  
        It's very^cool^." />
</p:html>                                    

<p:font>
Description
The font tag defines the default font to be used for all text inside of it.
Attributes
  • name — The font name, for example: COURIER, HELVETICA, TIMES-ROMAN, SYMBOL or ZAPFDINGBATS.
  • size — The point size of the font.
  • style — The font styles. Any combination of : NORMAL, BOLD, ITALIC, OBLIQUE, UNDERLINE, LINE-THROUGH
  • encoding — The character set encoding.
Usage
<p:font name="courier" style="bold" size="24"> 
  <p:paragraph>My Title</p:paragraph> 
</p:font>
<p:textcolumn>
Description
p:textcolumn inserts a text column that can be used to control the flow of text. The most common case is to support right to left direction fonts.
Attributes
  • left — The left bounds of the text column
  • right — The right bounds of the text column
  • direction — The run direction of the text in the column: RTL, LTR, NO-BIDI, DEFAULT
Usage
<p:textcolumn left="400" right="600" direction="rtl"> 
<p:font name="/Library/Fonts/Arial Unicode.ttf" 
encoding="Identity-H" 
embedded="true">#{phrases.arabic}</p:font> 
</p:textcolumn>
<p:newPage>
Description
p:newPage inserts a page break.
Usage
<p:newPage />
<p:image>
Description
p:image inserts an image into the document. Images can be loaded from the classpath or from the web application context using the value attribute.
Resources can also be dynamically generated by application code. The imageData attribute can specify a value binding expression whose value is a java.awt.Image object.
Attributes
  • value — A resource name or a method expression that binds to an application-generated image.
  • rotation — The rotation of the image in degrees.
  • height — The height of the image.
  • width — The width of the image.
  • alignment — The alignment of the image. (See Section 17.1.7.2, “Alignment Values” for possible values.)
  • alt — Alternative text representation for the image.
  • indentationLeft
  • indentationRight
  • spacingBefore — The blank space to be inserted before the element.
  • spacingAfter — The blank space to be inserted after the element.
  • widthPercentage
  • initialRotation
  • dpi
  • scalePercent — The scaling factor (as a percentage) to use for the image. This can be expressed as a single percentage value or as two percentage values representing separate x and y scaling percentages.
  • wrap
  • underlying
Usage
<p:image value="/jboss.jpg" />
<p:image value="#{images.chart}" />
<p:anchor>
Description
p:anchor defines clickable links from a document. It supports the following attributes:
Attributes
  • name — The name of an in-document anchor destination.
  • reference — The destination the link refers to. Links to other points in the document should begin with a "#". For example, "#link1" to refer to an anchor position with a name of link1. To point to a resource outside the document, links must be a complete URL.
Usage
<p:listItem>
  <p:anchor reference="#reason1">Reason 1</p:anchor>
</p:listItem> 
... 
<p:paragraph> 
  <p:anchor name="reason1">
    It's the quickest way to get "rich"
  </p:anchor> 
  ... 
</p:paragraph>

17.1.3. Headers and Footers

<p:header>
<p:footer>
Description
The p:header and p:footer components let you place header and footer text on each page of a generated document. Header and footer declarations should appear at the beginning of a document.
Attributes
  • alignment — The alignment of the header/footer box section. (See Section 17.1.7.2, “Alignment Values” for alignment values.)
  • backgroundColor — The background color of the header/footer box. (See Section 17.1.7.1, “Color Values” for color values.)
  • borderColor — The border color of the header/footer box. Individual border sides can be set using borderColorLeft, borderColorRight, borderColorTop and borderColorBottom. (See Section 17.1.7.1, “Color Values” for color values.)
  • borderWidth — The width of the border. Individual border sides can be specified using borderWidthLeft, borderWidthRight, borderWidthTop and borderWidthBottom.
Usage
<p:facet name="header"> 
  <p:font size="12"> 
    <p:footer borderWidthTop="1" borderColorTop="blue" 
       borderWidthBottom="0" alignment="center">
       Why Seam? [<p:pageNumber />] 
    </p:footer> 
  </p:font> 
</f:facet>
<p:pageNumber>
Description
The current page number can be placed inside a header or footer with the p:pageNumber tag. The page number tag can only be used in the context of a header or footer and can only be used once.
Usage
<p:footer borderWidthTop="1" borderColorTop="blue" 
   borderWidthBottom="0" alignment="center"> 
  Why Seam? [<p:pageNumber />] 
</p:footer>

17.1.4. Chapters and Sections

<p:chapter>
<p:section>
Description
If the generated document follows a book/article structure, the p:chapter and p:section tags can be used to provide structure. Sections can only be used inside chapters, but they may be nested to any depth required. Most PDF viewers provide easy navigation between chapters and sections in a document.
Attributes
  • alignment — The alignment of the header/footer box section. (See Section 17.1.7.2, “Alignment Values” for alignment values.)
  • number — The chapter number. Every chapter should be assigned a chapter number.
  • numberDepth — The depth of numbering for section. All sections are numbered relative to their surrounding chapters/sections. The fourth section of the first section of chapter three would be section 3.1.4, if displayed at the default number depth of 3. To omit the chapter number, a number depth of 2 should be used — this would display the section number as 1.4.
Usage
                            <p:document xmlns:p="http://jboss.org/schema/seam/pdf" title="Hello">
  <p:chapter number="1">
    <p:title><p:paragraph>Hello</p:paragraph></p:title>
    <p:paragraph>Hello #{user.name}!</p:paragraph>
  </p:chapter>

  <p:chapter number="2">
    <p:title>
      <p:paragraph>
        Goodbye
      </p:paragraph>
    </p:title>
    <p:paragraph>Goodbye #{user.name}.</p:paragraph>
  </p:chapter>

</p:document> 
<p:header>
Description
Any chapter or section can contain a p:title. The title will be displayed next to the chapter or section number. The body of the title may contain raw text or may be a p:paragraph.

17.1.5. Lists

List structures can be displayed with the p:list and p:listItem tags. Lists may contain arbitrarily-nested sublists. List items may not be used outside of a list. The following document uses the ui:repeat tag to display a list of values retrieved from a Seam component.
             <p:document xmlns:p="http://jboss.org/schema/seam/pdf" 
   xmlns:ui="http://java.sun.com/jsf/facelets" title="Hello">
    
  <p:list style="numbered"> 
    <ui:repeat value="#{documents}" var="doc">
      <p:listItem>#{doc.name}</p:listItem> 
    </ui:repeat> 
  </p:list>
   
</p:document>
<p:list>
Attributes
  • style — The ordering/bulleting style of the list. One of: NUMBERED, LETTERED, GREEK, ROMAN, ZAPFDINGBATS, ZAPFDINGBATS_NUMBER. If no style is given, the list items are bulleted by default.
  • listSymbol — For bulleted lists, specifies the bullet symbol.
  • indent — The indentation level of the list.
  • lowerCase — For list styles using letters, indicates whether the letters should be lower case.
  • charNumber — For ZAPFDINGBATS, indicates the character code of the bullet character.
  • numberType — For ZAPFDINGBATS_NUMBER, indicates the numbering style.
Usage
<p:list style="numbered"> 
  <ui:repeat value="#{documents}" var="doc">
    <p:listItem>#{doc.name}</p:listItem> 
  </ui:repeat> 
</p:list>
<p:listItem>
Description
p:listItem supports the following attributes:
Attributes
  • alignment — The alignment of the header/footer box section. (See Section 17.1.7.2, “Alignment Values” for alignment values.)
  • alignment — The alignment of the list item. (See Section 17.1.7.2, “Alignment Values” for possible values.)
  • indentationLeft — The left indentation amount.
  • indentationRight — The right indentation amount.
  • listSymbol — Overrides the default list symbol for this list item.
Usage
...

17.1.6. Tables

Table structures can be created using the p:table and p:cell tags. Unlike many table structures, there is no explicit row declaration. If a table has three columns, then every three cells will automatically form a row. Header and footer rows can be declared, and the headers and footers will be repeated in the event a table structure spans multiple pages.
<p:table>
Description
p:table supports the following attributes.
Attributes
  • columns — The number of columns (cells) that make up a table row.
  • widths — The relative widths of each column. There should be one value for each column. For example: widths="2 1 1" would indicate that there are three columns and the first column should be twice the size of the second and third column.
  • headerRows — The initial number of rows considered to be header and footer rows, which should be repeated if the table spans multiple pages.
  • footerRows — The number of rows considered to be footer rows. This value is subtracted from the headerRows value. If document has two rows which make up the header and one row that makes up the footer, headerRows should be set to 3 and footerRows should be set to 1.
  • widthPercentage — The percentage of the page width spanned by the table.
  • horizontalAlignment — The horizontal alignment of the table. (See Section 17.1.7.2, “Alignment Values” for possible values.)
  • skipFirstHeader
  • runDirection
  • lockedWidth
  • splitRows
  • spacingBefore — The blank space to be inserted before the element.
  • spacingAfter — The blank space to be inserted after the element.
  • extendLastRow
  • headersInEvent
  • splitLate
  • keepTogether
Usage
<p:table columns="3" headerRows="1">
  <p:cell>name</p:cell>
  <p:cell>owner</p:cell>
  <p:cell>size</p:cell>
  <ui:repeat value="#{documents}" var="doc">
    <p:cell>#{doc.name}</p:cell>
    <p:cell>#{doc.user.name}</p:cell>
    <p:cell>#{doc.size}</p:cell>
  </ui:repeat>
</p:table>
<p:cell>
Description
p:cell supports the following attributes:
Attributes
  • colspan — Cells can span more than one column by declaring a colspan greater than one. Cells cannot span multiple rows.
  • horizontalAlignment — The horizontal alignment of the cell. (See Section 17.1.7.2, “Alignment Values” for possible values.)
  • verticalAlignment — The vertical alignment of the cell. (See Section 17.1.7.2, “Alignment Values” for possible values.)
  • padding — Specify padding on a particular side using paddingLeft, paddingRight, paddingTop and paddingBottom.
  • useBorderPadding
  • leading
  • multipliedLeading
  • indent
  • verticalAlignment
  • extraParagraphSpace
  • fixedHeight
  • noWrap
  • minimumHeight
  • followingIndent
  • rightIndent
  • spaceCharRatio
  • runDirection
  • arabicOptions
  • useAscender
  • grayFill
  • rotation
Usage
<p:cell>...</p:cell>

17.1.7. Document Constants

This section documents some of the constants shared by attributes on multiple tags.

17.1.7.1. Color Values

Seam documents do not yet support a full color specification. Currently, only named colors are supported. They are: white, gray, lightgray, darkgray, black, red, pink, yellow, green, magenta, cyan and blue.

17.1.7.2. Alignment Values

Seam PDF supports the following horizontal alignment values: left, right, center, justify and justifyall. The vertical alignment values are top, middle, bottom, and baseline.

17.2. Charting

Charting support is also provided with jboss-seam-pdf.jar. Charts can be used in PDF documents, or as images in an HTML page. To use charting, you will need to add the JFreeChart library (jfreechart.jar and jcommon.jar) to the WEB-INF/lib directory. Three types of charts are currently supported: pie charts, bar charts and line charts.
<p:chart>
Description
Displays a chart already created in Java by a Seam component.
Attributes
  • chart -- The chart object to display
  • height -- The height fo the chart
  • width -- The width of the chart
Usage
<p:chart chart="#{mycomponent.chart}" width="500" height="500" />



<p:barchart>
Description
Displays a bar chart.
Attributes
  • borderVisible — Controls whether or not a border is displayed around the entire chart.
  • borderPaint — The color of the border, if visible.
  • borderBackgroundPaint — The default background color of the chart.
  • borderStroke
  • domainAxisLabel — The text label for the domain axis.
  • domainLabelPosition — The angle of the domain axis category labels. Valid values are STANDARD, UP_45, UP_90, DOWN_45 and DOWN_90. The value can also be a positive or negative angle in radians.
  • domainAxisPaint — The color of the domain axis label.
  • domainGridlinesVisible— Controls whether or not gridlines for the domain axis are shown on the chart.
  • domainGridlinePaint— The color of the domain gridlines, if visible.
  • domainGridlineStroke — The stroke style of the domain gridlines, if visible.
  • height — The height of the chart.
  • width — The width of the chart.
  • is3D — A Boolean value indicating that the chart should be rendered in 3D instead of 2D.
  • legend — A Boolean value indicating whether or not the chart should include a legend.
  • legendItemPaint— The default color of the text labels in the legend.
  • legendItemBackgoundPaint— The background color for the legend, if different from the chart background color.
  • legendOutlinePaint— The color of the border around the legend.
  • orientation — The orientation of the plot, either vertical (the default) or horizontal.
  • plotBackgroundPaint — The color of the plot background.
  • plotBackgroundAlpha — The alpha (transparency) level of the plot background. This should be a number between 0 (completely transparent) and 1 (completely opaque).
  • plotForegroundAlpha — The alpha (transparency) level of the plot. This should be a number between 0 (completely transparent) and 1 (completely opaque).
  • plotOutlinePaint — The color of the range gridlines, if visible.
  • plotOutlineStroke — The stroke style of the range gridlines, if visible.
  • rangeAxisLabel — The text label for the range axis.
  • rangeAxisPaint — The color of the range axis label.
  • rangeGridlinesVisible — Controls whether or not gridlines for the range axis are shown on the chart.
  • rangeGridlinePaint — The color of the range gridlines, if visible.
  • rangeGridlineStroke — The stroke style of the range gridlines, if visible.
  • title — The chart title text.
  • titlePaint — The color of the chart title text.
  • titleBackgroundPaint — The background color around the chart title.
  • width — The width of the chart.
Usage
<p:barchart title="Bar Chart" legend="true" width="500" height="500">
  <p:series key="Last Year">
    <p:data columnKey="Joe" value="100" />
    <p:data columnKey="Bob" value="120" />
  </p:series>        
  <p:series key="This Year">
    <p:data columnKey="Joe" value="125" />
    <p:data columnKey="Bob" value="115" />
  </p:series>
</p:barchart>
<p:linechart>
Description
Displays a line chart.
Attributes
  • borderVisible — Controls whether or not a border is displayed around the entire chart.
  • borderPaint — The color of the border, if visible.
  • borderBackgroundPaint — The default background color of the chart.
  • borderStroke
  • domainAxisLabel — The text label for the domain axis.
  • domainLabelPosition — The angle of the domain axis category labels. Valid values are STANDARD, UP_45, UP_90, DOWN_45 and DOWN_90. The value can also be a positive or negative angle in radians.
  • domainAxisPaint — The color of the domain axis label.
  • domainGridlinesVisible— Controls whether or not gridlines for the domain axis are shown on the chart.
  • domainGridlinePaint— The color of the domain gridlines, if visible.
  • domainGridlineStroke — The stroke style of the domain gridlines, if visible.
  • height — The height of the chart.
  • width — The width of the chart.
  • is3D — A Boolean value indicating that the chart should be rendered in 3D instead of 2D.
  • legend — A Boolean value indicating whether or not the chart should include a legend.
  • legendItemPaint — The default color of the text labels in the legend.
  • legendItemBackgoundPaint — The background color for the legend, if different from the chart background color.
  • legendOutlinePaint — The color of the border around the legend.
  • orientation — The orientation of the plot, either vertical (the default) or horizontal.
  • plotBackgroundPaint — The color of the plot background.
  • plotBackgroundAlpha — The alpha (transparency) level of the plot background. It should be a number between 0 (completely transparent) and 1 (completely opaque).
  • plotForegroundAlpha — The alpha (transparency) level of the plot. It should be a number between 0 (completely transparent) and 1 (completely opaque).
  • plotOutlinePaint — The color of the range gridlines, if visible.
  • plotOutlineStroke — The stroke style of the range gridlines, if visible.
  • rangeAxisLabel — The text label for the range axis.
  • rangeAxisPaint — The color of the range axis label.
  • rangeGridlinesVisible — Controls whether or not gridlines for the range axis are shown on the chart.
  • rangeGridlinePaint — The color of the range gridlines, if visible.
  • rangeGridlineStroke — The stroke style of the range gridlines, if visible.
  • title — The chart title text.
  • titlePaint — The color of the chart title text.
  • titleBackgroundPaint — The background color around the chart title.
  • width — The width of the chart.
Usage
<p:linechart title="Line Chart" width="500" height="500">
  <p:series key="Prices">
    <p:data columnKey="2003" value="7.36" />
    <p:data columnKey="2004" value="11.50" />
    <p:data columnKey="2005" value="34.625" />
    <p:data columnKey="2006" value="76.30" />
    <p:data columnKey="2007" value="85.05" />
  </p:series>
</p:linechart>
<p:piechart>
Description
Displays a pie chart.
Attributes
  • title — The chart title text.
  • label — The default label text for pie sections.
  • legend — A Boolean value indicating whether or not the chart should include a legend. Default value is true.
  • is3D — A Boolean value indicating that the chart should be rendered in 3D instead of 2D.
  • labelLinkMargin — The link margin for labels.
  • labelLinkPaint — The paint used for the label linking lines.
  • labelLinkStroke — The stroke used for the label linking lines.
  • labelLinksVisible — A flag that controls whether or not the label links are drawn.
  • labelOutlinePaint — The paint used to draw the outline of the section labels.
  • labelOutlineStroke — The stroke used to draw the outline of the section labels.
  • labelShadowPaint — The paint used to draw the shadow for the section labels.
  • labelPaint — The color used to draw the section labels.
  • labelGap — The gap between the labels and the plot, as a percentage of the plot width.
  • labelBackgroundPaint — The color used to draw the background of the section labels. If this is null, the background is not filled.
  • startAngle — The starting angle of the first section.
  • circular — A Boolean value indicating that the chart should be drawn as a circle. If false, the chart is drawn as an ellipse. The default is true.
  • direction — The direction in which the pie sections are drawn. One of: clockwise or anticlockwise. The default is clockwise.
  • sectionOutlinePaint — The outline paint for all sections.
  • sectionOutlineStroke — The outline stroke for all sections.
  • sectionOutlinesVisible — Indicates whether an outline is drawn for each section in the plot.
  • baseSectionOutlinePaint — The base section outline paint.
  • baseSectionPaint — The base section paint.
  • baseSectionOutlineStroke — The base section outline stroke.
Usage
<p:piechart title="Pie Chart" circular="false" 
            direction="anticlockwise" startAngle="30" 
            labelGap="0.1" labelLinkPaint="red">    
  <p:series key="Prices"> 
    <p:data key="2003" columnKey="2003" value="7.36" /> 
    <p:data key="2004" columnKey="2004" value="11.50" /> 
    <p:data key="2005" columnKey="2005" value="34.625" /> 
    <p:data key="2006" columnKey="2006" value="76.30" /> 
    <p:data key="2007" columnKey="2007" value="85.05" /> 
  </p:series> 
</p:piechart>
<p:series>
Description
Category data can be broken down into series. The series tag is used to categorize a data set with a series and apply styling to the entire series.
Attributes
  • key — The series name.
  • seriesPaint — The color of each item in the series.
  • seriesOutlinePaint — The outline color for each item in the series.
  • seriesOutlineStroke — The stroke used to draw each item in the series.
  • seriesVisible — A Boolean indicating if the series should be displayed.
  • seriesVisibleInLegend — A Boolean indicating whether the series should be listed in the legend.
Usage
<p:series key="data1"> 
  <ui:repeat value="#{data.pieData1}" var="item"> 
    <p:data columnKey="#{item.name}" 
            value="#{item.value}" /> 
  </ui:repeat> 
</p:series>
<p:data>
Description
The data tag describes each data point to be displayed in the graph.
Attributes
  • key — The name of the data item.
  • series — The series name, when not embedded inside a <p:series> .
  • value — The numeric data value.
  • explodedPercent — For pie charts, indicates how exploded from the pie a piece is.
  • sectionOutlinePaint — For bar charts, the color of the section outline.
  • sectionOutlineStroke — For bar charts, the stroke type for the section outline.
  • sectionPaint — For bar charts, the color of the section.
Usage
<p:data key="foo" value="20" sectionPaint="#111111" explodedPercent=".2" /> 
<p:data key="bar" value="30" sectionPaint="#333333" /> 
<p:data key="baz" value="40" sectionPaint="#555555" 
   sectionOutlineStroke="my-dot-style" />
<p:color>
Description
The color component declares a color or gradient for filled shapes.
Attributes
  • color — The color value. For gradient colors, this indicates the starting color. See Section 17.1.7.1, “Color Values” for color values.
  • color2 — For gradient colors, this is the color that ends the gradient.
  • point — The coordinates that mark the beginning of the gradient color.
  • point2 — The coordinates that mark the end of the gradient color.
Usage
<p:color id="foo" color="#0ff00f"/> 
<p:color id="bar" color="#ff00ff" color2="#00ff00" 
   point="50 50" point2="300 300"/>
<p:stroke>
Description
Describes a stroke used to draw lines in a chart.
Attributes
  • width — The width of the stroke.
  • cap — The line cap type. Valid values are butt, round and square
  • join — The line join type. Valid values are miter, round and bevel
  • miterLimit — For miter joins, this value is the limit of the size of the join.
  • dash — The dash value sets the dash pattern used to draw the line. Use space-separated integers to indicate the length of each alternating drawn and undrawn segment.
  • dashPhase — The dash phase indicates the point in the dash pattern that corresponds to the beginning of the stroke.
Usage
<p:stroke id="dot2" width="2" cap="round" join="bevel" dash="2 3" />

17.3. Bar codes

Seam can use iText to generate barcodes in a wide variety of formats. These barcodes can be embedded in a PDF document or displayed as an image on a web page. However, barcodes cannot currently display barcode text when used with HTML images.
<p:barCode>
Description
Displays a barcode image.
Attributes
  • type — A barcode type supported by iText. Valid values include: EAN13, EAN8, UPCA, UPCE, SUPP2, SUPP5, POSTNET, PLANET, CODE128, CODE128_UCC, CODE128_RAW and CODABAR.
  • code — The value to be encoded by the barcode.
  • xpos — For PDFs, the absolute x position of the barcode on the page.
  • ypos — For PDFs, the absolute y position of the barcode on the page.
  • rotDegrees — For PDFs, the rotation factor of the barcode in degrees.