Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide

Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Red Hat JBoss Web Framework
Kit 2.3
Seam Reference Guide
for use with Red Hat JBoss Enterprise Application Platform
Edition 2.3.0
Gavin King
Shane Bryzak
Christian Bauer
Max Andersen
Daniel Roth
Marek Novotny
Pete Muir
Michael Yuan
Jay Balunas
Emmanuel Bernard
Matt Drees
Norman Richards
Mike Youngstrom
Dan Allen
Nicklas Karlsson
Jacob Orshalick
Marek Novotny
Red Hat JBoss Web Framework Kit 2.3 Seam
Reference Guide
for use with Red Hat JBoss Enterprise Application Platform
Edition 2.3.0
Gavin King
Pete Muir
No rman Richards
Shane Bryzak
Michael Yuan
Mike Yo ungstro m
Christian Bauer
Jay Balunas
Dan Allen
Max Andersen
Emmanuel Bernard
Nicklas Karlsso n
Daniel Ro th
Matt Drees
Jaco b Orshalick
Marek No vo tny
Edited by
Red Hat Do cumentatio n Team
Red Hat Engineering Co ntent Services
Legal Notice
Copyright © 2013 Red Hat, Inc.
T his 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 T orvalds 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.
T he 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
T his book is a reference guide for the Seam framework shipped with JBoss Web Framework Kit, and its
patch releases.
Table of Contents
Table of Contents
.Preface
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .13
...........
1. Document Conventions
13
1.1. T ypographic Conventions
13
1.2. Pull-quote Conventions
14
1.3. Notes and Warnings
14
2. Getting Help and Giving Feedback
15
2.1. Do You Need Help?
15
2.2. Give us Feedback
15
.Chapter
. . . . . . . . 1.
. . .Seam
. . . . . . T. .utorial
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
............
1.1. Using the Seam examples
17
1.1.1. Running the examples on Red Hat JBoss Enterprise Application Platform
17
1.1.2. Running the example tests
17
1.2. Your first Seam application: the registration example
18
1.2.1. Understanding the code
18
1.2.1.1. T he entity bean: User.java
18
1.2.1.2. T he stateless session bean class: RegisterAction.java
20
1.2.1.3. T he session bean local interface: Register.java
22
1.2.1.4. T he view: register.xhtml and registered.xhtml
22
1.2.1.5. T he Seam component deployment descriptor: components.xml
23
1.2.1.6. T he web deployment description: web.xml
24
1.2.1.7. T he JSF configuration: faces-config.xml
25
1.2.1.8. T he EJB deployment descriptor: ejb-jar.xml
26
1.2.1.9. T he JPA persistence deployment descriptor: persistence.xml
26
1.2.1.10. T he EAR deployment descriptor: application.xml
27
1.2.2. How it works
27
1.3. Clickable lists in Seam: the messages example
28
1.3.1. Understanding the code
28
1.3.1.1. T he entity bean: Message.java
28
1.3.1.2. T he stateful session bean: MessageManagerBean.java
30
1.3.1.3. T he session bean local interface: MessageManager.java
32
1.3.1.4. T he view: messages.xhtml
32
1.3.2. How it works
33
1.4. A complete Seam application: the Hotel Booking example
34
1.4.1. Introduction
34
1.4.2. Overview of the booking example
35
1.4.3. Understanding Seam conversations
36
1.4.4. T he Seam Debug Page
46
1.5. Nested conversations: extending the Hotel Booking example
47
1.5.1. Introduction
47
1.5.2. Understanding Nested Conversations
48
1.6. Bookmarkable URLs with the Blog example
56
1.6.1. Using "pull"-style MVC
57
1.6.2. Bookmarkable search results page
59
1.6.3. Using "push"-style MVC in a REST ful application
61
.Chapter
. . . . . . . . 2.
. . .Migration
. . . . . . . . . . from
. . . . . 2.2
. . . .to
. . .2.3
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
............
2.1. Migration of XML Schemas
65
2.1.1. Seam schema migration
65
2.1.2. Java EE 6 schema changes
66
2.2. Java EE 6 upgrade
67
2.2.1. Using Bean Validation standard instead of Hibernate Validator
67
1
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
2.2.2. Migration of JSF 1 to JSF 2 Facelets templates
2.2.3. Using s:validate and s:validateAll in JSF 2
2.2.4. Migration to JPA 2.0
2.2.5. Using compatible JNDI for resources
2.3. Red Hat 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 T ree
2.6. Dependency changes when using Maven
2.6.1. Seam Bill of Materials
67
68
68
68
68
68
69
70
71
71
71
.Chapter
. . . . . . . . 3.
. . .Getting
. . . . . . . . started
. . . . . . . . with
. . . . . seam-gen
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .73
...........
3.1. Before you start
73
3.2. Setting up a new project
73
3.3. Creating a new action
76
3.4. Creating a form with an action
77
3.5. Generating an application from an existing database
77
3.6. Generating an application from existing JPA/EJB3 entities
77
3.7. Deploying the application as an EAR
77
3.8. Seam and incremental hot deployment
78
.Chapter
........4
. ...Getting
. . . . . . . . started
. . . . . . . . .with
. . . . Red
. . . . .Hat
. . . . JBoss
. . . . . . .Developer
. . . . . . . . . . . Studio
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
............
4.1. Hot deployment with JBoss Developer Studio
79
.Chapter
. . . . . . . . 5.
. . .T. he
. . . contextual
. . . . . . . . . . . .component
. . . . . . . . . . . .model
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
............
5.1. Seam contexts
80
5.1.1. Stateless context
80
5.1.2. Event context
80
5.1.3. Page context
80
5.1.4. Conversation context
80
5.1.5. Session context
81
5.1.6. Application context
81
5.1.7. Context variables
81
5.1.8. Context search priority
81
5.1.9. Concurrency model
82
5.2. Seam components
82
5.2.1. Stateless session beans
82
5.2.2. Stateful session beans
83
5.2.3. Entity beans
83
5.2.4. JavaBeans
84
5.2.5. Message-driven beans
84
5.2.6. Interception
84
5.2.7. Component names
85
5.2.8. Defining the component scope
86
5.2.9. Components with multiple roles
86
5.2.10. Built-in components
86
5.3. Bijection
87
5.4. Life cycle methods
89
5.5. Conditional installation
89
5.6. Logging
90
5.7. T he Mutable interface and @ReadOnly
91
5.8. Factory and manager components
93
.Chapter
. . . . . . . . 6.
. . .Configuring
. . . . . . . . . . . . Seam
. . . . . . .components
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
............
6.1. Configuring components through property settings
95
2
Table of Contents
6.2. Configuring components via components.xml
6.3. Fine-grained configuration files
6.4. Configurable property types
6.5. Using XML Namespaces
95
97
98
99
.Chapter
. . . . . . . . 7.
. . .Events,
. . . . . . . . interceptors
. . . . . . . . . . . . . and
. . . . .exception
. . . . . . . . . . .handling
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
.............
7.1. Seam events
102
7.2. Page actions
102
7.3. Page parameters
103
7.3.1. Mapping request parameters to the model
103
7.4. Propagating request parameters
104
7.5. URL rewriting with page parameters
104
7.6. Conversion and Validation
105
7.7. Navigation
106
7.8. Fine-grained files for defining navigation, page actions and parameters
108
7.9. Component-driven events
108
7.10. Contextual events
109
7.11. Seam interceptors
111
7.12. Managing exceptions
112
7.12.1. Exceptions and transactions
112
7.12.2. Enabling Seam exception handling
113
7.12.3. Using annotations for exception handling
113
7.12.4. Using XML for exception handling
113
7.12.4.1. Suppressing exception logging
114
7.12.5. Some common exceptions
115
.Chapter
. . . . . . . . 8.
. . .Conversations
. . . . . . . . . . . . . . . and
. . . . .workspace
. . . . . . . . . . . management
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
.............
8.1. Seam's conversation model
117
8.2. Nested conversations
118
8.3. Starting conversations with GET requests
119
8.4. Requiring a long-running conversation
120
8.5. Using <s:link> and <s:button>
121
8.6. Success messages
122
8.7. Natural conversation IDs
122
8.8. Creating a natural conversation
123
8.9. Redirecting to a natural conversation
123
8.10. Workspace management
124
8.10.1. Workspace management and JSF navigation
124
8.10.2. T he conversation switcher
124
8.10.3. T he conversation list
125
8.10.4. Breadcrumbs
126
8.11. Conversational components and JSF component bindings
126
8.12. Concurrent calls to conversational components
126
8.12.1. How should we design our conversational AJAX application?
127
8.12.2. Dealing with errors
128
8.12.3. RichFaces (Ajax4jsf)
129
.Chapter
. . . . . . . . 9.
. . .Seam
. . . . . . and
. . . . .Object/Relational
. . . . . . . . . . . . . . . . . .Mapping
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
.............
9.1. Introduction
130
9.2. Seam managed transactions
130
9.2.1. Disabling Seam-managed transactions
131
9.2.2. Configuring a Seam transaction manager
131
9.2.3. T ransaction synchronization
132
9.3. Seam-managed persistence contexts
132
9.3.1. Using a Seam-managed persistence context with JPA
132
9.3.2. Using a Seam-managed Hibernate session
133
3
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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
133
134
135
135
. . . . . . . . . 10.
Chapter
. . . . JSF
. . . . .form
. . . . .validation
. . . . . . . . . . in
. . .Seam
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
.............
.Chapter
. . . . . . . . 11.
. . . . Groovy
. . . . . . . . integration
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .14
. . .2. . . . . . . . . .
11.1. Groovy introduction
142
11.2. Writing Seam applications in Groovy
142
11.2.1. Writing Groovy components
142
11.2.1.1. Entity
142
11.2.2. Seam component
143
11.2.3. seam-gen
143
11.3. Deployment
143
11.3.1. Deploying Groovy code
143
11.3.2. Native .groovy file deployment at development time
143
11.3.3. seam-gen
144
.Chapter
. . . . . . . . 12.
. . . . T. .he
. . .Seam
. . . . . .Application
. . . . . . . . . . . . Framework
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .14
. . .5. . . . . . . . . .
12.1. Introduction
145
12.2. Home objects
146
12.3. Query objects
149
12.4. Controller objects
151
.Chapter
. . . . . . . . 13.
. . . .Seam
. . . . . . and
. . . . .JBoss
. . . . . . .Rules
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
.............
13.1. Installing rules
153
13.2. Using rules from a Seam component
154
.Chapter
. . . . . . . . 14
. . . .. Security
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
.............
14.1. Overview
155
14.2. Disabling Security
155
14.3. Authentication
155
14.3.1. Configuring an Authenticator component
155
14.3.2. Writing an authentication method
156
14.3.2.1. Identity.addRole()
157
14.3.2.2. Writing an event observer for security-related events
157
14.3.3. Writing a login form
158
14.3.4. Configuration Summary
158
14.3.5. Remember Me
158
14.3.5.1. T oken-based Remember Me Authentication
159
14.3.6. Handling Security Exceptions
161
14.3.7. Login Redirection
161
14.3.8. HT T P Authentication
162
14.3.8.1. Writing a Digest Authenticator
162
14.3.9. Advanced Authentication Features
162
14.3.9.1. Using your container's JAAS configuration
162
14.4. Identity Management
163
14.4.1. Configuring IdentityManager
163
14.4.2. JpaIdentityStore
164
14.4.2.1. Configuring JpaIdentityStore
164
14.4.2.2. Configuring the Entities
164
14.4.2.3. Entity Bean Examples
165
14.4.2.3.1. Minimal schema example
165
14.4.2.3.2. Complex Schema Example
166
14.4.2.4. JpaIdentityStore Events
168
14.4.2.4.1. JpaIdentityStore.EVENT _PRE_PERSIST _USER
168
4
Table of Contents
14.4.2.4.2. JpaIdentityStore.EVENT _USER_CREAT ED
14.4.2.4.3. JpaIdentityStore.EVENT _USER_AUT HENT ICAT ED
14.4.3. LdapIdentityStore
14.4.3.1. Configuring LdapIdentityStore
14.4.3.2. LdapIdentityStore Configuration Example
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.1.1. What is a role?
14.6.1.2. What is a permission?
14.6.2. Securing components
14.6.2.1. T he @Restrict annotation
14.6.2.2. Inline restrictions
14.6.3. Security in the user interface
14.6.4. Securing pages
14.6.5. Securing Entities
14.6.5.1. Entity security with JPA
14.6.5.2. Entity security with a Managed Hibernate Session
14.6.6. T ypesafe Permission Annotations
14.6.7. T ypesafe Role Annotations
14.6.8. T he Permission Authorization Model
14.6.8.1. PermissionResolver
14.6.8.1.1. Writing your own PermissionResolver
14.6.8.2. ResolverChain
14.6.9. RuleBasedPermissionResolver
14.6.9.1. Requirements
14.6.9.2. Configuration
14.6.9.3. Writing Security Rules
14.6.9.4. Non-String permission targets
14.6.9.5. Wildcard permission checks
14.6.10. PersistentPermissionResolver
14.6.10.1. Configuration
14.6.10.2. Permission Stores
14.6.10.3. JpaPermissionStore
14.6.10.3.1. Permission annotations
14.6.10.3.2. Example Entity
14.6.10.3.3. Class-specific Permission Configuration
14.6.10.3.4. Permission masks
14.6.10.3.5. Identifier Policy
14.6.10.3.6. ClassIdentifierStrategy
14.6.10.3.7. EntityIdentifierStrategy
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. CAPT CHA
14.9.1. Configuring the CAPT CHA Servlet
14.9.2. Adding a CAPT CHA to a form
14.9.3. Customizing the CAPT CHA algorithm
14.10. Security Events
14.11. Run As
168
168
168
169
171
172
172
172
175
175
176
176
176
176
176
177
178
179
179
180
181
181
182
182
183
183
184
185
185
185
186
187
188
188
188
188
190
190
191
192
193
193
194
194
195
195
196
197
198
198
198
198
198
199
199
5
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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
200
200
201
201
202
202
202
.Chapter
. . . . . . . . 15.
. . . . Internationalization,
. . . . . . . . . . . . . . . . . . . . . .localization
. . . . . . . . . . . .and
. . . . themes
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
.............
15.1. Internationalizing your application
203
15.1.1. Application server configuration
203
15.1.2. T ranslated application strings
203
15.1.3. Other encoding settings
203
15.2. Locales
204
15.3. Labels
205
15.3.1. Defining labels
205
15.3.2. Displaying labels
205
15.3.3. Faces messages
206
15.4. T imezones
206
15.5. T hemes
206
15.6. Persisting locale and theme preferences via cookies
207
.Chapter
. . . . . . . . 16.
. . . . Seam
......T
. .ext
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
.............
16.1. Basic formatting
208
16.2. Entering code and text with special characters
209
16.3. Links
210
16.4. Entering HT ML
210
16.5. Using the SeamT extParser
210
.Chapter
. . . . . . . . 17.
. . . . iT
. . ext
. . . .PDF
. . . . .generation
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
.............
17.1. Using PDF Support
212
17.1.1. Creating a document
212
17.1.2. Basic T ext Elements
213
17.1.3. Headers and Footers
216
17.1.4. Chapters and Sections
217
17.1.5. Lists
217
17.1.6. T ables
218
17.1.7. Document Constants
220
17.1.7.1. Color Values
220
17.1.7.2. Alignment Values
220
17.2. Charting
220
17.3. Bar codes
225
17.4. Fill-in-forms
226
17.5. Rendering Swing/AWT components
226
17.6. Configuring iT ext
227
17.7. Further documentation
227
.Chapter
. . . . . . . . 18.
. . . . T. .he
. . .Microsoft®
. . . . . . . . . . . .Excel®
. . . . . . . spreadsheet
. . . . . . . . . . . . . .application
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
.............
18.1. Microsoft Excel support
228
18.2. Creating a simple workbook
228
18.3. Workbooks
229
18.4. Worksheets
230
18.5. Columns
233
18.6. Cells
233
18.6.1. Validation
234
18.6.2. Format masks
236
6
Table of Contents
18.6.2.1. Number masks
18.6.2.2. Date 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. T he datatable exporter
18.14.8. Limitations
18.15. Internationalization
18.16. Links and further documentation
236
236
236
237
237
238
239
240
240
241
241
242
242
242
243
243
244
244
244
244
244
245
245
.Chapter
. . . . . . . . 19.
. . . . Email
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .24
. . .6. . . . . . . . . .
19.1. Creating a message
246
19.1.1. Attachments
247
19.1.2. HT ML/T ext alternative part
247
19.1.3. Multiple recipients
248
19.1.4. Multiple messages
248
19.1.5. T emplating
248
19.1.6. Internationalization
249
19.1.7. Other Headers
249
19.2. Configuration
249
19.2.1. mailSession
250
19.2.1.1. JNDI look up in Enterprise Application Platform
250
19.2.1.2. Seam-configured Session
250
19.3. T ags
250
.Chapter
. . . . . . . . 20.
. . . . Asynchronicity
. . . . . . . . . . . . . . . .and
. . . . messaging
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
.............
20.1. Asynchronicity
253
20.1.1. Asynchronous methods
253
20.1.2. Asynchronous methods with the Quartz Dispatcher
255
20.1.3. Asynchronous events
257
20.1.4. Handling exceptions from asynchronous calls
257
20.2. Messaging in Seam
258
20.2.1. Configuration
258
20.2.2. Sending messages
258
20.2.3. Receiving messages using a message-driven bean
259
20.2.4. Receiving messages in the client
260
.Chapter
. . . . . . . . 21.
. . . . Caching
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
.............
21.1. Using Caching in Seam
261
21.2. Page fragment caching
263
. . . . . . . . . 22.
Chapter
. . . . Web
. . . . . Services
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
.............
7
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
22.1. Configuration and Packaging
22.2. Conversational Web Services
22.2.1. A Recommended Strategy
22.3. An example web service
22.4. REST ful HT T P web services with REST Easy
22.4.1. REST Easy configuration and request serving
22.4.2. Resources and providers as Seam components
22.4.3. Securing resources
22.4.4. Mapping exceptions to HT T P responses
22.4.5. Exposing entities via REST ful API
22.4.5.1. ResourceQuery
22.4.5.2. ResourceHome
265
265
266
266
267
267
269
271
271
272
272
273
.Chapter
. . . . . . . . 23.
. . . .Remoting
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
.............
23.1. Configuration
275
23.2. T he Seam object
275
23.2.1. A Hello World example
275
23.2.2. Seam.Component
277
23.2.2.1. Seam.Component.newInstance()
277
23.2.2.2. Seam.Component.getInstance()
278
23.2.2.3. Seam.Component.getComponentName()
278
23.2.3. Seam.Remoting
278
23.2.3.1. Seam.Remoting.createT ype()
278
23.2.3.2. Seam.Remoting.getT ypeName()
278
23.3. Evaluating EL Expressions
278
23.4. Client Interfaces
279
23.5. T he Context
279
23.5.1. Setting and reading the Conversation ID
279
23.5.2. Remote calls within the current conversation scope
280
23.6. Batch Requests
280
23.7. Working with Data types
280
23.7.1. Primitives / Basic T ypes
280
23.7.1.1. String
280
23.7.1.2. Number
280
23.7.1.3. Boolean
280
23.7.2. JavaBeans
280
23.7.3. Dates and T imes
281
23.7.4. Enums
281
23.7.5. Collections
281
23.7.5.1. Bags
281
23.7.5.2. Maps
281
23.8. Debugging
282
23.9. Handling Exceptions
282
23.10. T he Loading Message
282
23.10.1. Changing the message
282
23.10.2. Hiding the loading message
282
23.10.3. A Custom Loading Indicator
283
23.11. Controlling what data is returned
283
23.11.1. Constraining normal fields
283
23.11.2. Constraining Maps and Collections
284
23.11.3. Constraining objects of a specific type
284
23.11.4. Combining Constraints
284
23.12. T ransactional Requests
284
23.13. JMS Messaging
284
23.13.1. Configuration
284
23.13.2. Subscribing to a JMS T opic
285
8
Table of Contents
23.13.3. Unsubscribing from a T opic
23.13.4. T uning the Polling Process
285
285
.Chapter
. . . . . . . . 24
. . . .. Seam
. . . . . . .and
. . . .the
. . . .Google
. . . . . . . .Web
. . . . .T. oolkit
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
.............
24.1. Configuration
287
24.2. Preparing your component
287
24.3. Hooking up a GWT widget to the Seam component
288
24.4. GWT Ant T argets
289
24.5. GWT Maven plugin
290
.Chapter
. . . . . . . . 25.
. . . . Spring
. . . . . . . Framework
. . . . . . . . . . . . integration
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
.............
25.1. Injecting Seam components into Spring beans
292
25.2. Injecting Spring beans into Seam components
293
25.3. Making a Spring bean into a Seam component
294
25.4. Seam-scoped Spring beans
294
25.5. Using Spring PlatformT ransactionManagement
295
25.6. Using a Seam-Managed Persistence Context in Spring
295
25.7. Using a Seam-Managed Hibernate Session in Spring
296
25.8. Spring Application Context as a Seam Component
297
25.9. Using a Spring T askExecutor for @Asynchronous
297
.Chapter
. . . . . . . . 26.
. . . . Hibernate
. . . . . . . . . . .Search
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
.............
26.1. Introduction
299
26.2. Configuration
299
26.3. Usage
300
.Chapter
. . . . . . . . 27.
. . . . Configuring
. . . . . . . . . . . . .Seam
. . . . . .and
. . . . packaging
. . . . . . . . . . . .Seam
. . . . . .applications
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
.............
27.1. Basic Seam configuration
302
27.1.1. Integrating Seam with JSF and your servlet container
302
27.1.2. Seam Resource Servlet
303
27.1.3. Seam Servlet filters
303
27.1.3.1. Exception handling
304
27.1.3.2. Conversation propagation with redirects
304
27.1.3.3. URL rewriting
304
27.1.3.4. Multipart form submissions
304
27.1.3.5. Character encoding
305
27.1.3.6. RichFaces
305
27.1.3.7. Identity Logging
305
27.1.3.8. Context management for custom servlets
306
27.1.3.9. Enabling HT T P cache-control headers
306
27.1.3.10. Adding custom filters
306
27.1.4. Integrating Seam with your EJB container
307
27.1.5. Remember
309
27.2. Using Alternate JPA Providers
309
27.3. Configuring Seam in Java EE 6
310
27.3.1. Packaging
310
27.4. Configuring Seam without EJB
311
27.4.1. Boostrapping Hibernate in Seam
312
27.4.2. Boostrapping JPA in Seam
312
27.4.3. Packaging
312
27.5. Configuring Seam in Java SE
313
27.6. Deployment in Red Hat JBoss Enterprise Application Platform 6
313
27.7. Configuring SFSB and Session T imeouts in Red Hat JBoss Enterprise Application Platform 6
27.8. Running Seam in a Portlet
317 316
27.9. Deploying custom resources
317
. . . . . . . . . 28.
Chapter
. . . . Seam
. . . . . . annotations
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
.............
9
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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 dataT able
28.11. Meta-annotations for databinding
28.12. Annotations for packaging
28.13. Annotations for integrating with the Servlet container
320
322
325
325
327
328
328
328
329
329
330
331
331
331
.Chapter
. . . . . . . . 29.
. . . . Built-in
. . . . . . . . Seam
. . . . . . .components
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .333
............
29.1. Context injection components
333
29.2. JSF-related components
333
29.3. Utility components
334
29.4. Components for internationalization and themes
335
29.5. Components for controlling conversations
336
29.6. Security-related components
337
29.7. JMS-related components
337
29.8. Mail-related components
337
29.9. Infrastructural components
338
29.10. Miscellaneous components
340
29.11. Special components
340
.Chapter
. . . . . . . . 30.
. . . .Seam
. . . . . . JSF
. . . . .controls
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
. . .2. . . . . . . . . .
30.1. T ags
342
30.1.1. Navigation Controls
342
30.1.1.1. <s:button>
342
30.1.1.2. <s:conversationId>
342
30.1.1.3. <s:taskId>
343
30.1.1.4. <s:link>
343
30.1.1.5. <s:conversationPropagation>
343
30.1.1.6. <s:defaultAction>
343
30.1.2. Converters and Validators
344
30.1.2.1. <s:convertDateT ime>
344
30.1.2.2. <s:convertEntity>
344
30.1.2.3. <s:convertEnum>
345
30.1.2.4. <s:convertAtomicBoolean>
346
30.1.2.5. <s:convertAtomicInteger>
346
30.1.2.6. <s:convertAtomicLong>
346
30.1.2.7. <s:validateEquality>
346
30.1.2.8. <s:validate>
347
30.1.2.9. <s:validateAll>
347
30.1.3. Formatting
348
30.1.3.1. <s:decorate>
348
30.1.3.2. <s:div>
349
30.1.3.3. <s:span>
349
30.1.3.4. <s:fragment>
349
30.1.3.5. <s:label>
350
30.1.3.6. <s:message>
350
30.1.4. Seam T ext
350
30.1.4.1. <s:validateFormattedT ext>
350
10
Table of Contents
30.1.4.2. <s:formattedT ext>
30.1.5. Form support
30.1.5.1. <s:token>
30.1.5.2. <s:enumItem>
30.1.5.3. <s:selectItems>
30.1.5.4. <s:fileUpload>
30.1.6. Other
30.1.6.1. <s:cache>
30.1.6.2. <s:resource>
30.1.6.3. <s:download>
30.1.6.4. <s:graphicImage>
30.1.6.5. <s:remote>
30.2. Annotations
350
351
351
352
352
352
353
353
354
355
355
356
356
.Chapter
. . . . . . . . 31.
. . . .JBoss
. . . . . . .EL
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358
.............
31.1. Parameterized Expressions
358
31.1.1. Usage
358
31.1.2. Limitations and Hints
359
31.2. Projection
360
.Chapter
. . . . . . . . 32.
. . . .Performance
. . . . . . . . . . . . . .T. uning
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361
.............
32.1. Bypassing Interceptors
361
.Chapter
. . . . . . . . 33.
. . . .T. esting
. . . . . . . Seam
. . . . . . .applications
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362
.............
33.1. Unit testing Seam components
362
33.2. Integration testing Seam components
363
33.2.1. Configuration
364
33.2.2. Using JUnitSeamT est with Arquillian
365
33.2.2.1. Using mocks in integration tests
367
33.2.3. Integration testing Seam application user interactions
368
33.2.3.1. Configuration
369
33.2.3.2. Using SeamT est with another test framework
370
33.2.3.3. Integration T esting with Mock Data
370
.Chapter
. . . . . . . . 34
. . ... Dependencies
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372
.............
34.1. JDK Dependencies
372
34.1.1. Oracle's JDK 6 Considerations
372
34.2. Project Dependencies
372
34.2.1. Core
372
34.2.2. RichFaces
372
34.2.3. Seam PDF
373
34.2.4. Seam Microsoft® Excel®
373
34.2.5. Drools
373
34.2.6. GWT
374
34.2.7. Spring
374
34.2.8. Groovy
374
34.3. Dependency Management using Maven
374
. . . . . . . . . .History
Revision
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
.............
11
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
12
Preface
Preface
1. Document Conventions
T his 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. T he
Liberation Fonts set is also used in HT ML 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. T hese
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:
T o see the contents of the file m y_next_bestselling_novel in your current working
directory, enter the cat m y_next_bestselling_novel command at the shell prompt
and press Enter to execute the command.
T he 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.
T he first example highlights a particular key to press. T he 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 m ono-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
T his 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 sub-menu titles. For example:
Choose System → Preferences → Mouse from the main menu bar to launch Mouse
Preferences. In the Buttons tab, select the Left-handed m ouse 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).
T o insert a special character into a gedit file, choose Applications → Accessories →
Character Map from the main menu bar. Next, choose Search → Find… from the
Character Map menu bar, type the name of the character in the Search field and click
Next. T he character you sought will be highlighted in the Character T able. Double-click
this highlighted character to place it in the T ext to copy field and then click the Copy
button. Now switch back to your document and choose Edit → Paste from the gedit menu
bar.
13
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
T he 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:
T o connect to a remote machine using ssh, type ssh [email protected] domain.name at a shell
prompt. If the remote machine is exam ple.com and your username on that machine is
john, type ssh [email protected] exam ple.com .
T he m ount -o rem ount file-system command remounts the named file system. For
example, to remount the /hom e file system, the command is m ount -o rem ount /hom e.
T o 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
T erminal output and source code listings are set off visually from the surrounding text.
Output sent to a terminal is set in m ono-spaced rom an and presented thus:
books
books_tests
Desktop
Desktop1
documentation
downloads
drafts
images
mss
notes
photos
scripts
stuff
svgs
svn
Source-code listings are also set in m ono-spaced rom an 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.
14
Preface
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. T hrough 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 Enterprise WFK Platform 2 and the
component doc-Seam -Reference-Guide. T he 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:
15
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Be sure to give us your name so that you can receive full credit for reporting the issue.
16
Chapter 1. Seam Tutorial
Chapter 1. Seam Tutorial
1.1. Using the Seam examples
Seam provides a number of example applications that demonstrate the use of a variety of Seam's
features. T his tutorial guides you through a few examples to help you start learning Seam. T he Seam
examples are located in the exam ples subdirectory of the Seam Demo file distributed with Red Hat
JBoss Web Framework Kit. T he first example, on registration, is in the exam ples/registration
directory.
All examples have similar directory structure based on Maven project structure defaults:
T he <exam ple>-ear directory contains enterprise application submodule files such as aggregator
for web application files, and EJB project.
T he <exam ple>-web directory contains web application submodule view-related files such as web
page templates, images, and stylesheets.
T he <exam ple>-ejb directory contains Enterprise Java Beans components.
T he <exam ple>-tests directory contains integration and functional tests.
T he <exam ple>-web/src/m ain/webapp directory contains view-related files such as web page
templates, images, and stylesheets.
T he <exam ple>-[ear|ejb]/src/m ain/resources directory contains deployment descriptors,
and other configuration files.
T he <exam ple>-ejb/src/m ain/java directory contains the application source code.
Note
Seam examples are built and run from the Maven pom .xm l file, so you must have at least
version 3.x of Maven installed before you get started.
1.1.1. Running the examples on Red Hat JBoss Enterprise Application Platform
Seam examples are configured for use on JBoss Enterprise Application Platform.
Set JBOSS_HOME as the environment variable. Start the application server and build examples by typing
m vn install in the example's root directory. T o deploy an example, change the directory to *-ear or *web and use the command m vn jboss-as:deploy. An 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 /${nam e}-web.
Note
Examples like groovybooking, hibernate, jpa, and spring can only be deployed as a WAR.
1.1.2. Running the example tests
Most examples come with a suite of JUnit and Arquillian integration tests. T he easiest way to run the
tests is to run the command m vn verify -Darquillian=jbossas-m anaged-7.
17
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
1.2. Your first Seam application: the registration example
T he registration example is a simple application that allows a new user to store user name, real name,
and password in the database. T his 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.
T he start page displays a basic form with three input fields. If a user fills these fields and submits the
form a user object is saved in the database.
1.2.1. Understanding the code
T he example is implemented with two Facelet templates: entity bean, and stateless session bean. T his
section explains the code in detail, starting from the base level.
1.2.1.1. T he entity bean: User.java
You need an EJB entity bean for user data. T his class defines persistence and validation declaratively
through annotations. It also requires some extra annotations to define the class as a Seam component.
18
Chapter 1. Seam Tutorial
Example 1.1. User.java
@Entity
@Name("user")
@Scope(SESSION)
@Table(name="users")
public class User implements Serializable
{
private static final long serialVersionUID = 1881413500711441951L;
private String username;
private String password;
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()
{
return password;
}
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;
}
public void setUsername(String username)
{
this.username = username;
}
}
T he EJB3 standard @ Entity annotation indicates that the User class is an entity bean.
A Seam component needs a component name specified by the @ Nam e annotation. T his name
must be unique within the Seam application. When JSF asks Seam to resolve a context
variable with a name that is the same as a Seam component name, and the context variable is
currently undefined (null), Seam instantiates the component, and binds the new instance to the
context variable. In this case, Seam instantiates a User the first time JSF encounters a
19
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
variable named user.
Whenever Seam instantiates a component, it binds the new instance to a context variable in
the component's default context. T he default context is specified using the @ Scope
annotation. T he User bean is a session scoped component.
T he EJB standard @ T able annotation indicates that the User class is mapped to the users
table.
nam e, password, and usernam e are persistent attributes of the entity bean. All persistent
attributes define accessor methods. T hese attributes are required when a component is used
by JSF in the render response, and update model values phase.
An empty constructor is required by both; EJB specification, and Seam.
T he @ NotNull and @ Size annotations are part of the Hibernate Validator framework. Seam
integrates Hibernate Validator and allows you use it for data validation (even if you are not
using Hibernate for persistence).
T he EJB standard @ Id annotation indicates the primary key attribute of the entity bean.
In the above example, @ Nam e and @ Scope annotations are very important as these annotations
establish the class as a Seam component.
T he next section shows that the properties of the User class are bound directly to JSF components and
populated by JSF during the update model values phase. T here is no glue code to copy data back and
forth between the JSP pages and the entity bean domain model.
However, entity beans do not perform transaction management or database access. So, the JSF
component is not used as a JSF action listener. In this situation, use a session bean.
1.2.1.2. T he stateless session bean class: RegisterAction.java
Most Seam applications use session beans as JSF action listeners, though you may also use
JavaBeans.
T his example application has exactly one JSF action and one session bean method attached to it. It
uses a stateless session bean as the state associated with the action is retained by the User bean.
T he relevant code is shown below:
20
Chapter 1. Seam Tutorial
Example 1.2. RegisterAction.java
@Stateless
@Name("register")
public class RegisterAction implements Register
{
@In
private User user;
@PersistenceContext
private EntityManager em;
@Logger
private Log log;
public String register()
{
List existing = em.createQuery("select username " +
"from User " +
"where username = #{user.username}")
.getResultList();
if (existing.size()==0)
{
em.persist(user);
log.info("Registered new user #{user.username}");
return "/registered.xhtml";
}
else
{
FacesMessages.instance().add("User #{user.username} already
exists");
return null;
}
}
}
T he EJB @ Stateless annotation marks this class as a stateless session bean.
T he @ 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).
T he EJB standard @ PersistenceContext annotation is used to inject the EJB3 entity
manager.
T he Seam @ Logger annotation is used to inject the component's Log instance.
T he action listener method uses the standard EJB3 EntityManager API to interact with the
database, and returns the JSF outcome. Note that, as this is a session bean, a transaction
automatically starts when the register() method is called, and committed when the
register() method completes.
Seam allows you to use a JSF EL expression inside EJB-QL. T his results in an ordinary JPA
setParam eter() call on the standard JPA Query object.
T he Log API allows you to easily display templated log messages that can also use JSF EL
expressions.
JSF action listener methods return a string-valued outcome that determines the page that is
21
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
displayed next. A null outcome (or a void action listener method) redisplays the previous page.
In plain JSF, it is normal to always use a JSF navigation rule to determine the JSF view id from
the outcome. For complex applications this indirection is useful and a good practice. However,
for very simple examples like this one, Seam allows you to use the JSF view id as the outcome,
eliminating the requirement for a navigation rule. Note that when you use a view id as an
outcome, Seam always performs a browser redirect.
Seam provides a number of built-in components to help solve common problems. T he
FacesMessages component makes it easy to display templated error or success messages.
(As of Seam 2.1, you can use StatusMessages instead to remove the semantic dependency
on JSF). Built-in Seam components may be obtained by injection, or by calling the
instance() method on the class of the built-in component.
Note that we did not explicitly specify a @ Scope this time. Each Seam component type has a default
scope, which is used if a scope is not explicitly specified. For stateless session beans, the default scope
is the stateless context.
T he session bean action listener performs the business and persistence logic for a mini-application. In a
more complex application, a separate service layer might be necessary, but Seam allows you to
implement your own strategies for application layering. You can make any application as simple, or as
complex, as you want.
Note
T his application is more complex than necessary for the sake of clear example code. All the
application code could have been eliminated by using Seam's application framework controllers.
1.2.1.3. T he session bean local interface: Register.java
T he session bean requires a local interface.
Example 1.3. Register.java
@Local
public interface Register
{
public String register();
}
T hat is the end of the Java code. T he next level to examine is the view.
1.2.1.4 . T he view: register.xhtm l and registered.xhtm l
T he view pages for a Seam application can be implemented using any technology that supports JSF.
T he following example is written with Facelets.
22
Chapter 1. Seam Tutorial
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>
<f:view>
<h:form>
<s:validateAll>
<h:panelGrid columns="2">
Username: <h:inputText value="#{user.username}" required="true"/>
Real Name: <h:inputText value="#{user.name}" required="true"/>
Password: <h:inputSecret value="#{user.password}" required="true"/>
</h:panelGrid>
</s:validateAll>
<h:messages/>
<s:button value="Register" action="#{register.register}"/>
</h:form>
</f:view>
</h:body>
</html>
T he only Seam-specific tag in the above example is <s:validateAll>
. T his 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>
T he above is a simple Facelets page, created with inline EL — it contains nothing specific to Seam.
1.2.1.5. T he Seam component deployment descriptor: com ponents.xm l
Seam strongly values minimal configuration. T he configuration files are created when you create a Seam
application, and they are rarely required to be altered.
Unlike Java frameworks, Seam does not require application components to be accompanied by XML files.
23
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Most Seam applications require very few XML files, which do not tend to increase in size as the project
expands.
However, it is useful to provide external configuration of some components, particularly the components
that are built into Seam. T he most flexible option, here, is to provide this configuration in a file called
com ponents.xm l, located in the WEB-INF directory. T he com ponents.xm l file can be used to tell
Seam the method of finding 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>
T he above code configures jndiPattern property of the built-in Seam component
org.jboss.seam .core.init. More information about the working of this process is available at:
Section 6.2, “Configuring components via com ponents.xm l”.
1.2.1.6. T he web deployment description: web.xm l
T he presentation layer of a mini-application is deployed in a WAR, so a web deployment descriptor is
required.
24
Chapter 1. Seam Tutorial
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>
T he above web.xm l file configures both Seam and JSF. T his configuration changes very little between
Seam applications.
1.2.1.7. T he JSF configuration: faces-config.xm l
Most Seam applications use JSF views as the presentation layer, so faces-config.xm l is usually a
requirement.
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">
</faces-config>
Note that JSF managed bean declarations are unnecessary because the managed beans are annotated
Seam components. In Seam applications, faces-config.xm l file is used less often than in plain JSF.
After setting the basic descriptors, define the orchestration: navigation rules to add functionality to the
Seam application. Seam operates on the principle that process flow and configuration data are all that
truly belongs in XML.
25
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
T he above example does not require a navigation rule, as the view ID is embedded in the action code.
1.2.1.8. T he EJB deployment descriptor: ejb-jar.xm l
T he ejb-jar.xm l file integrates Seam with EJB3 by attaching the Seam Interceptor to all the
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. T he JPA persistence deployment descriptor: persistence.xm l
T he persistence.xm l file contains the location of the datasource that is required by the JPA
persistence provider, and also contains some vendor-specific settings. In this case, the
persistence.xm l file also enables automatic schema export at startup.
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>
26
Chapter 1. Seam Tutorial
1.2.1.10. T he EAR deployment descriptor: application.xm l
Applications deployed as an EAR require a deployment descriptor. T he descriptor file can be generated
by Maven EAR plug-in if the registration application has it set up in the registration-ear/pom .xm l
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>
T his deployment descriptor links modules in the enterprise archive and binds the web application to the
context root /seam -registration.
You have now seen all the files in the application.
1.2.2. How it works
When the form is submitted, JSF requests Seam to resolve the user variable. T he uservariable is not
assigned a value yet (in any Seam context). So, Seam instantiates the user component, and returns the
resulting User entity bean instance to JSF after storing the instance in the Seam session context.
T he form input values are validated against the Hibernate Validator constraints specified on the User
entity. If the constraints are violated, JSF redisplays the page. Otherwise, JSF binds the form input
values to the properties of the User entity bean.
JSF requests Seam to resolve the register variable. Seam uses the JNDI pattern to locate the
stateless session bean, wraps the bean as a Seam component, and returns it. Seam then presents this
Seam component to JSF, and JSF invokes the register() action listener method.
Seam then intercepts the method call and injects the User entity from the Seam session context, before
allowing the invocation to continue.
T he register() method checks if a user already exists with the entered username. If so, an error
message is queued with the FacesMessages component, and a null outcome is returned, causing a
page redisplay. T he FacesMessages component interpolates the JSF expression embedded in the
message string and adds a JSF FacesMessage to the view.
If no user exists with the entered username, the "/registered.xhtm l" outcome triggers a browser
redirect to the registered.xhtm l 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.
27
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
1.3. Clickable lists in Seam: the messages example
Clickable lists of database search results are a vital part of an online application. Seam provides special
functionality on top of JSF to make it easier to query data with EJB-QL or HQL, and display the result as
a clickable list using a JSF <h:dataT able>
. T he messages example demonstrates this functionality.
1.3.1. Understanding the code
T he message list example has one entity bean (Message), one session bean (MessageListBean),
and one JSF.
1.3.1.1. T he entity bean: Message.java
T he Message entity defines the title, text, date, and time of a message, and a flag indicating whether the
message has been read.
28
Chapter 1. Seam Tutorial
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;
}
}
29
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
1.3.1.2. T he stateful session bean: MessageManagerBean.java
T his example contains a session bean (MessageManagerBean) that defines the action listener
methods for both of the buttons on the form. One buttons selects a message from the list and displays
the message; the other button deletes a message.
However, MessageManagerBean is also responsible for fetching the list of messages the first time
you navigate to the message list page. T here are various ways to navigate to the message list page, all
the ways are not preceded by a JSF action. (For example, navigating to the page from your favorites will
not necessarily call the JSF action.). T herefore, fetching the message list must take place in a Seam
factory method, instead of in an action listener method.
T o cache the list of messages in memory between server requests, make this a stateful session bean.
30
Chapter 1. Seam Tutorial
Example 1.13. MessageManagerBean.java
@Stateful
@Scope(SESSION)
@Name("messageManager")
public class MessageManagerBean
implements Serializable, MessageManager
{
@DataModel
private List<Message> messageList;
@DataModelSelection
@Out(required=false)
private Message message;
@PersistenceContext(type=EXTENDED)
private EntityManager em;
@Factory("messageList")
public void findMessages()
{
messageList = em.createQuery("select msg " +
"from Message msg" +
"order by msg.datetime desc")
.getResultList();
}
public void select()
{
message.setRead(true);
}
public void delete()
{
messageList.remove(message);
em.remove(message);
message=null;
}
@Remove
public void destroy() {}
}
T he @ DataModel annotation exposes an attributes of type java.util.List to the JSF
page as an instance of javax.faces.m odel.DataModel. T his allows you to use the list in
a JSF <h:dataT able> with clickable links for each row. In this case, the DataModel is
made available in the m essageList session context variable.
T he @ DataModelSelection annotation tells Seam to inject the List element that
corresponds to the clicked link.
T he @ Out annotation exposes the selected value directly to the page. So every time a row of
the clickable list is selected, the Message is injected to the attribute of the stateful bean, and
subsequently outjected to the m essage event context variable.
T his stateful bean has an EJB3 extended persistence context. T he messages retrieved in the
query remain in the managed state as long as the bean exists. So subsequent method calls to
the stateful bean can update the messages without making an explicit call to the
EntityManager.
T he first time you navigate to the JSF page, there is no value in the m essageList context
variable. T he @ Factory annotation requests Seam to create an instance of
MessageManagerBean and invoke the findMessages() method to initialize the value.
findMessages()i called a factory method for m essages.
31
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
T he select() action listener method marks the selected Message as read, and updates it in
the database.
T he delete() action listener method removes the selected Message from the database.
All stateful session bean Seam components must define a parameterless method marked
@ Rem ove. Seam uses this method to remove stateful beans when Seam context ends, and
clean up server-side states.
Note
T his is a session-scoped Seam component. It is associated with the user log in the session, and
all requests from a log in the session share the same instance of the component. Sessionscoped components are used sparingly in Seam applications.
1.3.1.3. T he 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 not shown in code examples. Com ponents.xm l,
persistence.xm l, web.xm l, ejb-jar.xm l, faces-config.xm l, and application.xm l files
operate in a similar fashion as in the previous example, and go directly to the JSF.
1.3.1.4 . T he view: m essages.xhtm l
T he JSF page is a straightforward use of the JSF <h:dataT able> component.
32
Chapter 1. Seam Tutorial
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
T he first time you navigate to the m essages.xhtm l page, the page tries to resolve the m essageList
context variable. As the context variable is not initialized, Seam calls the findMessages()factory
method, which performs a query against the database and results in a DataModel being outjected.
T his DataModel provides the row data needed for rendering <h:dataT able>.
When you click <s:link>, JSF calls the select() action listener. Seam intercepts the call and injects
the selected row data into the m essage attribute of the m essageManager component. T he action
listener fires, marking the selected Message as read. At the end of the call, Seam outjects the selected
33
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Message to the m essage context variable. T he EJB container commits the transaction, and the change
to the Message is flushed to the database. T he page is re-rendered, redisplaying the message list, and
displaying the selected message below it.
If you click <s:button>, JSF calls the delete() action listener. Seam intercepts the call and injects the
selected row data into the m essage attribute of the m essageManager component. T he action listener
fires, removing the selected Message from the list, and calling rem ove() on the EntityManager. At
the end of the call, Seam refreshes the m essageList context variable and clears the m essage context
variable. T he EJB container commits the transaction, and deletes the Message from the database. T he
page is re-rendered, redisplaying the message list.
1.4. A complete Seam application: the Hotel Booking example
1.4.1. Introduction
T he 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
34
Chapter 1. Seam Tutorial
T he booking application uses JSF, EJB 3.1, and Seam, along with Facelets for the view. T here is also a
port of this application to JSF, Facelets, Seam, JavaBeans, and Hibernate 4.
T his application is extremely robust. You can open multiple windows, use the back, browser, and refresh
buttons, and enter nonsensical data, but it is difficult to crash the application. Seam is designed to build
straightforward robust web applications. Robustness that was previously hand-coded now comes
naturally and automatically with Seam.
In the source code of the example application you can see how declarative state management and
integrated validation are used to achieve robustness.
1.4.2. Overview of the booking example
T he project structure is identical to that of the previous project. T o install and deploy this application,
refer to Section 1.1, “Using the Seam examples”. Once you have successfully started the application, you
can access the application by pointing the browser to http://localhost:8080/seam -booking/
T he application uses six session beans to implement the business logic for the following features:
35
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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. T his functionality is
implemented as a conversation, so HotelBookingAction class is an important class in the
application.
HotelSearchingAction implements the hotel search functionality.
RegisterAction registers a new system user.
T hree entity beans implement the application's persistent domain model:
Hotel is an entity bean that represents a hotel.
Booking is an entity bean that represents an existing booking.
User is an entity bean that represents a user who can make hotel bookings.
1.4.3. Understanding Seam conversations
T his tutorial concentrates on one particular piece of functionality: placing a hotel reservation. From the
user's perspective, hotel search, selection, booking, and confirmation are one continuous unit of work —
a conversation. However, from the application perspective, it is important that searching remains
separate. T his will help users to select multiple hotels from the same search results page, and open
distinct conversations in separate browser tabs.
Most web application architectures do not have first class constructs to represent conversations, which
makes managing conversational state problematic. Java web applications generally use a combination of
several techniques. Some state is transferred in the URL, but that cannot be transferred is either added
to the HttpSession or recorded to the database at the beginning and end of each request.
As the database is the least-scalable tier, it drastically reduces scalability. T he extra traffic to and from
the database also increases latency. In order to reduce redundant traffic, Java applications introduce a
data cache to store commonly-accessed data between requests. However, invalidation is based upon an
LRU policy, rather than whether the user has finished using the data. T herefore, this data cache is
inefficient. T his data cache is shared between concurrent transactions, which introduces issues
associated with keeping the cached state consistent with that of the database.
State held in the HttpSession suffers similar issues. T he HttpSession can be used to store true
session data; data common to all requests between user and application. However, for data related to
individual request series, HttpSession is not effective. Conversations stored in HttpSession break
down quickly when dealing with multiple windows or the back button. Without careful programming, data
in HttpSession can grow large, and make the session difficult to cluster. Developing mechanisms to
deal with the problems these methods present (by isolating session state associated with distinct
concurrent conversations, and incorporating failsafes to ensure conversation state is destroyed when a
conversation is aborted) can be complicated.
Seam improves conditions by introducing conversation context as a first class construct. Conversation
state is stored safely in the conversation context, with a well-defined life cycle. Also, there is no need to
push data continually between the application server and the database; the conversation context is a
natural cache for currently-used data.
In the following application, the conversation context is used to store stateful session beans. T hese
beans are sometimes regarded as detrimental to scalability. However, modern application servers have
sophisticated mechanisms for stateful session bean replication. JBoss Enterprise Application Platform
performs fine-grained replication, replicating only altered bean attribute values. If used correctly, stateful
session beans pose no scalability problems. However, if you are uncomfortable or unfamiliar with the
use of stateful session beans, Seam also allows the use of POJOs.
T he booking example shows one way in which stateful components with different scopes can
collaborate to achieve complex behaviors. T he main page of the booking application allows you to
search for hotels. Search results are stored in the Seam session scope. When you navigate to a hotel, a
conversation begins, and a conversation scoped component retrieves the selected hotel from the
36
Chapter 1. Seam Tutorial
conversation begins, and a conversation scoped component retrieves the selected hotel from the
session scoped component.
T he booking example also demonstrates the use of RichFaces Ajax to implement rich client behavior
without handwritten JavaScript.
T he search function is implemented with a session-scoped stateful session bean, similar to the one
used in the message list example.
37
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Example 1.16. HotelSearchingAction.java
38
Chapter 1. Seam Tutorial
@Stateful
@Name("hotelSearch")
@Scope(ScopeType.SESSION)
@Restrict("#{identity.loggedIn}")
public class HotelSearchingAction implements HotelSearching
{
@PersistenceContext
private EntityManager em;
private String searchString;
private int pageSize = 10;
private int page;
@DataModel
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;
39
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
}
public void setSearchString(String searchString)
{
this.searchString = searchString;
}
@Remove
public void destroy() {}
}
T he EJB standard @ Stateful annotation identifies HotelSearchingAction.java class
as a stateful session bean. Stateful session beans are scoped to the conversation context by
default.
T he @ Restrict annotation applies a security restriction to the component. It restricts access
to the component allowing only logged-in users. T he security chapter explains more about
security in Seam.
T he @ DataModel annotation exposes a List as a JSF ListDataModel. T his makes it
easy to implement clickable lists for search screens. In this case, the list of hotels is exposed
to the page as a ListDataModel in the hotels conversation variable.
T he EJB standard @ Rem ove annotation specifies that a stateful session bean should be
removed and its state should be destroyed after invocation of the annotated method. In Seam,
all stateful session beans must define a parameterless method marked @ Rem ove. T his
method is called when Seam destroys the session context.
T he main page of the application is a Facelets page. T he fragment that relates to hotel searching is
shown below:
40
Chapter 1. Seam Tutorial
Example 1.17. main.xhtml
41
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<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>
&#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>
</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}">
<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>
42
Chapter 1. Seam Tutorial
</h:dataTable>
<s:link id="MoreResultsLink" value="More results"
action="#{hotelSearch.nextPage}" rendered="#{hotelSearch.nextPageAvailable}"/>
</div>
</a:outputPanel>
T he RichFaces Ajax <a:ajax> tag allows a JSF action event listener to be called by
asynchronous XMLHttpRequest when a JavaScript event like keyup occurs. Also, the
render attribute allows you to render a fragment of the JSF page and perform a partial page
update when the asynchronous response is received.
T he RichFaces Ajax <a:status> tag allows you to display an animated image while you wait
for asynchronous requests to return.
T he RichFaces Ajax <a:outputPanel> tag defines a region of the page that can be rerendered by an asynchronous request.
T he Seam <s:link> tag allows you to attach a JSF action listener to an ordinary (nonJavaScript) HT ML link. T he advantage of this over the standard JSF <s:link> is that it
preserves the operation of "open in new window" and "open in new tab". Also notice the use
of a method binding with a parameter: #{hotelBooking.selectHotel(hot)}. T his is not
possible in the standard Unified EL, but Seam provides an extension to the EL that allows you
to use parameters on any method binding expression.
All the navigation rules are in WEB-INF/pages.xm l file, and discussed in Section 7.7,
“Navigation”.
T he main page of the application displays search results dynamically as you type and pass a selected
hotel to the selectHotel() method of HotelBookingAction.
T he 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. T hink of the
code as a list of scripted actions that implement the various steps of the conversation.
43
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Example 1.18. HotelBookingAction.java
44
Chapter 1. Seam Tutorial
@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
private FacesMessages facesMessages;
@In
private Events events;
@Logger
private Log log;
private boolean bookingValid;
@Begin
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
45
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
{
bookingValid=true;
}
}
public boolean isBookingValid()
{
return bookingValid;
}
@End
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
public void destroy() {}
}
A conversation-scoped stateful session bean uses an EJB3 extended persistence context, to
manage entity instances for the entire life cycle of the stateful session bean.
T he @ Out annotation declares that an attribute value is outjected to a context variable after
method invocations. In this case, the hotel context variable is set to the value of the hotel
instance variable after every action listener invocation completes.
T he @ Begin annotation specifies that the annotated method begins a long-running
conversation, so the current conversation context is not destroyed at the end of the request.
Instead, the current conversation context is re-associated with every request from the current
window, and destroyed either by timeout due to conversation inactivity or invocation of a
matching @ End method.
T he @ End annotation specifies that the annotated method ends the current long-running
conversation, so the current conversation context is destroyed at the end of the request.
T his EJB remove method is called when Seam destroys the conversation context. Do not
forget to define this method!
HotelBookingAction contains all the action listener methods that implement selection, booking, and
booking confirmation; and holds the state related to this work in its instance variables. T his code is much
cleaner and simpler than getting and setting HttpSession attributes.
Also, you can have multiple isolated conversations per log in session. Log in, run a search, and navigate
to different hotel pages in multiple browser tabs. You can work on creating two different hotel
reservations at the same time. If you leave one conversation inactive for a long time, Seam will eventually
time out that conversation and destroy its state. If, after ending a conversation, you backtrack to a page
of that conversation and try to perform an action, Seam will detect that the conversation was already
ended, and redirect you to the search page.
1.4.4. The Seam Debug Page
T he WAR also includes seam -debug.jar. T o make the Seam debug page available, deploy seam debug.jar in WEB-INF/lib alongside Facelets, and set the debug property of the init component
as shown here:
46
Chapter 1. Seam Tutorial
<core:init jndi-pattern="@[email protected]" debug="true"/>
T he debug page allows you to browse and inspect the Seam components in Seam contexts associated
with your current log in session. Just point your browser at http://localhost:8080/seam booking/debug.seam .
1.5. Nested conversations: extending the Hotel Booking example
1.5.1. Introduction
Long-running conversations allow you to easily maintain state consistency in an application, even in the
face of multi-window operation and back-buttoning. However, simply beginning and ending a longrunning conversation is not always enough. Depending on the requirements of the application, there can
be inconsistency between user expectations and the application state.
T he nested booking application extends the features of the hotel booking application to incorporate
room selection. Each hotel has a list of available rooms from which you can select a room. T his requires
the addition of a room selection page in the hotel reservation flow.
47
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
You can now select an available room to be included in the booking. If room selection is left in the same
conversation context, it can lead to issues with state consistency — if a conversation variable changes,
it affects all windows operating within the same conversation context.
For example, you clone the room selection screen in a new window. You then select the Wonderful Room
and proceed to the confirmation screen. T o check the cost of a more expensive room, you return to the
original window, select the Fantastic Suite for booking, and again proceed to confirmation. After reviewing
the total cost, you return to the window showing Wonderful Room to confirm.
In this scenario, if all the states are stored in the conversation, flexibility for multi-window operation within
the same conversation are limited. Nested conversations allow you to achieve correct behavior even
when contexts vary within the same conversation.
1.5.2. Understanding Nested Conversations
T he following code shows the behavior of the hotel booking application with intended behavior for
nested conversations. T hink of the code as a set of steps to be read in a sequence.
48
Chapter 1. Seam Tutorial
Example 1.19. RoomPreferenceAction.java
49
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
@Stateful
@Name("roomPreference")
@Restrict("#{identity.loggedIn}")
public class RoomPreferenceAction implements RoomPreference
{
@Logger
private Log log;
@In private Hotel hotel;
@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)
public String selectPreference()
{
log.info("Room selected");
this.room = this.roomSelection;
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)
public String cancel()
{
log.info("ending conversation");
50
Chapter 1. Seam Tutorial
return "cancel";
}
@Destroy @Remove
public void destroy() {}
}
T he hotel instance is injected from the conversation context. T he hotel is loaded through an
extended persistence context so that the entity remains managed throughout the conversation.
T his allows you to load the availableRoom s through a @ Factory method by simply
walking the association.
When @ Begin(nested=true) is encountered, a nested conversation is pushed onto the
conversation stack. When executing within a nested conversation, components still have
access to all outer conversation states. However, setting values in the nested conversation’s
state container does not affect the outer conversation. In addition, nested conversations can
exist concurrently stacked on the same outer conversation, allowing independent state for
each.
T he room Selection is outjected to the conversation based on the
@ DataModelSelection. Note that because nested conversation has an independent
context, the room Selection is only set into the new nested conversation. If you select a
different preference in another window or tab a new nested conversation is started.
T he @ End annotation pops the conversation stack and resumes the outer conversation. T he
room Selection is destroyed along with the conversation context.
When you begin a nested conversation, it is pushed onto the conversation stack. In the
nestedbooking example, the conversation stack consists of the external long-running conversation
(the booking) and each of the nested conversations (room selections).
51
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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"
rendered="#{availableRooms.rowCount > 0}">
<h:column>
<f:facet name="header">Name</f:facet>
#{room.name}
</h:column>
<h:column>
<f:facet name="header">Description</f:facet>
#{room.description}
</h:column>
<h:column>
<f:facet name="header">Per Night</f:facet>
<h:outputText value="#{room.price}">
<f:convertNumber type="currency" currencySymbol="$"/>
</h:outputText>
</h:column>
<h:column>
<f:facet name="header">Action</f:facet>
<s:link id="selectRoomPreference"
action="#{roomPreference.selectPreference}">Select</s:link>
</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"/>
</div>
</div>
</h:form>
</div>
When requested from EL, the #{availableRoom s} are loaded by the @ Factory method
defined in Room PreferenceAction. T he @ Factory method is executed only once to load
the values into the current context as a @ DataModel instance.
Invoking the #{room Preference.selectPreference} action results in the row being
selected and set into the @ DataModelSelection. T his value is outjected to the nested
conversation context.
Revising the dates simply return to the /book.xhtm l. Note that you have not yet nested a
52
Chapter 1. Seam Tutorial
conversation (no room preference has been selected), so the current conversation can be
resumed. T he <s:button> component propagates the current conversation when displaying
the /book.xhtm l view.
T he following code shows how you can confirm the booking of a selected room by extending the
behavior of the HotelBookingAction.
53
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Example 1.21. HotelBookingAction.java
54
Chapter 1. Seam Tutorial
@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";
}
55
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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)
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);
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)
public void cancel() {}
@Destroy @Remove
public void destroy() {}
}
Annotating an action with @ End(root=true) ends the root conversation which effectively
destroys the entire conversation stack. When a conversation is ended, its nested
conversations are also ended. As root is the conversation that started the nested
conversation, this is a simple way to destroy and release all the states associated with a
workspace once the booking is confirmed.
T he room Selection is associated with the booking only on user confirmation. Outjecting
values to the nested conversation context does not impact the outer conversation, and objects
injected from the outer conversation are injected by reference. T herefore, any change in these
objects is reflected in the parent conversation and other concurrent nested conversations.
By annotating the cancellation action with @ End(root=true,
beforeRedirect=true)you can destroy and release all the states associated with the
workspace, before redirecting the user back to the hotel selection view.
Confirming a booking will always result in the correct hotel and room preference with the nested
conversation model.
1.6. Bookmarkable URLs with the Blog example
Seam makes it easy to implement applications that keep the state on the server side. However, serverside state is not always appropriate, particularly for functionality that serves up content. For this,
application state is often stored as part of the URL, so that a page can be accessed through a bookmark
at any time. T he blog example shows how to implement an application that supports bookmarking
throughout, even on the search results page. T his example demonstrates Seam's management of
application state in the URL.
56
Chapter 1. Seam Tutorial
T he blog example demonstrates the use of "pull"-style model view control (MVC). In this, the view pulls
data from components as it is being rendered rather than using action listener methods to retrieve and
prepare data for the view.
1.6.1. Using "pull"-style MVC
T he following snippet from the index.xhtm l facelets page, displays a list of recent blog entries:
57
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<h:dataTable value="#{blog.recentBlogEntries}" var="blogEntry" rows="3">
<h:column>
<div class="blogEntry">
<h3>#{blogEntry.title}</h3>
<div>
<s:formattedText value="#{blogEntry.excerpt==null ?
blogEntry.body : blogEntry.excerpt}"/>
</div>
<p>
<s:link view="/entry.xhtml" rendered="#{blogEntry.excerpt!=null}"
propagation="none" value="Read more...">
<f:param name="blogEntryId" value="#{blogEntry.id}"/>
</s:link>
</p>
<p>
[Posted on&#160;
<h:outputText value="#{blogEntry.date}">
<f:convertDateTime timeZone="#{blog.timeZone}"
locale="#{blog.locale}" type="both"/>
</h:outputText>]
&#160;
<s:link view="/entry.xhtml" propagation="none" value="[Link]">
<f:param name="blogEntryId" value="#{blogEntry.id}"/>
</s:link>
</p>
</div>
</h:column>
</h:dataTable>
If you navigate to this page from a bookmark, the #{blog.recentBlogEntries} data used by the
<h:dataT able>
is retrieved — "pulled" — when required, by a Seam component named blog. T his 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;
@Unwrap
public Blog getBlog()
{
return (Blog) entityManager.createQuery("select distinct b from Blog b
left join fetch b.blogEntries")
.setHint("org.hibernate.cacheable", true)
.getSingleResult();
}
}
T he blog component uses a seam-managed persistence context. Unlike the other examples,
this persistence context is managed by Seam, instead of the EJB3 container. T he persistence
context spans the entire web request, allowing you to avoid exceptions that occur when
accessing unfetched associations in the view.
T he @ Unwrap annotation requests Seam for the return value of the Blog method, instead of
the actual BlogService component to clients. T his is the Seam manager component
pattern.
58
Chapter 1. Seam Tutorial
T his method stores the basic view content. T o bookmark form submission results, like a search results
page, there are several other definitions are required.
1.6.2. Bookmarkable search results page
T he blog example has a small form at the top right of each page that allows you to search for blog
entries. T his form is defined in the m enu.xhtm l file, which is included in the Facelets template
tem plate.xhtm l:
<div id="search">
<h:form>
<h:inputText value="#{searchAction.searchPattern}"/>
<s:button value="Search" action="/search.xhtml"/>
</h:form>
</div>
T o implement a bookmarkable search results page, perform a browser redirect after the search form
submission is processed. As the JSF view ID is used as the action outcome, Seam automatically
redirects to the view ID when the form is submitted. You can also define a navigation rule as follows:
<navigation-rule>
<navigation-case>
<from-outcome>searchResults</from-outcome>
<to-view-id>/search.xhtml</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
If you use a navigation rule, the form will look as follows:
<div id="search">
<h:form>
<h:inputText value="#{searchAction.searchPattern}"/>
<s:button value="Search" action="searchResults"/>
</h:form>
</div>
However, to get a bookmarkable URL like http://localhost:8080/seam -blog/search/, the
values submitted with the form must be included in the URL. T here is no easy way to do this with JSF,
but with Seam, only two features are required: page parameters and URL rewriting. T hese features are
defined in WEB-INF/pages.xm l:
<pages>
<page view-id="/search.xhtml">
<rewrite pattern="/search/{searchPattern}"/>
<rewrite pattern="/search"/>
<param name="searchPattern" value="#{searchService.searchPattern}"/>
</page>
...
</pages>
T he 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.
T he URL for a search on the term book is ordinarily http://localhost:8080/seam blog/seam /search.xhtm l?searchPattern=book. Seam can simplify this URL by using a rewrite
59
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
rule. T he 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.xhtm l?
searchPattern= book) can be written as http://localhost:8080/seam -blog/search/book.
As with page parameters, rewriting URLs is bidirectional. T his 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. T he entire process is handled transparently. T he only requirement for rewriting URLs is
to enable the rewrite filter in com ponents.xm l:
<web:rewrite-filter view-mapping="/seam/*" />
T he redirect takes you to the search.xhtm l 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.
60
Chapter 1. Seam Tutorial
@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 REST ful pages, so Seam provides the notion of a page
action. T he blog example uses a page action for the blog entry page, entry.xhtm l.
Note
We use push-style for the sake of an example, but this particular function will be simpler to
implement with pull-style MVC.
61
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
T he entryAction component works much like an action class in a traditional push-MVC actionoriented 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.xm l:
<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
T he example uses page actions for post validation and pageview counter. Also a parameter is
used in the page action method binding. T his is not a standard JSF EL feature, but Seam allows it
both here and in JSF method bindings.
When the entry.xhtm l page is requested, Seam first binds the blogEntryId page parameter to the
model.
62
Chapter 1. Seam Tutorial
Note
Because of URL rewriting, the blogEntryId parameter name does not appear in the URL.
Seam then runs the page action, which retrieves the required data — the blogEntry — and places it in
the Seam event context. Finally, it renders the following:
<div class="blogEntry">
<h3>#{blogEntry.title}</h3>
<div>
<s:formattedText value="#{blogEntry.body}"/>
</div>
<p>
[Posted on&#160;
<h:outputText value="#{blogEntry.date}">
<f:convertDateTime timeZone="#{blog.timeZone}" locale="#{blog.locale}"
type="both"/>
</h:outputText>]
</p>
</div>
If a blog entry is not found in the database, the EntryNotFoundException exception is raised. T his
exception should result in a 404 error, and not a 505 error, so annotate the exception class as:
@ApplicationException(rollback=true)
@HttpError(errorCode=HttpServletResponse.SC_NOT_FOUND)
public class EntryNotFoundException extends Exception {
EntryNotFoundException(String id) {
super("entry not found: " + id);
}
}
An alternative implementation of the example does not use the parameter in the method binding:
@Name("entryAction")
@Scope(STATELESS)
public class EntryAction {
@In(create=true)
private Blog blog;
@In @Out
private BlogEntry blogEntry;
public void loadBlogEntry() throws EntryNotFoundException {
blogEntry = blog.getBlogEntry( blogEntry.getId() );
if (blogEntry==null) throw new EntryNotFoundException(id);
}
}
<pages>
...
<page view-id="/entry.xhtml" action="#{entryAction.loadBlogEntry}">
<param name="blogEntryId" value="#{blogEntry.id}"/>
</page>
...
</pages>
T he implementation used depends entirely upon personal preference.
T he blog example also demonstrates simple password authentication, blog posting, page fragment
caching, and atom feed generation.
63
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
64
Chapter 2. Migration from 2.2 to 2.3
Chapter 2. Migration from 2.2 to 2.3
T his migration guide assumes that you are migrating from Seam 2.2 to Seam 2.3. If you are migrating
from Seam 1.2 or 2.0, see the jboss-seam -x.y.z.Final/seam 2m igration.txt and jbossseam -x.y.z.Final/seam 21m igration.txt guides also.
Important
Seam 2.3 has been re-architected to support features of Java EE6 on Red Hat JBoss Enterprise
Application Platform 6. jBPM3 is deemed unsuitable for EE6 and JBoss Enterprise Application
Platform 6. Features depending on jBPM3 (pageflows and business processes) are removed
from Seam 2.3.
2.1. Migration of XML Schemas
2.1.1. Seam schema migration
Update the XML schemas for validation files that use Seam 2.2 XSDs to refer to 2.3 XSDs; notice the
version change. T he current namespace pattern is www.jboss.org/schem a/seam /* and the
schemaLocation URL is www.jboss.org/schem a/seam /* _-2.3.xsd, where * is a Seam module.
Warning
T he old XML namespace http://jboss.com /products/seam /* is invalid now. You must
update the com ponents.xm l file with the new namespace.
T he following snippet is an example of component declaration for Seam 2.2:
Example 2.1. Before migration of Seam com ponents.xm l
<?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/theme2.2.xsd
http://jboss.com/products/seam/cache http://jboss.com/products/seam/cache2.2.xsd
http://jboss.com/products/seam/web http://jboss.com/products/seam/web2.2.xsd
http://jboss.com/products/seam/components
http://jboss.com/products/seam/components-2.2.xsd">
Migrated declaration of com ponents.xm l for version 2.3:
65
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Example 2.2. Migrated com ponents.xm l
<?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/security2.3.xsd
http://jboss.org/schema/seam/theme http://jboss.org/schema/seam/theme2.3.xsd
http://jboss.org/schema/seam/cache http://jboss.org/schema/seam/cache2.3.xsd
http://jboss.org/schema/seam/web http://jboss.org/schema/seam/web-2.3.xsd
http://jboss.org/schema/seam/components
http://jboss.org/schema/seam/components-2.3.xsd">
Update pages.xm l files and schemas.
Example 2.3. Before migration of Seam pages.xm l
<?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.xm l
<?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. Update the following descriptors:
persistence.xm l for using JPA 2.
web.xm l for using Servlet 3.0 and Web application.
application.xm l for using Enterprise Java 6 application.
faces-config.xm l to specify advanced configuration for JSF 2 (this descriptor file is optional).
Following are examples of changed headers with correct versions:
66
Chapter 2. Migration from 2.2 to 2.3
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. EE 6 technology upgrades require a change in the XML schema declaration.
For details, 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 a new technology. Seam uses Hibernate
Validator, which is a reference implementation.
You need to migrate from using org.hibernate.validator.* Hibernate validator annotations to
javax.validation.constraint.* . Seam examples use many of the following annotations (Using
Bean Validation):
org.hibernate.validator.Length to javax.validation.constraint.Size.
org.hibernate.validator.NotNull to javax.validation.constraint.NotNull.
org.hibernate.validator.Pattern to javax.validation.constraint.Pattern.
2.2.2. Migration of JSF 1 to JSF 2 Facelets templates
For using JSF 2 in a simple application, migrate only web.xm l file. T he configuration file facesconfig.xm l is not required. However, for information on migrating faces-config.xm l file, see
Example 2.8, “faces-config.xml”.
67
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
All JSF templates in your application must use facelets technology only, as JSP is deprecated.
In the facelet templates, convert <head>/<body> tags to ><h:head>/<h:body>, respectively.
T he upgrade from JSF 1.x to JSF 2.x differs depending on the JSF components used (for example,
Richfaces or Icefaces). You may need to upgrade libraries entirely. See a component framework
documentation for details of these changes. Migration of these independent components is not covered
here.
2.2.3. Using s:validate and s:validateAll in JSF 2
In Seam 2.3, JSF 2 adds bean validators to all the fields by default. T he s:validateAll tag ignores
the fields to which JSF 2 has added bean validators.
T o use the s:validate tag or s:validateAll tag in Seam 2.3, disable the JSF default bean
validator view settings. Set javax.faces.validator.DISABLE_DEFAULT _BEAN_VALIDAT OR
context parameter to true in the application's web.xm lfile.
2.2.4. Migration to JPA 2.0
T o use JPA 2, change the version in persistence.xm l file to 2.0, see Example 2.5, “persistence.xml”.
T he version in application.xm l file should be 6 if you are using EAR (Example 2.6,
“application.xml”), or version 3.0 in web.xm l file if you use only WAR archives (Example 2.7, “web.xml”).
Note
Most applications can use WAR with EJB 3.1 and not require to be packaged as an EAR.
JPA 2.0 is backward compatible with JPA 1.0, so do not migrate JPA annotations or classes.
2.2.5. Using compatible JNDI for resources
Java EE 6 brings a new set of standardized global rules for creating portable JNDI syntax. Change all
JNDI strings from _your_application_/#{ejbNam e}/local to java:app/_applicationm odule-nam e_/#{ejbNam e}. For example, WEB-INF/com ponents.xm l jndiPattern changes
from:
seam-mail/#{ejbName}/local
to:
java:app/seam-mail-ejb/#{ejbName}
2.3. Red Hat JBoss Enterprise Application Platform 6 deployment
2.3.1. Deployment changes
T he next level of migration is the migration of the target runtime. Seam 2.3 uses JBoss Enterprise
Application Platform 6 as its default target runtime.
Change the JNDI datasource in the persistence.xm l file from java:/DefaultDS to
java:jboss/datasources/Exam pleDS.
Utilize refactored classloading. Classloading of bundled or provided libraries can be managed in jbossdeploym ent-structure.xm l file or in MET A-INF/MANIFEST .MF file, in the Dependencies
section. We recommend you to use the jboss-deploym ent-structure.xm l file, which should be
placed in the MET A-INF directory of your WAR or EAR application, based on your application type.
For EAR projects, the jboss-deploym ent-structure.xm l file is located in the
68
Chapter 2. Migration from 2.2 to 2.3
_your_ear_/MET A-INF directory.
For Web (non-ear) projects, the jboss-deploym ent-structure.xm l file is located in the
_your_war_/WEB-INF directory.
T he 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 include a database descriptor (*-ds.xml) file in your project in the MET A-INF directory. T he data
source is deployed automatically when you deploy the application. T he structure of the database
descriptor (*-ds.xml) file is changed to be based on IronJacamar (IronJacamar). Iron-Jacamar is the
JBoss JCA (Java Connector Architecture) project. Example 2.9, “Sample Seam 2.2 Datasource
Descriptor File” is the former datasource for JBoss. Example 2.10, “Ironjacamar Datasource Descriptor
File” is the conversion of the datasource to IronJacamar using the same driver, url, and credentials.
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" usejava-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>
69
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
2.4. Changes in the testing framework
Seam does not support SeamT est and JBoss Embedded legacy components (as supported in Seam
2.2).
Arquillian is the replacement for JBoss Embedded. Extend org.jboss.seam .m ock.JUnitSeam T est
instead of org.jboss.seam .m ock.Seam T est. DBUnit testing is provided by
org.jboss.seam .m ock.DBJUnitSeam T est instead of
org.jboss.seam .m ock.DBUnitSeam T est. Assertion issues mean JUnit is the preferred testing
framework over T estNG. Complete the migration to Junit and Arquillian as follows:
1. Add the following annotation to your test class:
@RunWith(Arquillian.class)
2. In your test class, extend org.jboss.seam .m ock.JUnitSeam T est instead of
org.jboss.seam .m ock.Seam T est.
3. Add a method for creating ShrinkWrap deployment; Seam examples and integration testsuite uses
helper class for this purpose. For example, see the Booking sample test modules
jboss-seam -x.y.z.Final/exam ples/booking/bookingejb/src/test/java/org/jboss/seam /exam ple/booking/test/Deploym ents.java.
package org.jboss.seam.example.booking.test;
import
import
import
import
import
java.io.File;
org.jboss.seam.example.booking.Booking;
org.jboss.shrinkwrap.api.ShrinkWrap;
org.jboss.shrinkwrap.api.spec.WebArchive;
org.jboss.shrinkwrap.resolver.api.maven.Maven;
public class Deployments
{
public static WebArchive bookingDeployment()
{
File[] libs = Maven.resolver().loadPomFromFile("pom.xml")
.importCompileAndRuntimeDependencies()
.resolve("org.jboss.seam:jboss-seam")
.withTransitivity().asFile();
return ShrinkWrap.create(WebArchive.class, "seam-booking.war")
.addPackage(Booking.class.getPackage())
.addAsWebInfResource("META-INF/ejb-jar.xml", "ejb-jar.xml")
.addAsResource("import.sql")
.addAsResource("persistence.xml", "META-INF/persistence.xml")
.addAsWebInfResource("components.xml")
.addAsWebInfResource("jboss-deployment-structure.xml")
.addAsResource("seam.properties")
.addAsWebInfResource("web.xml")
.addAsLibraries(libs);
}
}
4. Add a method for creating test deployment archive as:
@Deployment(name="_your_test_name_")
@OverProtocol("Servlet 3.0")
public static org.jboss.shrinkwrap.api.Archive<?> createDeployment(){}
T he following example is taken from the Booking example test suite:
70
Chapter 2. Migration from 2.2 to 2.3
@Deployment(name="BookingTest")
@OverProtocol("Servlet 3.0")
public static Archive<?> createDeployment()
{
return Deployments.bookingDeployment();
}
In the Booking example, call the already created bookingDeployment() method from the
Deployment class.
5. Add the arquillian.xm l file in the root of your classpath to run Arquillian tests. T he file
content should specify the path to a remote or managed container and some specific options for
JVM or Arquillian. An example of an arquillian file is available at
jboss-seam -x.y.z.Final/exam ples/booking/booking-ejb/src/test/resourcesintegration/arquillian.xm l.
<?xml version="1.0" encoding="UTF-8"?>
<arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://jboss.org/schema/arquillian"
xsi:schemaLocation="http://jboss.org/schema/arquillian
http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
<engine>
<property name="deploymentExportPath">target/</property>
</engine>
<container qualifier="jboss" default="true">
<configuration>
<property name="javaVmArguments">-Xmx1024m -XX:MaxPermSize=1024m
${jacoco.agent}</property>
<property name="serverConfig">standalone.xml</property>
</configuration>
</container>
</arquillian>
2.5. Moving from JBoss Cache to Infinispan Tree
In the Seam com ponents.xm l file, change all occurrences of<cache:jboss-cache3-provider/>
to <cache:infinispan-cache-provider/>
.
Migrate the JBoss Cache XML configuration to Infinispan XML; you can use the migration tool provided
with Infinispan. For more information see:
https://docs.jboss.org/author/display/ISPN/Configuration+Migration+T ools.
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 (BOM) is a set of dependeny elements in the <dependencyManagem ent> section
that is used to declare dependencies and their versions used in the application. T he usage of Seam
BOM is shown in Example 2.11, “Seam BOM usage”. T he Seam BOM is deployed in JBoss Maven
repository. For further information, see the Maven Repository User Guide in the Red Hat JBoss Web
Framework Kit.
71
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Example 2.11. Seam BOM usage
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jboss.seam</groupId>
<artifactId>bom</artifactId>
<version>2.3.1.Final-redhat-2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
...
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jboss.seam</groupId>
<artifactId>jboss-seam</artifactId>
<type>ejb</type>
</dependency>
...
</dependencies>
For further assistance, refer to the updated Seam examples distributed with Seam.
72
Chapter 3. Getting started with seam-gen
Chapter 3. Getting started with seam-gen
Seam includes a command line utility that makes it easy to set up an Eclipse project, generate a simple
Seam skeleton code, and reverse-engineer an application from a pre-existing database. T his is an easy
way to familiarize yourself with Seam. Seam-gen works best in conjunction with Red Hat JBoss
Enterprise Application Platform.
Seam-gen can be used without Eclipse, but this guide focuses on using seam-gen with Eclipse. If you
prefer not to use Eclipse, you can still follow this guide — all steps can be performed from the command
line.
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. T herefore, we recommend you to run JBoss in a JVM with a large
perm gen space during development.
If you are running JBoss from JBoss IDE, you can configure this in the server launch configuration, under
"VM arguments". We suggest the following values:
-Xms512m -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=512m
T he 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
Configure seam-gen for your environment:
cd jboss-seam-2.3.1
seam setup
A prompt appears for the required information:
73
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
~/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]
[n], ) y
(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/jbossseam/seam-gen/build.properties
74
Chapter 3. Getting started with seam-gen
[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 $
T he tool provides defaults, accept the defaults by pressing enter at the prompt.
Choose the deployment type for your project, EAR deployment or WAR deployment. EAR projects
support EJB 3.0 and require Java EE 5. WAR projects do not support EJB 3.0, but can be deployed in a
J2EE environment. T he packaging of a WAR is simple to understand. If you have installed an EJB3ready application server like JBoss, choose EAR. Otherwise, choose WAR. T he remaining document
assumes an EAR deployment, however, you can follow the same steps for a WAR deployment.
If you are working with an existing data model, inform seam-gen that the tables already exist in the
database.
T he settings are stored in seam -gen/build.properties, but you can also modify them by running
seam setup a second time.
Create a new project in your Eclipse workspace directory, by typing:
seam new-project
C:\Projects\jboss-seam>seam new-project
Buildfile: build.xml
...
new-project:
[echo] A new Seam project named 'helloworld' was created in the C:\Projects
directory
[echo] Type 'seam explode' and go to http://localhost:8080/helloworld
[echo] Eclipse Users: Add the project into Eclipse using File > New > Project and
select General > Project (not Java Project)
[echo] NetBeans Users: Open the project in NetBeans
BUILD SUCCESSFUL
Total time: 7 seconds
C:\Projects\jboss-seam>
T his copies the Seam jars, dependent jars, and the JDBC driver jar to a new Eclipse project. It also
generates the required resources and configuration files; a facelets template file and stylesheet, Eclipse
metadata, and an Ant build script. T he Eclipse project is automatically deployed to an exploded directory
structure in JBoss Enterprise Application Platform when you add the project using New ->
Project... -> General -> Project -> Next, typing the Project nam e (helloworld in this
case), and then clicking Finish. Do not select Java Project from the New Project wizard.
If your default JDK in Eclipse is not a Java SE 6 JDK, select a Java SE 6 compliant JDK using Project
-> Properties -> Java Com piler.
Alternatively, deploy the project from outside Eclipse by typing seam explode.
Go to http://localhost:8080/helloworld to see a welcome page. T his is a facelets page,
view/hom e.xhtm l, using the template view/layout/tem plate.xhtm l. You can edit this page, or
the template, in Eclipse, and see the results immediately, by clicking refresh in the browser.
T he XML configuration documents are mostly standard Java EE and similar between all Seam projects.
(T hey are so easy to write that even seam-gen can do it.)
75
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
3.3. Creating a new action
T ask:
T his 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. As we have added a new Seam component, it is necessary to restart the exploded directory
deployment. You can do this by typing seam restart, or by running the restart target in the
generated project's build.xm l file from within Eclipse. Alternatively, you can edit the
resources/MET A-INF/application.xm l file in Eclipse.
You do not need to restart JBoss each time you change the application.
4. Go to http://localhost:8080/helloworld/ping.seam and click the button. T he code
behind this action is in the project src directory. Add a breakpoint to the ping() method, and
click the button again.
76
Chapter 3. Getting started with seam-gen
3.4. Creating a form with an action
T he next step is to create a form. T ype: 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]
[copy]
[copy]
[copy]
[copy]
[copy]
[echo]
Creating a new stateful session bean component with an action method
Copying 1 file to C:\Projects\hello\src\hot\com\hello
Copying 1 file to C:\Projects\hello\src\hot\com\hello
Copying 1 file to C:\Projects\hello\src\hot\com\hello\test
Copying 1 file to C:\Projects\hello\view
Copying 1 file to C:\Projects\hello\src\hot\com\hello\test
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. (T o 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. T he 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 seamgen.
3.6. Generating an application from existing JPA/EJB3 entities
Place your existing, valid entity classes in the src/m ain directory. Now, type: seam generate-ui
Restart the deployment, and go to http://localhost:8080/helloworld.
3.7. Deploying the application as an EAR
Many changes are required before deploying the application with standard Java EE 5 packaging.
Remove the exploded directory by running seam unexplode. T o deploy the EAR, either type seam
deploy at the command prompt, or run the deploy target of the generated project build script. T o
77
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
undeploy, use seam undeploy or the undeploy target.
By default, the application deploys with the dev profile. T he EAR includes the persistence-dev.xm l
and im port-dev.sql files, and deploys m yproject-dev-ds.xm l. You can change the profile to
prod profile by typing: seam -Dprofile=prod deploy
You can also define new deployment profiles for your application. Add appropriately named files to your
project — for example, persistence-staging.xm l, im port-staging.sql, and m yprojectstaging-ds.xm l — 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 com ponents.xm l file to enable
debug mode in Seam and Facelets:
<core:init debug="true">
Warning
Hot deployment of Facelets does not work if the hot deployment scanner is not enabled for the
server profile.
T he following files may now be redeployed without requiring a full restart of the web application:
any Facelets page
any pages.xm l file
T o change any Java code, do a full restart of the application. In JBoss, this can be accomplished by
touching the top-level deployment descriptor: application.xm l for an EAR deployment, or web.xm l
for a WAR deployment.
Seam supports incremental redeployment of JavaBean components for a fast edit/compile/test cycle. T o
use this, deploy the JavaBean components in the WEB-INF/dev directory. Here, the JavaBean
components are loaded by a special Seam classloader instead of the WAR or EAR classloader.
T his function has the following limitations:
T he 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 com ponents.xm l cannot be hot-deployed.
Hot-deployable components are not visible to classes deployed outside WEB-INF/dev.
Seam debug mode must be enabled and jboss-seam -debug.jar must be included in WEBINF/lib.
T he Seam filter must be installed in web.xm l.
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.
78
Chapter 4. Getting started with Red Hat JBoss D eveloper Studio
Chapter 4. Getting started with Red Hat JBoss Developer
Studio
JBoss Developer Studio is a collection of Eclipse plug-ins like a project creation wizard for Seam, a
content assistant for the Unified Expression Language (EL) in 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 so on. For more information, see the Red Hat 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
T he Seam Developer Tools Reference Guide is available at http://docs.redhat.com/docs/enUS/JBoss_Developer_Studio/index.html.
4.1. Hot deployment with JBoss Developer Studio
JBoss Developer Studio supports incremental hot deployment of Facelets page and pages.xm l file out
of the box. However, to change Java code, restart the full application by doing a Full Publish.
Seam supports incremental redeployment of JavaBean components for a fast edit/compile/test cycle. T o
use this feature, the JavaBean components must be deployed into the WEB-INF/dev directory by a
special Seam classloader instead of the WAR or EAR classloader.
T he hot deployment of JBoss Developer Studio has the following limitations:
T he 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 through com ponents.xm l file may not be hot-deployed
T he hot-deployable components are not visible to classes deployed outside WEB-INF/dev directory
Seam debug mode must be enabled and jboss-seam -debug.jar file must be in WEB-INF/lib
directory
T he Seam filter must be installed in web.xm l file
Errors may occur if the system is placed under 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.
79
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Chapter 5. The contextual component model
T he two core concepts of Seam are the notions of a context, and a component. Components are stateful
objects, usually Enterprise JavaBeans (EJBs). An instance of a component is associated with a context,
and assigned a name within that context. Bijection provides a mechanism for aliasing internal component
names (instance variables) to contextual names, that allows component trees to be dynamically
assembled and reassembled by Seam.
5.1. Seam contexts
Seam has many built-in contexts that are created and destroyed by the framework. T he application does
not control context demarcation through explicit Java API calls. Contexts are usually implicit. In some
cases, however, contexts are demarcated with annotations.
T here are many basic contexts as follows:
Stateless context
Event (for instance, a request) context
Page context
Conversation context
Session context
Application context
Some basic contexts serve similar purposes in Servlet and related specifications. One you may not have
encountered previously is the conversation context. One reason that the state management in web
applications is fragile and error-prone, is that, the three built-in contexts (request, session, and
application) are not meaningful for business logic. A user login session, for example, is an arbitrary
construct in terms of the application workflow. T herefore, most Seam components are scoped to the
conversation, as these are the most meaningful contexts in terms of the application.
5.1.1. Stateless context
Components that are truly stateless (primarily stateless session beans) always operate in the stateless
context — the absence of a context, since the instance Seam resolves is not stored. Stateless
components are arguably object-oriented, but they are developed regularly and thus form an important
part of any Seam application.
5.1.2. Event context
T he event context is the "narrowest" stateful context, and expands the notion of the web request to
cover other event types. T he 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
T he 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. T his is especially useful for functionality such as clickable lists, where the
list is backed by changing data on the server side. T he state is 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
T he conversation context is a central concept to Seam. A conversation is a single unit of work from the
user's perspective. In reality, it may span several interactions with a user — several requests, and data
transactions. But to the user, a conversation solves a single problem. For example, the processes of
80
Chapter 5. The contextual component model
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.
T he conversation context ensures that states from different conversations do not collide and cause
bugs.
Some conversations last only for a single request. Conversations that span multiple requests must be
demarcated with annotations provided by Seam.
Conversations can be nested, with one conversation taking place inside a broader conversation. T his 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. T here 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
T he 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. T hese work similar to the session or
request attributes in the Servlet specification. Any value may be bound to a context variable, but they are
usually bound to Seam component instances.
T he context variable name identifies a component instance within a context. (T he context variable name
usually matches the component name.) You can programmatically access a named component instance
in a particular scope with the Contexts class, which provides access to several thread-bound
instances of the Context interface:
User user = (User) Contexts.getSessionContext().get("user");
You may also set or change the value associated with a name:
Contexts.getSessionContext().set("user", user);
However, components are usually obtained from a context through injection. Component instances are
subsequently given to contexts through outjection.
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
81
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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. T he Servlet container allows all threads to run concurrently, without ensuring threadsafeness. T he EJB container allows concurrent access of stateless components, and generates an
exception when multiple threads access a stateful session bean. T his 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. T herefore, Seam adds a concurrency
management layer to its context model.
Session and application contexts are multi-threaded in Seam, allowing concurrent requests to be
processed concurrently. Event and page contexts are single-threaded. Seam serializes concurrent
requests within a long-running conversation context in order to enforce a single thread per conversation
per process model for the conversation context.
As session context is multi-threaded and often contains volatile state, Seam always protects sessionscoped 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.
T his concurrency model means that AJAX clients can safely use volatile session and conversational
state, without the need for any special work on the part of the developer.
Important
Be warned that requests to stateful session beans are not serialized by Seam anymore.
Serialization of requests to Stateful session beans are controlled by EJB container, so there is no
need for Seam to duplicate that. So @ Synchronized annotation is ignored on stateful session
beans.
5.2. Seam components
Seam components are Plain Old Java Objects (POJOs). Specifically, they are JavaBeans, or Enterprise
JavaBean 3.0 (EJB3). While Seam does not require components to be EJBs, and can be used without an
EJB3-compliant container, Seam was designed with EJB3 in mind, and includes deep integration with
EJB3. Seam supports the following component types:
EJB3 stateless session beans
EJB3 stateful session beans
EJB3 entity beans (for instance, JPA entity classes)
JavaBeans
EJB3 message-driven beans
Spring beans (see Chapter 25, Spring Framework integration)
5.2.1. Stateless session beans
82
Chapter 5. The contextual component model
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. T hey 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. T hey can be accessed concurrently as a
new instance is used for each request. T he 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 Com ponent.getInstance()
or @ In(create=true). T hey 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 to the database is held by
stateful session beans. T his 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. T his feature allows Seam to
manage state life cycle, and ensures that there are no collisions between states related to concurrent
conversations.
Stateful session beans are often used as JSF action listeners, and as backing beans to provide
properties to JSF components for display or form submission.
By default, stateful session beans are bound to the conversation context. T hey 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. T his is a difference in comparison to previous Seam 2.2.x.
Seam stateful session bean components are instantiated with either Com ponent.getInstance() or
@ In(create=true). T hey 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. As entities have a
persistent identity in addition to their contextual identity, entity instances are bound explicitly in Java
code, rather than being instantiated implicitly by Seam.
Entity bean components do not support bijection, context demarcation, and invocation of an entity bean
trigger validation.
Entity beans are not usually used as JSF action listeners, but often function as backing beans to provide
properties to JSF components for display or form submission. T hey 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 Com ponent.getInstance() or
@ In(create=true), or directly instantiated with the new operator.
83
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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, and so on.
A later chapter discusses the use of Seam and Hibernate without an EJB container. In this case,
components are JavaBeans rather than session beans.
Note
In a clustered environment, it is less efficient to cluster conversation-scoped or session-scoped
Seam JavaBean components than it is to cluster stateful session bean components.
By default, JavaBeans are bound to the event context. Seam always serializes concurrent requests to
session-scoped JavaBeans.
Seam JavaBean components are instantiated with Com ponent.getInstance() or
@ In(create=true). T hey 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
T o perform actions such as bijection, context demarcation, and validation, Seam must intercept
component invocations. For JavaBeans, Seam controls component instantiation completely, and no
special configuration is required. For entity beans, interception is not required, as bijection and context
demarcation are not defined. For session beans, an EJB interceptor must be registered for the session
bean component. T his 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.xm l:
<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>
84
Chapter 5. The contextual component model
5.2.7. Component names
All Seam components require names. Assign a name with the @ Nam e annotation:
@Name("loginAction")
@Stateless
public class LoginAction implements Login { ... }
T his 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.
@ Nam e 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. T his is identical to JSF managed bean behavior, except that
Seam allows you to configure this mapping with annotations rather than XML. You can also
programmatically bind a component to a context variable. T his 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. T ake 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 { ... }
T he qualified component name can be used both in Java code and in JSF's expression language:
<h:commandButton type="submit" value="Login"
action="#{com.jboss.myapp.loginAction.login}"/>
As this is noisy, Seam also provides a means of aliasing a qualified name to a simple name. Add a line
like this to the com ponents.xm l 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. T he com ponents.xm l file included in the Seam JAR
defines the following namespaces:
85
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<components xmlns="http://jboss.org/schema/seam/components">
<import>org.jboss.seam.core</import>
<import>org.jboss.seam.cache</import>
<import>org.jboss.seam.transaction</import>
<import>org.jboss.seam.framework</import>
<import>org.jboss.seam.web</import>
<import>org.jboss.seam.faces</import>
<import>org.jboss.seam.international</import>
<import>org.jboss.seam.theme</import>
<import>org.jboss.seam.jms</import>
<import>org.jboss.seam.mail</import>
<import>org.jboss.seam.security</import>
<import>org.jboss.seam.security.management</import>
<import>org.jboss.seam.security.permission</import>
<import>org.jboss.seam.captcha</import>
<import>org.jboss.seam.excel.exporter</import>
<!-- ... --->
</components>
When attempting to resolve an unqualified name, Seam checks each of these namespaces, in order.
Additional application-specific namespaces can be included in your application's com ponents.xm l file.
5.2.8. Defining the component scope
T he @ Scope annotation allows you to override the scope (context) of a component to define the context
a component instance is bound to when instantiated by Seam.
@Name("user")
@Entity
@Scope(SESSION)
public class User { ... }
org.jboss.seam .ScopeT ype 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 it
becomes a conversation-scoped component. T he @ Role annotation allows you to define an additional
named role for a component, with a different scope — it allows you to bind the same component class to
different context variables. (Any Seam component instance can be bound to multiple context variables,
but this allows you to do it at the class level to take advantage of automatic instantiation.)
@Name("user")
@Entity
@Scope(CONVERSATION)
@Role(name="currentUser", scope=SESSION)
public class User { ... }
T he @ Roles annotation allows you to specify additional roles as required.
@Name("user")
@Entity
@Scope(CONVERSATION)
@Roles({ @Role(name="currentUser", scope=SESSION),
@Role(name="tempUser", scope=EVENT)})
public class User { ... }
5.2.10. Built-in components
Seam is implemented as a set of built-in interceptors and components. T his 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. T he built-in components are defined in
86
Chapter 5. The contextual component model
the Seam namespace org.jboss.seam .core, and in the Java package of the same name.
T he 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. T his is reasonable for stateless components — from
the client's perspective, all instances of a particular stateless component are interchangeable. However,
Seam emphasizes the use of stateful components, so traditional dependency injection as a construct is
less useful. Seam introduces the notion of bijection as a generalization of injection. In contrast to
injection, bijection is:
contextual
Bijection is used to assemble stateful components from different contexts. A component from a
wider context can even refer to a component from a narrower context.
bidirectional
Values are injected from context variables into attributes of the invoked component, and
returned (via outjection) to the context, allowing the invoked component to manipulate contextual
variable values simply by setting its own instance variables.
dynamic
As the value of contextual variables change over time, and as Seam components are stateful,
bijection takes place every time a component is invoked.
In essence, bijection allows you to alias a context variable to a component instance variable, by
specifying that the value of the instance variable is injected, outjected, or both. Annotations are used to
enable bijection.
T he @ 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:
87
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
@Name("loginAction")
@Stateless
public class LoginAction implements Login
{
User user;
@In
public void setUser(User user)
{
this.user=user;
}
...
}
By default, Seam performs a priority search of all contexts, using the name of the property or instance
variable being injected. You can specify the context variable name explicitly, for example,
@ In("currentUser").
If you want Seam to create an instance of the component, where there is no existing component instance
bound to the named context variable, you should specify @ In(create=true). If the value is optional (it
can be null), specify @ In(required=false).
For some components, specifying @ In(create=true) each time it is used can be repetitive. In such
cases, annotate the component @ AutoCreate. T his causes @ In(create=true)to be created
whenever required, even without the explicit use of create=true.
You can even inject the value of an expression:
@Name("loginAction")
@Stateless
public class LoginAction implements Login
{
@In("#{user.username}") String username;
...
}
Injected values are disinjected (that is, set to null) immediately after method completion and outjection.
(More information about component life cycle and injection can be found in the next chapter.)
T he @ 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;
}
...
}
88
Chapter 5. The contextual component model
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, as these annotations are not available in a J2EE environment,
Seam defines two additional component life cycle callbacks, equivalent to @ PostConstruct and
@ PreDestroy.
T he @ Create method is called after Seam instantiates a component. Components may define only one
@ Create method.
T he @ 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
@ Rem ove. T his method is called by Seam when the context ends.
Finally, the @ Startup annotation can be applied to any application-scoped or session-scoped
component. T he @ 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
T he @ Install annotation controls conditional installation of components that are required in some
deployment scenarios and not required in others. T his is useful when you want to:
mock out an infrastructural component in a test
change a component's implementation in certain deployment scenarios
install some components only if their dependencies are available. (T his is useful for framework
89
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
authors.)
@ Install allows you to specify precedence and dependencies.
T he precedence of a component is a number that Seam uses to decide which component to install when
there are multiple classes with the same component name in the classpath. Seam chooses the
component with the higher precedence. Some predefined precedence values are (in ascending order):
1. BUILT _IN — the lowest precedence components are the components built in to Seam.
2. FRAMEWORK — components defined by third-party frameworks may override built-in components,
but are overridden by application components.
3. APPLICAT ION — the default precedence. T his 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 m essageSender that talks to a JMS queue.
@Name("messageSender")
public class MessageSender
{
public void sendMessage()
{
//do something with JMS
}
}
In your unit tests, there is no available JMS queue, so you would like to stub out this method. Create a
mock component that exists in the classpath when unit tests are running, but is never deployed with the
application:
@Name("messageSender")
@Install(precedence=MOCK)
public class MockMessageSender extends MessageSender
{
public void sendMessage()
{
//do nothing!
}
}
T he precedence helps Seam decide which version to use when it finds both components in the
classpath.
If you are able to control precisely which classes are in the classpath, this works well. But if you are
writing a reuseable framework with many dependencies, you do not want to break that framework across
multiple jars. You want to be able to decide which components to install based on other installed
components, and classes available in the classpath. T he @ 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:
90
Chapter 5. The contextual component model
private static final Log log = LogFactory.getLog(CreateOrderAction.class);
public Order createOrder(User user, Product product, int quantity)
{
if ( log.isDebugEnabled() )
{
log.debug("Creating new order for user: " + user.username() + " product: " +
product.name() + " quantity: " + quantity);
}
return new Order(user, product, quantity);
}
Seam provides a logging API that simplifies this code significantly:
@Logger private Log log;
public Order createOrder(User user, Product product, int quantity)
{
log.debug("Creating new order for user: #0 product: #1 quantity: #2",
user.username(), product.name(), quantity);
return new Order(user, product, quantity);
}
Except for entity bean components (which require the log variable to be static), this works regardless of
whether the log variable is declared static.
String concatenation occurs inside the debug() method, so the verbose if (
log.isDebugEnabled() ) guard is unnecessary. Usually, you would not need to explicitly specify
the log category, as Seam knows where it is injecting the log.
If User and Product are Seam components available in the current contexts, the code is even more
concise:
@Logger private Log log;
public Order createOrder(User user, Product product, int quantity)
{
log.debug("Creating new order for user: #{user.username} product:
#{product.name} quantity: #0", quantity);
return new Order(user, product, quantity);
}
Seam logging automatically chooses whether to send output to log4j or JDK logging — if log4j is in the
classpath, Seam uses log4j; if not, Seam uses JDK logging.
SECURITY WARNING: Do not use string concatenation to construct log
messages
Seam logging evaluates expression language (EL) statements in log messages. T his 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. T his could
lead to a variety of security impacts. T o 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. T his can lead
to bugs that manifest only upon failover, which cannot be effectively tested during development. Further,
91
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
the replication messages themselves are inefficient, as they contain the entire serialized object graph,
bound to the session attribute.
EJB stateful session beans must perform automatic dirty checking (that is, they must automatically
detect object state changes to synchronize updated states with the database) and replicate mutable
state. A sophisticated EJB container can introduce optimizations such as attribute-level replication.
Unfortunately, not all Seam users will be working in an environment that supports EJB3. T herefore, Seam
provides an extra layer of cluster-safe state management for session-scoped and conversation-scoped
JavaBean and entity bean components.
For session-scoped or conversation-scoped JavaBean components, Seam automatically forces
replication by calling setAttribute() once in every request where the component was invoked by the
application. However, this strategy is inefficient for read-mostly components. Control this behavior by
implementing the org.jboss.seam .core.Mutable interface, or by extending
org.jboss.seam .core.AbstractMutable and writing your own dirty-checking logic inside the
component. For example,
@Name("account")
public class Account extends AbstractMutable
{
private BigDecimal balance;
public void setBalance(BigDecimal balance)
{
setDirty(this.balance, balance);
this.balance = balance;
}
public BigDecimal getBalance()
{
return balance;
}
...
}
Or, you can use the @ ReadOnly annotation to achieve a similar effect:
@Name("account")
public class Account
{
private BigDecimal balance;
public void setBalance(BigDecimal balance)
{
this.balance = balance;
}
@ReadOnly
public BigDecimal getBalance()
{
return balance;
}
...
}
For session-scoped or conversation-scoped entity bean components, Seam automatically forces
replication by calling setAttribute() once in every request, unless the (conversation-scoped) entity
is currently associated with a Seam-managed persistence context, in which case replication is
unnecessary. T his strategy is not necessarily efficient, so session or conversation scoped entity beans
should be used with care. You can always write a stateful session bean or JavaBean component to
"manage" the entity bean instance. For example:
92
Chapter 5. The contextual component model
@Stateful @Name("account")
public class AccountManager extends AbstractMutable
{
private Account account; // an entity bean
@Unwrap
public Account getAccount()
{
return account;
}
...
}
Note
T he EntityHom e 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 annotation, use them in value-binding and method-binding
expressions, and tie them into the Seam context life cycle (@ Destroy, for example). T herefore, 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.
T he factory component pattern allows a Seam component to act as the instantiator for a non-component
object. A factory method is called when a context variable is referenced but has no value bound to it.
Factory methods are defined with the @ Factory annotation. T he factory method binds a value to the
context variable, and determines the scope of the bound value. T here are two styles of factory method.
T he first style returns a value, which is bound to the context by Seam:
@Factory(scope=CONVERSATION)
public List<Customer> getCustomerList()
{
return ... ;
}
T he 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 custom erList context variable is referenced, and
its value is null. T he factory method then has no further part in the life cycle of the value. T he 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. T his method returns the value that
will be visible to clients, and is called every time a context variable is referenced.
93
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
@Name("customerList")
@Scope(CONVERSATION)
public class CustomerListManager
{
...
@Unwrap
public List<Customer> getCustomerList()
{
return ... ;
}
}
T he 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. T he
component manages these actions itself, and because the object is unwrapped each time it is accessed,
a consistent view is provided.
94
Chapter 6. Configuring Seam components
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. T o isolate deployment-specific information from the
Java code, to enable the creation of reusable frameworks, to configure Seam's built-in functionality, and
so on. Seam provides two basic approaches to component configuration; property settings in a
properties file or web.xm l file, and property settings com ponents.xm l file.
6.1. Configuring components through property settings
You can provide Seam with configuration properties with either servlet context parameters (in system
properties), or with a properties file named seam .properties in the root of the classpath.
T he configurable Seam component must expose JavaBean-style property setter methods for the
configurable attributes. T hat is, if a Seam component named com .jboss.m yapp.settings has a
setter method named setLocale(), you can provide either:
a property named com .jboss.m yapp.settings.locale in the seam .properties file
a system property named
org.jboss.seam .properties.com .jboss.m yapp.settings.locale through -D at startup
the same system property as a Servlet context parameter
Any of these can set the value of the locale attribute in the root of the class path.
T he same mechanism is used to configure Seam itself. For example, to set conversation timeout, provide
a value for org.jboss.seam .core.m anager.conversationT im eout in web.xm l,
seam .properties, or through a system property prefixed with org.jboss.seam .properties.
(T here is a built-in Seam component named org.jboss.seam .core.m anager with a setter method
named setConversationT im eout().)
6.2. Configuring components via components.xml
T he com ponents.xm l file is more powerful than property settings. It allows you to:
configure components that have been installed automatically, including built-in components, and
application components that have been annotated with @ Nam e and picked up by Seam's deployment
scanner.
install classes with no @ Nam e annotation as Seam components. T his is most useful for
infrastructural components which can be installed multiple times with different names (for example,
Seam-managed persistence contexts).
install components that have a @ Nam e annotation but are not installed by default because of an
@ Install annotation that indicates that the component should not be installed.
override the scope of a component.
T he com ponents.xm l file appears in one of the following three locations:
T he WEB-INF directory of a WAR.
T he MET A-INF directory of a JAR.
Any JAR directory containing classes with a @ Nam e annotation.
Seam components are installed when the deployment scanner discovers a class with a @ Nam e
annotation in an archive with a seam .properties file, or a MET A-INF/com ponents.xm l file, unless
the component also has an @ Install annotation indicating that it should not be installed by default.
T he com ponents.xm l file handles special cases where the annotations must be overridden.
T he following example installs and configures two different Seam-managed persistence contexts:
95
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<components xmlns="http://jboss.org/schema/seam/components"
xmlns:persistence="http://jboss.org/schema/seam/persistence">
<persistence:managed-persistence-context name="customerDatabase" persistenceunit-jndi-name="java:/customerEntityManagerFactory"/>
<persistence:managed-persistence-context name="accountingDatabase" persistenceunit-jndi-name="java:/accountingEntityManagerFactory"/>
</components>
T he following example also installs and configures two different Seam-managed persistence contexts:
<components>
<component name="customerDatabase"
class="org.jboss.seam.persistence.ManagedPersistenceContext">
<property name="persistenceUnitJndiName">
java:/customerEntityManagerFactory
</property>
</component>
<component name="accountingDatabase"
class="org.jboss.seam.persistence.ManagedPersistenceContext">
<property name="persistenceUnitJndiName">
java:/accountingEntityManagerFactory
</property>
</component>
</components>
T he following examples creates a session-scoped Seam-managed persistence context. (T his 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>
T he 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" autocreate="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>
T he <factory>
declaration specifies a value-binding or method-binding expression that initializes the value of a context
96
Chapter 6. Configuring Seam components
variable when the variable is first referenced.
<components>
<factory name="contact" method="#{contactManager.loadContact}"
scope="CONVERSATION"/>
</components>
You can create an alias (a second name) for a Seam component as follows:
<components>
<factory name="user" value="#{actor}" scope="STATELESS"/>
</components>
You can create an alias for a commonly used expression as follows:
<components>
<factory name="contact" value="#{contactManager.contact}" scope="STATELESS"/>
</components>
auto-create="true" is often used with the <factory>
declaration as follows:
<components>
<factory name="session" value="#{entityManager.delegate}" scope="STATELESS" autocreate="true"/>
</components>
T he com ponents.xm l file is sometimes used (with minor changes) during both deployment and
testing. Seam allows wildcards of the form @ [email protected] to be placed in the com ponents.xm l file.
T his can be replaced at deployment time by your Ant build script, or by providing a file named
com ponents.properties in the classpath. (T he latter approach appears in the Seam examples.)
6.3. Fine-grained configuration files
If a large number of components require XML configuration, it is better to split the com ponents.xm l file
into several smaller files. With Seam, configuration for a class named com .helloworld.Hello can
be placed in a resource named com /helloworld/Hello.com ponent.xm l. (T his pattern is also
used in Hibernate.) T he root element of the file may either be a <com ponents>
or <com ponent>
element.
T he <com ponents>
element allows you to define multiple components in the file:
<components>
<component class="com.helloworld.Hello" name="hello">
<property name="name">#{user.name}</property>
</component>
<factory name="message" value="#{hello.message}"/>
</components>
T he <com ponent>
element allows you to configure only one component, but is less verbose:
<component name="hello">
<property name="name">#{user.name}</property>
</component>
T he class name in the latter is implied by the file in which the component definition appears.
97
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Alternatively, you can put configuration for all classes in the com .helloworld package in
com /helloworld/com ponents.xm l file.
6.4. Configurable property types
Properties of string, primitive, or primitive wrapper type are configured as follows:
org.jboss.seam.core.manager.conversationT imeout 60000
<core:manager conversation-timeout="60000"/>
<component name="org.jboss.seam.core.manager">
<property name="conversationTimeout">60000</property>
</component>
Even maps with String-valued keys and string, or primitive values are supported:
<component name="issueEditor">
<property name="issueStatuses">
<key>open</key> <value>open issue</value>
<key>resolved</key> <value>issue resolved by developer</value>
<key>closed</key> <value>resolution accepted by user</value>
</property>
</component>
When configuring multi-valued properties, Seam preserves the order of attributes set out in the
com ponents.xm l file by default. If SortedSet/SortedMap are used, Seam refers to
T reeMap/T reeSet. If the property has a concrete type (LinkedList, for example) Seam uses that
type.
You can also override the type by specifying a fully qualified class name:
<component name="issueEditor">
<property name="issueStatusOptions" type="java.util.LinkedHashMap">
<key>open</key> <value>open issue</value>
<key>resolved</key> <value>issue resolved by developer</value>
<key>closed</key> <value>resolution accepted by user</value>
</property>
</component>
You can link components with a value-binding expression. Note that as this occurs upon component
instantiation, not invocation, this is quite different to injection with @ In. It is more similar to the
dependency injection facilities offered by traditional Inversion of Control containers such as JavaServer
Faces (JSF) or Spring.
<drools:managed-working-memory name="policyPricingWorkingMemory" rulebase="#{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:
98
Chapter 6. Configuring Seam 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. T he message templates on
the Hom e 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 argum ents}). T o 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. T he following example shows a typical com ponents.xm l 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">@[email protected]</property>
</component>
</components>
As you can see, this code is verbose. More importantly, the component and attribute names cannot be
validated at development time.
Using namespaces gives you:
<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://jboss.org/schema/seam/components"
xmlns:core="http://jboss.org/schema/seam/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://jboss.org/schema/seam/core
http://jboss.org/schema/seam/core-2.2.xsd
http://jboss.org/schema/seam/components
http://jboss.org/schema/seam/components-2.3.xsd">
<core:init debug="true" jndi-pattern="@[email protected]"/>
</components>
Although the schema declarations are verbose, the XML content itself is lean and easy to understand.
T he 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 com ponents.xm l files.
T his works well for built-in Seam components, but for user components there are two available options.
99
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
First, Seam supports mixing both models, allowing the use of generic <com ponent>
declarations for user components, and namespaced declarations for built-in components. More
importantly, Seam allows you to quickly declare namespaces for your own components.
Any Java package can be associated with an XML namespace by annotating the package with
@ Nam espace. (Package-level annotations are declared in a file named package-info.java in the
package directory.) An example of this from the seampay demo is:
@Namespace(value="http://jboss.org/schema/seam/examples/ seampay") package
org.jboss.seam.example.seampay; import org.jboss.seam.annotations.Namespace;
You can write:
<components xmlns="http://jboss.org/schema/seam/components"
xmlns:pay="http://jboss.org/schema/seam/examples/seampay" ... >
<pay:payment-home new-instance="#{newPayment}" created-message="Created a new
payment to #{newPayment.payee}" />
<pay:payment name="newPayment"
payee="Somebody"
account="#{selectedAccount}"
payment-date="#{currentDatetime}"
created-date="#{currentDatetime}" />
...
</components>
Or:
<components xmlns="http://jboss.org/schema/seam/components"
xmlns:pay="http://jboss.org/schema/seam/examples/seampay"
... >
<pay:payment-home>
<pay:new-instance>"#{newPayment}"</pay:new-instance>
<pay:created-message>
Created a new payment to #{newPayment.payee}
</pay:created-message>
</pay:payment-home>
<pay:payment name="newPayment">
<pay:payee>Somebody"</pay:payee>
<pay:account>#{selectedAccount}</pay:account>
<pay:payment-date>#{currentDatetime}</pay:payment-date>
<pay:created-date>#{currentDatetime}</pay:created-date>
</pay:payment>
...
</components>
T he previous examples illustrate the two usage models of a namespaced element. In the first
declaration, <pay:paym ent-hom e>
references the paym entHom e component:
package org.jboss.seam.example.seampay;
...
@Name("paymentHome")
public class PaymentController extends EntityHome<Payment> {
...
}
T he element name is the hyphenated form of the component name. T he attributes of the element are the
hyphenated forms of the property names.
In the second declaration, the <pay:paym ent>
element refers to the Paym ent class in the org.jboss.seam .exam ple.seam pay package. In this
case, Paym ent is an entity that is being declared as a Seam component:
100
Chapter 6. Configuring Seam components
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.
T he following are the namespaces used by Seam:
components — http://jboss.org/schem a/seam /com ponents
core — http://jboss.org/schem a/seam /core
drools — http://jboss.org/schem a/seam /drools
framework — http://jboss.org/schem a/seam /fram ework
jms — http://jboss.org/schem a/seam /jm s
remoting — http://jboss.org/schem a/seam /rem oting
theme — http://jboss.org/schem a/seam /them e
security — http://jboss.org/schem a/seam /security
mail — http://jboss.org/schem a/seam /m ail
web — http://jboss.org/schem a/seam /web
pdf — http://jboss.org/schem a/seam /pdf
spring — http://jboss.org/schem a/seam /spring
101
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Chapter 7. Events, interceptors and exception handling
T o complement the contextual component model, there are two further basic concepts that facilitate the
extremely loose coupling distinctive of Seam applications. T he first is a strong event model, where
events are mapped to event listeners with method-binding expressions like those in JavaServer Faces
(JSF). T he second is the pervasive use of annotations and interceptors to apply cross-cutting concerns
to components that implement business logic.
7.1. Seam events
T he 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. T here
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.xm l. You can define a page action for a particular JSF view ID:
<pages>
<page view-id="/hello.xhtml" action="#{helloWorld.sayHello}"/>
</pages>
Or you can use a * wildcard as a suffix to the view-id to specify an action that applies to all view IDs
that match that pattern:
<pages>
<page view-id="/hello/*" action="#{helloWorld.sayHello}"/>
</pages>
Note
If the <page>
element is defined in a fine-grained page descriptor, the view-id attribute can be omitted, as it is
already implied.
If multiple page actions, with wildcards, match the current view-id; Seam calls all the actions, in the order
of least-specific to most-specific.
T he 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.
T he view ID mentioned in the <page>
element need not correspond to a real JSP or Facelets page. T his way, you can reproduce the
functionality of a traditional action-oriented framework like Struts or WebWork using page actions. T his is
useful for performing complex actions in response to non-Faces requests like HT T P GET .
102
Chapter 7. Events, interceptors and exception handling
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>
T his condition consults the ResponseStateManager#isPostback(FacesContext) to determine if
the request is a postback. T he 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>
T he on-postback attribute defaults to true to maintain backward compatibility.
7.3. Page parameters
A Faces request (a JSF form submission) encapsulates both, action (a method binding) and parameters
(input value bindings). A page action can also require parameters. As non-Faces (GET ) requests can be
bookmarked, page parameters are passed as human-readable request parameters. You can use page
parameters with or without an action method.
7.3.1. Mapping request parameters to the model
Seam allows you to provide a value binding that maps a named request parameter to an attribute of a
model object.
<pages>
<page view-id="/hello.xhtml" action="#{helloWorld.sayHello}">
<param name="firstName" value="#{person.firstName}"/>
<param name="lastName" value="#{person.lastName}"/>
</page>
</pages>
T he <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
103
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
parameter to the model object, after performing appropriate type conversions.
Any <s:link>
or <s:button>
includes the request parameter transparently. T he 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. T he parameter value is determined by
evaluating the value binding at the end of the invoke application phase.
T he value is transparently propagated with any JSF form submission for the page with the given view
ID. T his means that view parameters behave like PAGE-scoped context variables for Faces requests.
However we arrive at /hello.xhtm l, 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 nam e 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.
T he value is transparently propagated with any JSF form submission for the page with the given view
ID. T his means that view parameters behave like PAGE-scoped context variables for Faces requests.
Although this is fairly complex, it is definitely worthwhile to dedicate time to an understanding of page
parameters. Page parameters are the most elegant method of propagating state across non-Faces
requests. Page parameters are particularly useful in certain situations. For example, if we have search
screens with bookmarkable results pages, page parameters allow you to write handling for both POST
and GET requests in the same code. Page parameters eliminate repetitive request parameter-listing in
the view definition, and simplify redirect code.
7.5. URL rewriting with page parameters
Rewriting occurs based on patterns found for views in pages.xm l. 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 /hom e will be sent to /hom e.xhtm l. Any link generated that
would normally point to /hom e.seam will instead be rewritten as /hom e. Rewrite patterns only match
the portion of the URL before the query parameters, so /hom e.seam ?conversationId=13 and
/hom e.seam ?color=red will both be matched by this rewrite rule.
104
Chapter 7. Events, interceptors and exception handling
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 /hom e/red will be served as if it was a request for
/hom e.seam ?color=red. Similarly, if color is a page parameter, an outgoing URL that would normally
show as /hom e.seam ?color=blue would instead be output as /hom e/blue. Rules are processed
in order, so it is important to list specific rules before general rules.
Default Seam query parameters can also be mapped with URL rewriting, further concealing Seam's
fingerprints. In the following example, /search.seam ?conversationId=13 would be written as
/search-13.
<page view-id="/search.xhtml">
<rewrite pattern="/search-{conversationId}" />
<rewrite pattern="/search" />
</page>
Seam URL rewriting provides simple, bidirectional rewriting on a per-view basis. For more complex
rewriting rules that cover non-Seam components, Seam applications can continue to use the
org.tuckey.URLRewriteFilter, or apply rewriting rules at the web server.
T o 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>
105
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<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.xm l 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.xm l and facesconfig.xm l. It is better to unify this logic under pages.xm l.
T his 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 Docum entEditor with string-valued return values (the JSF outcomes).
Instead, Seam allows you to write:
<page view-id="/editDocument.xhtml">
<navigation from-action="#{documentEditor.update}"
evaluate="#{documentEditor.errors.size}">
<rule if-outcome="0">
<redirect view-id="/viewDocument.xhtml"/>
</rule>
</navigation>
</page>
Or even:
106
Chapter 7. Events, interceptors and exception handling
<page view-id="/editDocument.xhtml">
<navigation from-action="#{documentEditor.update}">
<rule if="#{documentEditor.errors.empty}">
<redirect view-id="/viewDocument.xhtml"/>
</rule>
</navigation>
</page>
T he first form evaluates a value binding to determine the outcome value used by the subsequent rules.
T he second approach ignores the outcome and evaluates a value binding for each possible rule.
When an update succeeds, you probably want to end the current conversation, as:
<page view-id="/editDocument.xhtml">
<navigation from-action="#{documentEditor.update}">
<rule if="#{documentEditor.errors.empty}">
<end-conversation/>
<redirect view-id="/viewDocument.xhtml"/>
</rule>
</navigation>
</page>
As the conversation has ended, subsequent requests will not know which document you are interested
in. You can pass the document ID as a request parameter, which also makes the view bookmarkable:
<page view-id="/editDocument.xhtml">
<navigation from-action="#{documentEditor.update}">
<rule if="#{documentEditor.errors.empty}">
<end-conversation/>
<redirect view-id="/viewDocument.xhtml">
<param name="documentId" value="#{documentEditor.documentId}"/>
</redirect>
</rule>
</navigation>
</page>
Null outcomes are a special case in JSF, and are interpreted as instructions to redisplay the page. T he
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>
T o 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>
T he view ID can be given as a JSF EL expression:
107
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<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 suggested that you split the declarations into several smaller files. You can
define actions and parameters for a page with the view ID /calc/calculator.xhtm l in a resource
named calc/calculator.page.xm l. 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.
Specify event listeners (observers) in com ponents.xm l.
<components>
<event type="hello">
<action execute="#{helloListener.sayHelloBack}"/>
<action execute="#{logger.logHello}"/>
</event>
</components>
Here, the event type is an arbitrary string.
When an event occurs, the actions registered for that event are called in the order they appear in
com ponents.xm l. 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:
108
Chapter 7. Events, interceptors and exception handling
@Name("helloWorld")
public class HelloWorld
{
@RaiseEvent("hello")
public void sayHello()
{
FacesMessages.instance().add("Hello World!");
}
}
T his event producer is not dependent upon event consumers. T he 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!");
}
}
T he method binding defined above in com ponents.xm l maps the event to the consumer. You can also
do this with annotations:
@Name("helloListener")
public class HelloListener {
@Observer("hello")
public void sayHelloBack() {
FacesMessages.instance().add("Hello to you too!");
}
}
In Seam, event objects do not need to propagate state between the event producer and listener. State is
held in the Seam contexts, and shared between components. However, you can pass an event object as:
@Name("helloWorld")
public class HelloWorld {
private String name;
public void sayHello() {
FacesMessages.instance().add("Hello World, my name is #0.", name);
Events.instance().raiseEvent("hello", name);
}
}
@Name("helloListener")
public class HelloListener {
@Observer("hello")
public void sayHelloBack(String name) {
FacesMessages.instance().add("Hello #0!", name);
}
}
7.10. Contextual events
Seam defines a number of built-in events that the application uses for framework integration. T he events
are:
109
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
T able 7.1. Contextual Events
Event
Description
org.jboss.seam .validationFailed
Called when JSFvalidation fails.
org.jboss.seam .noConversation
Called when there is no long-running
conversation and a long-running
conversation is required.
org.jboss.seam .preSetVariable.<nam e>
Called when the context variable
<name> is set.
org.jboss.seam .postSetVariable.<nam e>
Called when the context variable
<name> is set.
org.jboss.seam .preRem oveVariable.<nam e>
Called when the context variable
<name> is unset.
org.jboss.seam .postRem oveVariable.<nam e>
Called when the context variable
<name> is unset.
org.jboss.seam .preDestroyContext.<SCOPE>
Called before the <SCOPE> context
is destroyed.
org.jboss.seam .postDestroyContext.<SCOPE>
Called after the <SCOPE> context is
destroyed.
org.jboss.seam .beginConversation
Called whenever a long-running
conversation begins.
org.jboss.seam .endConversation
Called whenever a long-running
conversation ends.
org.jboss.seam .conversationT im eout
Called when a conversation timeout
occurs. T he conversation ID is
passed as a parameter.
org.jboss.seam .postCreate.<nam e>
Called when the component <name>
is created.
org.jboss.seam .preDestroy.<nam e>
Called when the component <name>
is destroyed.
org.jboss.seam .beforePhase
Called before the start of a JSF
phase.
org.jboss.seam .afterPhase
Called after the end of a JSF phase.
org.jboss.seam .postInitialization
Called when Seam has initialized and
started all components.
org.jboss.seam .postReInitialization
Called when Seam has re-initialized
and started all components after a
redeploy.
org.jboss.seam .exceptionHandled.<type>
Called when an uncaught exception is
handled by Seam.
org.jboss.seam .exceptionHandled
Called when an uncaught exception is
handled by Seam.
org.jboss.seam .exceptionNotHandled
Called when there is no handler for
an uncaught exception.
org.jboss.seam .afterT ransactionSuccess
Called when a transaction succeeds
in the Seam Application Framework.
org.jboss.seam .afterT ransactionSuccess.<nam
e>
Called when a transaction succeeds
in the Seam Application Framework
managing the entity <nam e>
.
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
110
Chapter 7. Events, interceptors and exception handling
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. T o add an interceptor to a
bean, you need to write a class with a method annotated @ AroundInvoke and annotate the bean with
an @ Interceptors annotation that specifies the name of the interceptor class. For example, the
following interceptor checks that the user is logged in before invoking an action listener method:
public class LoggedInInterceptor
{
@AroundInvoke
public Object checkLoggedIn(InvocationContext invocation)
throws Exception
{
boolean isLoggedIn = Contexts.getSessionContext()
.get("loggedIn")!=null;
if (isLoggedIn)
{
//the user is already logged in return invocation.proceed();
}
else
{
//the user is not logged in, fwd to login page return "login";
}
}
}
T o apply this interceptor to a session bean acting as an action listener, you must annotate the session
bean @ Interceptors(LoggedInInterceptor.class). However, Seam builds upon the interceptor
framework in EJB3 by allowing you to use @ Interceptors as a meta-annotation for class level
interceptors (those annotated @ T arget(T YPE)). In this example, you create a @ LoggedIn annotation,
as follows:
@Target(TYPE)
@Retention(RUNTIME)
@Interceptors(LoggedInInterceptor.class)
public @interface LoggedIn {}
You can annotate the action listener bean with @ LoggedIn to apply the interceptor:
@Stateless
@Name("changePasswordAction")
@LoggedIn
@Interceptors(SeamInterceptor.class)
public class ChangePasswordAction implements ChangePassword {
...
public String changePassword()
{
...
}
}
111
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Where interceptor order is important, add @ Interceptor annotations to your interceptor classes to
specify an order of interceptors.
@Interceptor(around={BijectionInterceptor.class,
ValidationInterceptor.class,
ConversationInterceptor.class},
within=RemoveInterceptor.class)
public class LoggedInInterceptor
{
...
}
You can have a client-side interceptor, for built-in EJB3 functions:
@Interceptor(type=CLIENT)
public class LoggedInInterceptor
{
...
}
EJB interceptors are stateful, and their life cycles match the life cycles of the component they intercept.
For interceptors that do not need to maintain state, Seam allows performance optimization where
@ Interceptor(stateless=true) is specified.
Much of Seam's functionality is implemented as a set of built-in Seam interceptors, including the
interceptors named in the previous example. T hese interceptors exist for all interceptable Seam
components; you need not specify them explicitly through annotations.
Seam interceptors can also be used with JavaBean components. EJB defines interception not only for
business methods (using @ AroundInvoke), but also for the life cycle methods @ PostConstruct,
@ PreDestroy, @ PrePassivate, and @ PostActive. Seam supports these life cycle methods on both
component and interceptor, not only for EJB3 beans, but also for JavaBean components (except
@ PreDestroy, which is not meaningful for JavaBean components).
7.12. Managing exceptions
JSF has a limited ability to handle exceptions. T o work around this problem, Seam alows you to define
treatment of an exception class through annotation, or through declaration in an XML file. T his 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 the exception is generated by a business method of the bean. System
exceptions always cause a transaction rollback. Application exceptions do not cause a rollback by
default, but they cause a rollback if @ ApplicationException(rollback=true) is specified. (An
application exception is any checked exception, or any unchecked exception annotated
@ ApplicationException. A system exception is any unchecked exception without an
@ ApplicationException annotation.)
Note
Marking a transaction for rollback is not the same as actually rolling back the transaction. T he
exception rules say that the transaction should only be marked for rollback, but it may still be
active after the exception is generated.
Seam also applies the EJB3 exception rollback rules to Seam JavaBean components. T hese rules apply
only in the Seam component layer. When an exception occurs outside the Seam component layer, Seam
112
Chapter 7. Events, interceptors and exception handling
rolls back active transactions.
7.12.2. Enabling Seam exception handling
T o enable Seam's exception handling, the master Servlet filter must be declared in web.xm l:
<filter>
<filter-name>Seam Filter</filter-name>
<filter-class>org.jboss.seam.servlet.SeamFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Seam Filter</filter-name>
<url-pattern>*.seam</url-pattern>
</filter-mapping>
For the exception handlers to execute, disable Facelets development mode in web.xm l and Seam
debug mode in com ponents.xm l.
7.12.3. Using annotations for exception handling
T he following exception results in an HT T P 404 error whenever it propagates outside the Seam
component layer. T he exception does not roll back the current transaction immediately, but the
transaction is rolled back if the exception is not caught by another Seam component.
@HttpError(errorCode=404)
public class ApplicationException extends Exception
{
...
}
T his exception results in a browser redirect whenever it propagates outside the Seam component layer.
It also ends the current conversation. It causes an immediate rollback of the current transaction.
@Redirect(viewId="/failure.xhtml")
@ApplicationException(rollback=true)
public class UnrecoverableApplicationException extends RuntimeException
{
...
}
Note
Seam cannot handle exceptions that occur during JSF's RENDER_RESPONSE phase, as it is not
possible to perform a redirect once writing to the response has begun.
You can also use EL to specify the viewId to redirect to. When this exception propagates outside the
Seam component layer, it results in a redirect and a message to the user. It also immediately rolls back
the current transaction.
@Redirect(viewId="/error.xhtml", message="Unexpected error")
public class SystemException extends RuntimeException
{
...
}
7.12.4. Using XML for exception handling
As annotations cannot be added to all exception classes, Seam allows you to specify this functionality in
pages.xm l.
113
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<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>
T he final <exception>
declaration does not specify a class, and acts as catch-all for any exception without specified handling
through annotations or in pages.xm l.
You can also use EL to specify the view-id to redirect to. You can also access the handled exception
instance through EL. Seam places the exception in the conversation context. For example, to access the
exception message:
...
throw new AuthorizationException("You are not allowed to do this!");
<pages>
<exception class="org.jboss.seam.security.AuthorizationException">
<end-conversation/>
<redirect view-id="/error.xhtml">
<message severity="WARN">
#{org.jboss.seam.handledException.message}
</message>
</redirect>
</exception>
</pages>
T he org.jboss.seam .handledException holds the nested exception that was handled by an
exception handler. T he 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.xm l, it is possible to declare the level at which the
exception is logged, or to suppress exception logging altogether. T he log and log-level attributes
are used to control exception logging. No log message is generated if the specified exception occurs
when log="false" is set, as shown here:
<exception class="org.jboss.seam.security.NotLoggedInException"
log="false">
<redirect view-id="/register.xhtml">
<message severity="warn">
You must be a member to use this feature
</message>
</redirect>
</exception>
If the log attribute is not specified, then it defaults to true — that is, the exception will be logged.
Alternatively, you can specify the log-level to control the level at which the exception is logged:
114
Chapter 7. Events, interceptors and exception handling
<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>
T he acceptable values for log-level are: fatal, error, warn, info, debug, and trace. If
the log-level is not specified, or if an invalid value is configured, log-level defaults to error.
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 a user posts to a page after the session has expired. T he
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.
115
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
116
Chapter 8. Conversations and workspace management
Chapter 8. Conversations and workspace management
T his chapter discusses Seam's conversation model in detail.
T he notion of a Seam conversation came about as a combination of three separate concepts:
T he concept of a workspace, and effective workspace management.
T he 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.
T he concept of a workflow task.
By unifying these ideas and providing deep support in the framework, we have created a powerful
construct that allows richer and more efficient applications, using less verbose code.
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 longrunning conversation.
When an @ End method is encountered, any long-running conversation context is demoted to a
temporary conversation.
At the end of the render response phase of the JSF request life cycle, Seam either stores the
contents of a long-running conversation context, or destroys the contents of a temporary
conversation context.
Faces request (a JSF postback) propagates the conversation context. By default, non-Faces
requests (for example, GET requests) do not propagate the conversation context.
If the JSF request life cycle is foreshortened by a redirect, Seam transparently stores and restores
the current conversation context, unless the conversation was already ended through
@ End(beforeRedirect=true).
Seam transparently propagates the conversation context (including the temporary conversation context)
across JSF postbacks and redirects. Without special additions, a non-Faces request (a GET request, for
example) does not propagate the conversation context, and is processed in a new temporary
conversation. T his is usually — but not always — the desired behavior.
T o 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:
117
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<h:outputLink value="main.jsf">
<s:conversationId/>
<h:outputText value="Continue"/>
</h:outputLink>
T he 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>
T he 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.
T he 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>
T his conversation model makes it easy to build applications which behave correctly with respect to multiwindow 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. T he smaller nested conversations have their own isolated set of conversation state,
and have access to the state of the outer conversation.
T he user can switch between many conversations within the same browser window. T his 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. T he outer conversation's context is read-only within a
nested conversation, but because objects are obtained by reference, changes to the objects themselves
are reflected in the outer context.
118
Chapter 8. Conversations and workspace management
Nesting a conversation initializes a context that is stacked on the context of the original, or outer,
conversation. T he outer conversation is considered the parent.
Values outjected or set directly into the nested conversation’s context do not affect the objects
accessible in the parent conversation’s context.
Injection, or a context lookup from the conversation context, first looks for the value in the current
conversation context. If no value is found, lookup continues down the conversation stack, if the
conversation is nested. T his behavior can be overridden.
When an @ End is subsequently encountered, the nested conversation is destroyed, and the outer
conversation resumes, popping the conversation stack. Conversations can be nested to any arbitrary
depth.
Certain user activities (workspace management, or the back button) can cause the outer conversation to
be resumed before the inner conversation ends. In this case, it is possible to have multiple concurrent
nested conversations belonging to the same outer conversation. If the outer conversation ends before a
nested conversation ends, Seam destroys all nested conversation contexts along with the outer context.
T he conversation at the bottom of the conversation stack is the root conversation. Destroying this
conversation always destroys all descendant conversations. You can achieve this declaratively by
specifying @ End(root=true).
A conversation can be thought of as a continuable state. Nested conversations allow the application to
capture a consistent continuable state at various points in a user interaction. T his ensures correct
behavior in the case of backbuttoning and workspace management.
As mentioned previously, if a component exists in a parent conversation of the current nested
conversation, the nested conversation uses the same instance. Occasionally, it is useful to have a
different instance in each nested conversation, so that the component instance of the parent
conversation is invisible to its child conversations. You can achieve this behavior by annotating the
component @ PerNestedConversation.
8.3. Starting conversations with GET requests
JSF does not define action listeners triggered when a page is accessed through a non-Faces request
(for example, an HT T P GET request). T his can occur when a user bookmarks the page, or navigates to
the page through <h:outputLink>
.
Sometimes you want a conversation to begin immediately when a page is accessed. As there is no JSF
action method, you cannot annotate the action with @ Begin.
Further problems arise when the page requires state to be fetched into a context variable. You have
seen two methods of solving this problem. If the state is held in a Seam component, you can fetch the
state in a @ Create method. If not, you can define a @ Factory method for the context variable.
If neither option works for you, Seam allows you to define a page action in the pages.xm l file.
<pages>
<page view-id="/messageList.xhtml" action="#{messageManager.list}"/>
...
</pages>
T his action method is called at the beginning of the render response phase — that is, any time the page
is about to be rendered. If a page action returns a non-null outcome, Seam processes appropriate JSF
and Seam navigation rules. T his 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:
119
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<pages>
<page view-id="/messageList.xhtml" action="#{conversation.begin}"/>
...
</pages>
You can also call this built-in action from a JSF control, and #{conversation.end} similarly ends
conversations.
You can use the <begin-conversation>
element for further control over joining existing conversations, or beginning a nested conversation or an
atomic conversation; as follows:
<pages>
<page view-id="/messageList.xhtml">
<begin-conversation nested="true" />
<page>
...
</pages>
T here is also a <end-conversation>
element.
<pages>
<page view-id="/home.xhtml">
<end-conversation/>
<page>
...
</pages>
You have five options to begin a conversation immediately when a page is accessed:
Annotate the @ Create method with @ Begin
Annotate the @ Factory method with @ Begin
Annotate the Seam page action method with @ Begin
Use <begin-conversation>
in pages.xm l
Use #{conversation.begin} as the Seam page action method
8.4. Requiring a long-running conversation
Some pages are relevant only in the context of a long-running conversation. One way to restrict access
to such a page is to make the existence of a long-running conversation a prerequisite to the page being
rendered.
Seam's page descriptor has a conversation-required attribute, which allows you to indicate that
the current conversation must be long-running (or nested) for a page to be rendered, as:
<page view-id="/book.xhtml" conversation-required="true"/>
Note
At present, you cannot indicate which long-running conversation is required. However, you can
build on the basic authorization by checking whether a specific value is also present in the
conversation within a page action.
When Seam determines that the page has been requested when no long-running conversation is
present, it performs the following actions:
120
Chapter 8. Conversations and workspace management
Raises a contextual event called org.jboss.seam .noConversation
Registers a warning status message with the bundle key org.jboss.seam .NoConversation
Redirects the user to an alternative page, if defined in the no-conversation-view-id attribute,
as:
<pages no-conversation-view-id="/main.xhtml"/>
T his 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. T o get this functionality in plain JSF
use <h:outputLink>
, but there are two major limitations to this method:
JSF provides no way to attach an action listener to <h:outputLink>
JSF does not propagate the selected row of a DataModel, as there is no actual form submission.
T o solve the first problem, Seam implements the notion of a page action. You can work around the
second problem by passing a request parameter and requerying for the selected object on the serverside; in some cases like the Seam blog example application, this is the best approach. As it is REST ful
and does not require a server-side state, bookmarking is supported. In other cases, where bookmarking
is not required, @ DataModel and @ DataModelSelection are transparent and convenient.
T o replace this missing functionality, and to simplify conversation propagation further, Seam provides the
<s:link>
JSF tag.
T he 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"/>
T he link automatically propagates the selected row of a DataModel inside <h:dataT able>
:
<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"/>
121
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Finally, use <s:button>
to '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.
T he built-in conversation-scoped Seam component named facesMessages solves this problem. (T his
requires the Seam redirect filter.)
@Name("editDocumentAction")
@Stateless
public class EditDocumentBean implements EditDocument
{
@In EntityManager em;
@In Document document;
@In FacesMessages facesMessages;
public String update()
{
em.merge(document);
facesMessages.add("Document updated");
}
}
When a message is added to facesMessages, it is used in the next render response phase for the
current conversation. Since Seam preserves temporary conversation contexts across redirects, this
works even without a long-running conversation.
You can include JSF EL expressions in a Faces message summary:
facesMessages.add("Document #{document.title} was updated");
Messages are displayed as:
<h:messages globalOnly="true"/>
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.
Consider the following example:
You are on Ebay, halfway through paying for an item you won as a Christmas present for your parents.
You want to send it straight to them, but once you have entered your payment details, you cannot
remember your parents' address. While you find the address, you accidentally reuse the same browser
window, but now you need to return to complete the payment for the item.
With a natural conversation, you can easily rejoin the previous conversation and pick up where you left.
In this case, you can rejoin the payForItem conversation with the itemId as the conversation ID.
User-friendly URLs
122
Chapter 8. Conversations and workspace management
A user-friendly URL is meaningful (refers to page contents plainly, without using ID numbers), and has a
navigable hierarchy (that is, you can navigate by editing the URL).
With a natural conversation, applications can generate long, complex URLs, but display simple,
memorable URLs by using URLRewrite. In the case of the hotel booking example, http://seam hotels/book.seam ?hotel=BestWesternAntwerpen is rewritten as http://seam hotels/book/BestWesternAntwerpen — much clearer. Note that URLRewrite relies on parameters;
for example hotel in the previous example must map to a unique parameter on the domain model.
8.8. Creating a natural conversation
Natural conversations are defined in pages.xm l file:
<conversation name="PlaceBid" parameter-name="auctionId"
parameter-value="#{auction.auctionId}"/>
In the above definition the conversation name is PlaceBid. T he conversation name identifies a named
conversation uniquely, and is used by the page definition to identify the named conversation in which to
participate.
T he param eter-nam e attribute defines the request parameter that holds the natural conversation ID,
and replaces the default conversation ID parameter. In this case, param eter-nam e is auctionId.
T his means that the URL of your page will contain auctionId=7654 32 instead of a conversation
parameter like cid=123.
T he final attribute, param eter-value, defines an EL expression to evaluate the value of the natural
business key to be used as the conversation ID. In this example, the conversation ID will be the primary
key value of the auction instance currently in scope.
Next, we define the pages participating in the named conversation. T his 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 ifoutcome="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. You can start with the following page definition:
<page view-id="/auction.xhtml">
<param name="id" value="#{auctionDetail.selectedAuctionId}"/>
<navigation from-action="#{bidAction.placeBid}">
<redirect view-id="/bid.xhtml"/>
</navigation>
</page>
Here that invoking #{bidAction.placeBid} redirects you to /bid.xhtm l, which is configured with
the natural conversation ID PlaceBid. Your action method declaration looks like this:
@Begin(join = true)
public void placeBid()
123
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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. T his can cause problems when redirecting to an existing conversation,
as redirection needs to occur before the action method is invoked. T herefore, the conversation name
must be specified before the action is invoked. One method of doing this uses the
<s:conversationNam e>
tag:
<h:commandButton id="placeBidWithAmount" styleClass="placeBid"
action="#{bidAction.placeBid}">
<s:conversationName value="PlaceBid"/>
</h:commandButton>
You can also specify the conversationNam e 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. T o enable workspace management:
Provide description text for each view ID (when using JSF or Seam navigation rules) or page node.
Workspace switchers display this description text.
Include one or more workspace switcher JSF or Facelets fragments in your page. Standard
fragments support workspace management via a drop-down menu and a list of conversations, or
"breadcrumbs".
8.10.1. Workspace management and JSF navigation
Seam uses JSF or Seam navigation rules to switch to a conversation by restoring the current view-id
for that conversation. T he descriptive text for the workspace is defined in the pages.xm l file, which
Seam expects to find in the WEB-INF directory alongside faces-config.xm l file:
<pages>
<page view-id="/main.xhtml">
<description>Search hotels: #{hotelBooking.searchString}</description>
</page>
<page view-id="/hotel.xhtml">
<description>View hotel: #{hotel.name}</description>
</page>
<page view-id="/book.xhtml">
<description>Book hotel: #{hotel.name}</description>
</page>
<page view-id="/confirm.xhtml">
<description>Confirm: #{booking.description}</description>
</page>
</pages>
Note
T he Seam application works even if the pages.xm l file is not present. However, workplace
switching is not available in this case.
8.10.2. The conversation switcher
Include the following fragment in your JSF or Facelets page to include a drop-down menu that allows you
to switch to any current conversation, or any other page of the application:
124
Chapter 8. Conversations and workspace management
<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"/>
T his example includes a menu that contains an item for each conversation, and two additional items that
allow you to begin an additional conversation.
Only conversations with a description (specified in pages.xm l file) are included in the drop-down menu.
8.10.3. The conversation list
T he conversation list is similar to the conversation switcher, except that it is displayed as a table:
<h:dataTable value="#{conversationList}" var="entry"
rendered="#{not empty conversationList}">
<h:column>
<f:facet name="header">Workspace</f:facet>
<h:commandLink action="#{entry.select}" value="#{entry.description}"/>
<h:outputText value="[current]" rendered="#{entry.current}"/>
</h:column>
<h:column>
<f:facet name="header">Activity</f:facet>
<h:outputText value="#{entry.startDatetime}">
<f:convertDateTime type="time" pattern="hh:mm a"/>
</h:outputText>
<h:outputText value=" - "/>
<h:outputText value="#{entry.lastDatetime}">
<f:convertDateTime type="time" pattern="hh:mm a"/>
</h:outputText>
</h:column>
<h:column>
<f:facet name="header">Action</f:facet>
<h:commandButton action="#{entry.select}" value="#{msg.Switch}"/>
<h:commandButton action="#{entry.destroy}" value="#{msg.Destroy}"/>
</h:column>
</h:dataTable>
You can customize the conversation list for your applications.
Only conversations with a description are included in the list.
Note that the conversation list also allows you to destroy workspaces.
125
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
8.10.4. Breadcrumbs
Breadcrumbs are a list of links to conversations in the current conversation stack. T hey 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, as it
creates a hard dependency from application logic to the view.) On a postback request, component
bindings are updated during the Restore View phase, before the Seam conversation context has been
restored.
You can work around this by storing component bindings with an event-scoped component, and injecting
this into the requiring conversation-scoped component.
@Name("grid")
@Scope(ScopeType.EVENT)
public class Grid {
private HtmlPanelGrid htmlPanelGrid; // getters and setters
...
}
@Name("gridEditor")
@Scope(ScopeType.CONVERSATION)
public class GridEditor {
@In(required=false)
private Grid grid;
...
}
Also, a conversation-scoped component cannot be injected into an event-scoped component with a JSF
control bound to it. T his includes Seam built-in components like facesMessages.
You can also access the JSF component tree with the implicit uiCom ponent handle. T he 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. T his section discusses the most common situation in which you will encounter concurrency
— when accessing conversational components from AJAX requests. T his section also describes the
126
Chapter 8. Conversations and workspace management
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. T his 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.
T herefore, when you work inside a long-running conversation, Seam queues the action even for a period
of time (the concurrent request timeout). If Seam cannot process the event before timeout, it creates a
temporary conversation and prints a message for the user, informing them of the timeout. It is therefore
important not to flood the server with AJAX events.
You can set a sensible default for the concurrent request timeout (in milliseconds) in the
com ponents.xm l file:
<core:manager concurrent-request-timeout="500" />
T he 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" />
T he above section discussed AJAX requests that appear serially. T he client tells the server that an
event has occurred, and then rerenders part of the page based on the result. T his 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. T he client then polls the server for updates
while the actions are executed. T his approach is useful when it is important that no action in a longrunning action sequence times out.
8.12.1. How should we design our conversational AJAX application?
T he first question is whether to use the simpler "serial" request method, or a polling approach.
If you want to use serial requests, you must estimate the time required for your request to complete. You
may need to alter the concurrent request timeout for this page, as discussed in the previous section. A
queue on the server side is probably necessary, to prevent requests from flooding the server. If the
event occurs often (for example, a keystroke, or onblur of input fields) and immediate client update is not
a priority, set a request delay on the client side. Remember to factor the possibility of server-side
queuing into your request delay.
Finally, the client library may provide an option to abort unfinished duplicate requests in favor of the most
recent requests.
A polling approach requires less fine-tuning — simply mark your action method @ Asynchronous and
decide on a polling interval:
127
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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 may become overloaded. T he overloaded server will be unable
to process all the requests before the request has to wait longer than the concurrent-requesttim eout. In this case, Seam generates ConcurrentRequestT im eoutException that can be
handled in the pages.xm l file. We recommend sending an HT T P 503 error:
<exception class="org.jboss.seam.ConcurrentRequestTimeoutException" loglevel="trace">
<http-error error-code="503" />
</exception>
503 Service Unavailable (HTTP/1.1 RFC)
T he server is currently unable to handle the request due to a temporary overloading or
maintenance of the server. T he 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" loglevel="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 HT T P error codes. Seam Remoting will pop-up a dialog box
showing the HT T P error. JSF 2 provides support for handling HT T P 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>
128
Chapter 8. Conversations and workspace management
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 the controls
discussed in the previous section.
attachQueue
Customizes queuing for specific components and behaviors. It can override queue settings of a
component or attach specific requests to a queue.
ignoreDupResponses
Ignores the response produced by a request if a more recent "similar" request is already
queued. ignoreDupResponses="true" does not cancel the processing of the request on
the server side; it only prevents unnecessary updates on the client side.
With Seam conversations, this option should be used with care, as it allows multiple concurrent
requests.
requestDelay
Defines the time in milliseconds for which the request remains on the queue. If, at this time, the
request has not been processed, the request is either sent (regardless of whether a response
has been received), or discarded (if there is a more recent "similar" event queued).
With Seam conversations, this option should be used with care, as it allows multiple concurrent
requests. T he delay that you set (in combination with the concurrent request timeout) must be
longer than the time the action will take to execute.
<a:poll render="total" interval="1000" />
Polls the server and renders an area, as required.
129
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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 statemanagement 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/JT A) 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. T his 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. T his 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:
T he 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.) T his 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. T herefore, 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. T he 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.
T his 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.
T he 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.
T o work around this problem, Hibernate users developed the open session in view pattern. T his is also
130
Chapter 9. Seam and Object/Relational Mapping
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. T he
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. T he 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.)
T he 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
com ponents.xm l:
<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 JT A 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 com ponents.xm l:
<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.EntityT ransaction interface. EntityT ransaction starts the
transaction at the beginning of the apply request values phase.
configure Hibernate managed transactions with the org.hibernate.T ransaction interface.
HibernateT ransaction starts the transaction at the beginning of the apply request values
phase.
configure Spring managed transactions with the
org.springfram ework.transaction.Platform T ransactionManager interface. T he
Spring Platform T ransactionManagem ent 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
T o configure JPA RESOURCE_LOCAL transaction management, add the following to your
131
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
com ponents.xm l, where #{em } is the name of the persistence:m anaged-persistencecontext component. If your managed persistence context is named entityManager, you may leave
out the entity-m anager attribute. (For further information, see Section 9.3, “Seam-managed
persistence contexts”.)
<transaction:entity-transaction entity-manager="#{em}"/>
T o configure Hibernate managed transactions, declare the following in your com ponents.xm l, where
#{hibernateSession} is the name of the project's persistence:m anaged-hibernatesession 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}"/>
T o explicitly disable Seam managed transactions, declare the following in your com ponents.xm l:
<transaction:no-transaction />
For information about configuring Spring-managed transactions see Section 25.5, “Using Spring
PlatformT ransactionManagement”.
9.2.3. Transaction synchronization
T ransaction synchronization provides callbacks for transaction-related events such as
beforeCom pletion() and afterCom pletion(). 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 com ponents.xm l 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 com ponents.xm l, write:
<persistence:managed-persistence-context name="bookingDatabase"
auto-create="true"
persistence-unit-jndi-name="java:/EntityManagerFactories/bookingData"/>
T his 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.
132
Chapter 9. Seam and Object/Relational Mapping
You must bind the EntityManagerFactory into JNDI. In JBoss, you can do this by adding the
following property setting to persistence.xm l.
<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 @ T ransactionAttribute(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. T herefore, 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 com ponents.xm l:
<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.xm l.
<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_com pletion to ensure that the session is
automatically synchronized before the JT A 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 m erge(), reloading data at the beginning of each request, or wrestling
133
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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. T his allows for truly atomic
conversations with EJB3 persistence. However, Hibernate provides this feature as a vendor extension to
the FlushModeT ypes defined by the specification. We expect other vendors will soon provide a similar
extension.
Seam lets you specify FlushModeT ype.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. T his means there is no manual flush available for SFSB.
9.4. Using the JPA "delegate"
T he 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
134
Chapter 9. Seam and Object/Relational Mapping
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 com ponents.xm l:
<factory name="session" scope="STATELESS" auto-create="true"
value="#{entityManager.delegate}"/>
T he 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.
T his 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. (T hese can only be used when Hibernate is
135
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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>
136
Chapter 10. JSF form validation in Seam
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.
137
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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. T his is a limitation of the JSF validation architecture.
T his approach defines constraints on the model, and presents constraint violations in the view.
T he 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>
T his 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. T o correlate the message with an input, you must define a label by
138
Chapter 10. JSF form validation in Seam
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.
T his 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>
:
139
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<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>
14 0
Chapter 10. JSF form validation in Seam
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}
14 1
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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. T he 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
14 2
Chapter 11. Groovy integration
previous example, the hotel class can be accessed from Java as hotel.getCity() — the getters and
setters are generated by the Groovy compiler. T his 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/m ain. 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. T his allows a seamless mix of Groovy and Java code.
11.3.2. Native .groovy file deployment at development time
14 3
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Seam supports .groovy file hot deployment (deployment without compilation) in incremental hot
deployment mode. T his 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. T he
GroovyBean components will deploy incrementally, without needing to restart either application or
application server.
Note
T he 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. T his 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.
T here is a Booking demonstration, written completely in Groovy and supporting incremental hot
deployment, in exam ples/groovybooking.
Important
T he groovybooking example uses m aven-antrun-plugin, which JBDS cannot import
automatically. As a result, the example cannot be built automatically in JBDS. T o build the
example, use Maven (mvn clean package).
14 4
Chapter 12. The Seam Application Framework
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.
T he 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. T he framework contains a handful of simple
classes that are easy to understand and to extend where required.
12.1. Introduction
T he components provided by the Seam Application Framework can be used in two separate
approaches. T he first approach is to install and configure an instance of the component in
com ponents.xm l, as with other built-in Seam components. For example, the following fragment (from
com ponents.xm l) 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;
}
}
T he 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: EntityHom e and
HibernateEntityHom e for CRUD, and EntityQuery and HibernateEntityQuery for queries.
T he Home and Query components are written so that they can be session-, event- or conversationscoped. T he scope depends upon the state model you wish to use in your application.
14 5
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
T he 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 personHom e 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(), rem ove(), update() and getInstance().
Before you can call rem ove() 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 com ponents.xm l (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 PersonHom e:
@Name("personHome")
public class PersonHome extends EntityHome<Person> {
@Factory("person")
public Person initPerson() {
return getInstance();
}
}
14 6
Chapter 12. The Seam Application Framework
T his 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>
T his 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
PersonHom e. 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:
14 7
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
@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);
}
}
T he Country could be an object managed by another Home object, for example, CountryHom e.
T o add more sophisticated operations (association management, etc.), we simply add methods to
PersonHom e.
@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();
}
}
T he Home object raises an org.jboss.seam .afterT ransactionSuccess event when a
transaction (a call to persist(), update() or rem ove()) 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 .afterT ransactionSuccess.<nam e>
(where <nam e>
is the name of the entity).
T he Home object automatically displays Faces messages when an operation succeeds. T o 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:
14 8
Chapter 12. The Seam Application Framework
@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");
}
}
T he best way to specify messages is to put them in a resource bundle known to Seam — by default, the
bundle named m essages.
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
T his 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:
14 9
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<pages>
<page view-id="/searchPerson.xhtml">
<param name="firstResult" value="#{people.firstResult}"/>
</page>
</pages>
T he 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. T he
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.
150
Chapter 12. The Seam Application Framework
<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>
T o refresh the query when the underlying entities change, we observe the
org.jboss.seam .afterT ransactionSuccess 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 PersonHom e:
<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
T he class Controller and its subclasses (EntityController, HibernateEntityController
and BusinessProcessController) are an optional part of the Seam Application Framework. T hese
classes provide a convenient method to access frequently-used built-in components and component
methods. T hey 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:
151
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
@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;
}
}
}
152
Chapter 13. Seam and JBoss Rules
Chapter 13. Seam and JBoss Rules
Seam makes it easy to call JBoss Rules (Drools) rulebases from Seam components.
13.1. Installing rules
T he 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 com ponents.xm l:
<drools:rule-base name="policyPricingRules">
<drools:rule-files>
<value>policyPricingRules.drl</value>
</drools:rule-files>
</drools:rule-base>
T his 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 APPLICAT ION 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. T his 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. T he RuleAgent can connect to a Drools rule server (BRMS), or hot-deploy rules
packages from a local file repository. T he RulesAgent-managed RuleBase is also configurable via
153
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
com ponents.xm l:
<drools:rule-agent name="insuranceRules"
configurationFile="/WEB-INF/deployedrules.properties" />
T he properties file contains properties specific to the RulesAgent. T he 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.WorkingMem ory available to each conversation. (Each
WorkingMem ory accumulates facts relating to the current conversation.)
<drools:managed-working-memory name="policyPricingWorkingMemory"
auto-create="true" rule-base="#{policyPricingRules}"/>
Notice that we referred the policyPricingWorkingMem ory 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 WorkingMem ory 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();
}
154
Chapter 14. Security
Chapter 14. Security
14.1. Overview
T he 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.
CAPT CHA support — to assist in the prevention of automated software/scripts abusing your Seambased site.
T his 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). T o 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 com ponents.xm l:
Entity Security
Hibernate Security Interceptor
Seam Security Interceptor
Page restrictions
Servlet API security integration
T his 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 (Seam LoginModule) to
delegate authentication to one of your own Seam components. (T his 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 thirdparty provider. Configuring this simplified authentication requires the identity component to be
configured in com ponents.xm l
155
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<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
T he authenticate-m ethod property specified for identity in com ponents.xm l specifies the
method used by Seam LoginModule to authenticate users. T his method takes no parameters, and is
expected to return a Boolean indicating authentication success or failure. Username and password are
obtained from Credentials.getUsernam e() 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(). T he 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. T he roles parameter
is populated with roles that the user is a member of. T his 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.
156
Chapter 14. Security
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()
T he 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. T he 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();
}
T his observer method can be placed anywhere, even in the Authenticator component itself. More
information about other security-related events appears later in the chapter.
157
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
information about other security-related events appears later in the chapter.
14.3.3. Writing a login form
T he credentials component provides both usernam e and password properties, catering for the
most common authentication scenario. T hese 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}. T his action clears the security state
of the currently authenticated user and invalidate the user's session.
14.3.4. Configuration Summary
T here are three easy steps to configure authentication:
Configure an authentication method in com ponents.xm l.
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 webbased applications. T he first mode allows the username to be stored in the user's browser as a cookie,
and leaves the browser to remember the password. T he 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.
158
Chapter 14. Security
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
rem em berMe.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. T oken-based Remember Me Authentication
T o use the automatic, token-based mode of the Remember Me feature, you must first configure a token
store. T hese 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.T okenStore interface. T his section assumes that you will be using the
provided JpaT okenStore implementation to store authentication tokens inside a database table.
First, create a new Entity to hold the tokens. T he following is one possible structure:
159
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
@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, @ T okenUsernam e and @ T okenValue, are used to configure the
username and token properties of the entity. T hese annotations are required for the entity that holds the
authentication tokens.
T he next step is to configure JpaT okenStore to store and retrieve authentication tokens with this
entity bean. Do this by specifying the token-class attribute in com ponents.xm l:
<security:jpa-token-store
token-class="org.jboss.seam.example.seamspace.AuthenticationToken"/>
T he final step is to configure the Rem em berMe component in com ponents.xm l. Its m ode should be
set to autoLogin:
<security:remember-me mode="autoLogin"/>
Users who check the Remember Me checkbox will now be authenticated automatically.
T o ensure that users are automatically authenticated when returning to the site, the following section
should be placed in com ponents.xm l:
<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>
160
Chapter 14. Security
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.xm l to redirect users to a more attractive page. T he two main exceptions thrown by the security
API are:
NotLoggedInException — T his exception is thrown when the user attempts to access a
restricted action or page when they are not logged in.
AuthorizationException — T his 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.xm l 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
T his 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 com ponents.xm l, 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
161
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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 HT T P Basic or HT T P Digest (RFC 2617) methods. For either form, you must first
enable the authentication-filter component in com ponents.xm l:
<web:authentication-filter url-pattern="*.seam" auth-type="basic"/>
T o 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"/>
T he key can be any String value. T he 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
T his 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-nam e property to
162
Chapter 14. Security
com ponents.xm l. For example, if you use Red Hat JBoss Enterprise Application Platform and want to
use the other policy (which uses the UsersRolesLoginModule login module provided by JBoss
Enterprise Application Platform), then the entry in com ponents.xm l 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. T he
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. T hese
components interact with the back-end security provider.
14.4.1. Configuring IdentityManager
T he identityManager component allows you to configure separate identity stores for authentication
and authorization. T his 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. T he default, JpaIdentityStore,
uses a relational database to store user and role information. T he other implementation is
LdapIdentityStore, which uses an LDAP directory to store users and roles.
T he identityManager component has two configurable properties: identityStore and
roleIndentityStore. T he 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 com ponents.xm l will
configure identityManager to use an LdapIdentityStore for both user-related and role-related
operations:
<security:identity-manager identity-store="#{ldapIdentityStore}"/>
T he following example configures identityManager to use an LdapIdentityStore for userrelated operations, and JpaIdentityStore for role-related operations:
<security:identity-manager identity-store="#{ldapIdentityStore}"
role-identity-store="#{jpaIdentityStore}"/>
T he following sections explain each identity storage method in greater detail.
163
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
14.4.2. JpaIdentityStore
T his 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.
T hese properties refer to the entity classes used to store user and role records, respectively. T he
following example shows the com ponents.xm l 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
T he following table describes the special annotations used to configure entity beans for user and role
storage.
T able 14 .1. User Entity Annotations
Annotation
Status
Description
@ UserPrincipal
Required
T his annotation marks the field or method
containing the user's username.
@ UserPassword
Required
T his 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 m d5, 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.
@ UserFirstNam e
Optional
T his annotation marks the field or method
containing the user's first name.
@ UserLastNam e
Optional
T his annotation marks the field or method
containing the user's last name.
@ UserEnabled
Optional
T his annotation marks the field or method
containing the enabled user status. T his should
be a Boolean property. If not present, all user
accounts are assumed to be enabled.
@ UserRoles
Required
T his annotation marks the field or method
containing the roles of the user. T his property will
be described in more detail in a later section.
164
Chapter 14. Security
T able 14 .2. Role Entity Annotations
Annotation
Status
Description
@ RoleNam e
Required
T his annotation marks the field or method
containing the name of the role.
@ RoleGroups
Optional
T his annotation marks the field or method
containing the group memberships of the role.
@ RoleConditional
Optional
T his 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. T his 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; }
}
165
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
@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
T his example builds on the previous minimal example by including all optional fields, and allowing group
memberships for roles.
166
Chapter 14. Security
@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; }
}
167
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
@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
T his 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. T he 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_CREAT ED
T his 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_AUT HENT ICAT ED
T his 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
T his 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. T he following
sections describe the configuration options for this identity store, and provide some configuration
examples.
168
Chapter 14. Security
14 .4 .3.1. Configuring LdapIdentityStore
T he following table describes the properties that can be configured in com ponents.xm l for
LdapIdentityStore.
169
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
T able 14 .3. LdapIdentityStore Configuration Properties
Property
Default Value
Description
server-address
localhost
T he address of
the LDAP server.
server-port
389
T he port number
that the LDAP
server listens on.
user-context-DN
ou=Person,dc=acm e,dc=com
T he Distinguished
Name (DN) of the
context containing
user records.
user-DN-prefix
uid=
T his value is
prefixed to the
front of the
username to
locate the user's
record.
user-DN-suffix
,ou=Person,dc=acm e,dc=com
T his value is
appended to the
end of the
username to
locate the user's
record.
role-context-DN
ou=Role,dc=acm e,dc=com
T he DN of the
context containing
role records.
role-DN-prefix
cn=
T his 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=acm e,dc=com
T his value is
appended to the
role name to form
the DN that
locates the role
record.
bind-DN
cn=Manager,dc=acm e,dc=com
T his is the context
used to bind to
the LDAP server.
bind-credentials
secret
T hese are the
credentials (the
password) used
to bind to the
LDAP server.
user-role-attribute
roles
T he attribute
name of the user
record containing
the list of roles
that the user is a
member of.
role-attribute-is-DN
true
T his Boolean
property indicates
whether the role
attribute of the
170
Chapter 14. Security
user record is
itself a
distinguished
name.
user-nam e-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-nam e-attribute
null
Indicates the user
record attribute
containing the
user's first name.
last-nam e-attribute
sn
Indicates the user
record attribute
containing the
user's last name.
full-nam e-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-nam e-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
T he following configuration example shows how LdapIdentityStore can be configured for an LDAP
directory running on fictional host directory.m ycom pany.com . T he users are stored within this
directory under the ou=Person,dc=m ycom pany,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=m ycom pany,dc=com , and are referenced from the user's entry via the roles
171
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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.m anagem ent.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-m ethod from the identity configuration in com ponents.xm l, and the
Seam LoginModule 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();
T he following table describes IdentityManager's API methods:
172
Chapter 14. Security
T able 14 .4 . Identity Management API
Method
Returns
Description
createUser(String nam e,
String password)
boolean
Creates a new user account, with the
specified name and password. Returns
true if successful; otherwise, returns
false.
deleteUser(String nam e)
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 nam e)
boolean
Deletes the role with the specified
name. Returns true if successful;
otherwise, returns false.
enableUser(String nam e)
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 nam e)
boolean
Disables the user account with the
specified name. Returns true if
successful; otherwise, returns false.
changePassword(String nam e,
String password)
boolean
Changes the password for the user
account with the specified name.
Returns true if successful; otherwise,
returns false.
isUserEnabled(String nam e)
boolean
Returns true if the specified user
account is enabled; otherwise, returns
false.
grantRole(String nam e,
String role)
boolean
Grants the specified role to the
specified user or role. T he 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 nam e,
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 nam e)
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 nam e)
List
Returns a list of all roles explicitly
granted to the specified user name.
getIm pliedRoles(String nam e)
List
Returns a list of all roles implicitly
granted to the specified user name.
173
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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 adm in role is a member
of the user role, and a user is a
member of the adm in role, then the
implied roles for the user are both the
adm in, and user roles.
authenticate(String nam e,
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.
addRoleT oGroup(String role,
String group)
boolean
Adds the specified role as a member of
the specified group. Returns true if the
operation is successful.
rem oveRoleFrom Group(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.
T he following table describes the permission requirements for each of the methods in
IdentityManager. T he permission targets listed below are literal String values.
T able 14 .5. Identity Management Security Permissions
Method
Permission T arget
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
addRoleT oGroup()
seam .role
update
rem oveRoleFrom Group()
seam .role
update
T he following code listing provides an example set of security rules that grants all adm in role members
access to all Identity Management-related methods:
174
Chapter 14. Security
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
T he security API produces a number of default Faces messages for various security-related events.
T he following table lists the message keys to specify in a m essage.properties resource file if you
want to override the messages. T o suppress a message, add the key (with an empty value) to the
resource file.
T able 14 .6. Security Message Keys
Message Key
Description
org.jboss.seam .loginSuccessful
T his message is produced
when a user successfully logs
in via the security API.
org.jboss.seam .loginFailed
T his 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
T his 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
T his message is produced
when a user that is already
authenticated attempts to log in
again.
14.6. Authorization
T his 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
com ponents.xm l file — see the Configuration section previous.
175
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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. T hey are simple constructs, consisting of a name (such as "admin", "user",
"customer", etc.) applied to users, or other roles. T hey 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. T he 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. T herefore, we recommend using the typesafe equivalent
(described later in this chapter) for its compile-time safety.
14 .6.2.1. T he @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 com ponentNam e:m ethodNam e. T ake for
example the following component method:
176
Chapter 14. Security
@Name("account")
public class AccountAction {
@Restrict
public void delete() {
...
}
}
In this example, account:delete is the implied permission required to call the delete() method.
T his is equivalent to writing @ Restrict("#{s:hasPerm ission('account','delete')}"). T he
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. T his 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 adm in role.
Before we go further, we will address the #{s:hasRole()} expression seen in the previous example.
s:hasRole and s:hasPerm ission are EL functions that delegate to the correspondingly-named
methods of the Identity class. T hese 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. T his is extremely useful when checking permissions for a specific object instance. T ake 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 hasPerm ission() function call refers to selectedAccount. T he value of this
variable will be looked up from within the Seam context, and passed to the hasPerm ission() method
in Identity. T his 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.
T o do so, use Identity.checkRestriction() to evaluate a security expression, like this:
public void deleteCustomer() {
Identity.instance().checkRestriction("#{s:hasPermission(selectedCustomer,
'delete')}");
}
177
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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 hasPerm ission() 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 m anager role. One
way you could write this is the following:
<h:outputLink action="#{reports.listManagerReports}"
rendered="#{s:hasRole('manager')}"> Manager Reports
</h:outputLink>
T his, too, is straightforward — if the user is not a member of the m anager role, the outputLink will not
be rendered. T he 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:dataT able control on a page, and you want to render action links on its records only for users with
certain privileges. T he s:hasPerm ission EL function lets us use an object parameter to determine
whether the user has the necessary permission for that object. A dataT able 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>
178
Chapter 14. Security
14.6.4. Securing pages
T o use page security, you will need a pages.xm l 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.xhtm l:render permission, and /viewId.xhtm l: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>
T his page requires an implied permission of /settings.xhtm l:render for non-Faces requests, and
an implied permission of /settings.xhtm l: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 adm in role.
14.6.5. Securing Entities
Seam Security also lets you apply security restrictions to certain actions (read, insert, update, and
delete) for entities.
T o 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.
@ PreRem ove — Called before an entity is deleted. Use this method to configure a delete
permission.
T he following example shows how an entity can be configured to perform a security check for insert
operations. Note that the method need not perform any action, but it must be annotated correctly.
@PrePersist
@Restrict
public void prePersist() {}
179
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Using /META-INF/orm.xml
You can also specify the callback method in /MET A-INF/orm .xm l:
<?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 Custom er with @ Restrict.
T he following configuration is based on the Seamspace example, and checks if the authenticated user
has permission to insert a new Mem berBlog record. T he entity being checked is automatically inserted
into the working memory (in this case, Mem berBlog):
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;
T his rule grants the permission m em berBlog: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. T he 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 MET A-INF/orm .xm l file:
180
Chapter 14. Security
<?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 .xm l, 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. T hese support arbitrary EL
expressions differently, which gives them additional compile-time safety.
Seam comes with a set of annotations for standard CRUD-based permissions. T he following
annotations are provided in the org.jboss.seam .annotations.security package:
@Insert
@Read
@Update
@Delete
T o 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. T ake 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 Custom er objects. T he target of the permission check is Custom er.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) {
...
}
T o create your own security annotation, just annotate it with @ Perm issionCheck. For example:
181
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
@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 @ Perm issionCheck 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.Adm in) out of the box. T his restricts access of a
particular method to users that belong to the adm in role, as long as such a role is supported by your
application. T o 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. T he
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. T he following
class diagram shows an overview of the main components of the permission framework:
182
Chapter 14. Security
T he 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 Perm issionResolver implementations, which are described in greater detail later in
the chapter:
RuleBasedPerm issionResolver — Resolves rule-based permission checks with Drools.
PersistentPerm issionResolver — 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. T he Perm issionResolver interface defines
two methods that must be implemented, as seen in the following table. If your Perm issionResolver is
deployed in your Seam project, it will be scanned automatically during deployment and registered with
the default ResolverChain.
183
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
T able 14 .7. PermissionResolver interface
Return type
Method
Description
boolean
hasPerm ission(Object
target, String action)
T his 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<Obje
ct>
targets, String action)
T his method removes any objects
from the specified set that would
return true if passed to the
hasPerm ission() method with the
same action parameter value.
Note
Because they are cached in the user's session, any custom Perm issionResolver
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 applicationor 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 Perm issionResolvers, to resolve object permissions
for a particular object class or permission target.
T he default ResolverChain consists of all permission resolvers discovered during application
deployment. T he org.jboss.seam .security.defaultResolverChainCreated event is raised
(and the ResolverChain instance passed as an event parameter) when the default ResolverChain
is created. T his 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.
T he 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:hasPerm ission EL function, or via an API call to
Identity.checkPerm ission:
184
Chapter 14. Security
1. A permission check is initiated (either in code or via an EL expression), resulting in a call to
Identity.hasPerm ission().
1.1. Identity invokes Perm issionMapper.resolvePerm ission(), passing in the permission
to be resolved.
1.1.1. Perm issionMapper 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 Perm issionResolvers it contains by calling
ResolverChain.getResolvers().
1.1.2. For each Perm issionResolver in the ResolverChain, the Perm issionMapper invokes
its hasPerm ission() method, passing in the permission instance to be checked. If the
Perm issionResolvers return true, the permission check has succeeded and the
Perm issionMapper also returns true to Identity. If none of the Perm issionResolvers
return true, then the permission check has failed.
14.6.9. RuleBasedPermissionResolver
One of the built-in permission resolvers provided by Seam. T his 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
T he configuration for RuleBasedPerm issionResolver requires that a Drools rule base is first
configured in com ponents.xm l. By default, it expects the rule base to be named securityRules, as
per the following example:
185
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<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/security2.3.xsd">
<drools:rule-base name="securityRules">
<drools:rule-files>
<value>/META-INF/security.drl</value>
</drools:rule-files>
</drools:rule-base>
</components>
T he default rule base name can be overridden by specifying the security-rules property for
RuleBasedPerm issionResolver:
<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
T he first step to writing security rules is to create a new rule file in the /MET A-INF directory of your
application's jar file. T his file should be named security.drl or similar, but can be named anything as
long as it is configured correspondingly in com ponents.xm l.
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. T he
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 Perm issionCheck and Role classes. T hese 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 CanUserDeleteCustom ers and will be
used to check whether a user is allowed to delete a customer record.
T here are two distinct sections in the body of the rule definition. Rules have a left hand side (LHS) and a
right hand side (RHS). T he LHS is the conditional portion of the rule, that is, a list of conditions that must
be satisfied for the rule to fire. T he LHS is represented by the when section. T he RHS is the
186
Chapter 14. Security
consequence or action section of the rule, which will only be fired if all conditions in the LHS are met. T he
RHS is represented by the then section. T he end of the rule is denoted by the end line.
T here are two conditions listed in the example LHS. T he first condition is:
c: PermissionCheck(target == "customer", action == "delete")
More plainly, this condition states that, to be fulfilled, there must be a Perm issionCheck object with a
target property equal to custom er, 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 hasPerm ission() method is called, a temporary
Perm issionCheck object, or Fact, is inserted into the working memory. T his Perm issionCheck
corresponds exactly to the permission being checked, so if you call hasPerm ission("account",
"create"), a Perm issionCheck 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 Perm issionCheck facts, there is also an org.jboss.seam .security.Role fact for
each role that the authenticated user is a member of. T hese 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. T he
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
RuleBasedPerm issionResolver.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.
T o return to our simple example, the first line of our LHS is prefixed with c:. T his is a variable binding,
and is used to refer back to the object matching the condition (in this case, the Perm issionCheck).
T he second line of the LHS is:
Role(name == "admin")
T his condition states that there must be a Role object with a nam e of "admin" within the working
memory. So, if you are checking for the custom er:delete permission and the user is a member of the
adm in role, this rule will fire.
T he RHS shows us the consequence of the rule firing:
c.grant()
T he RHS consists of Java code. In this case it invokes the grant() method of the c object, which is a
variable binding for the Perm issionCheck object. Other than the nam e and action properties of the
Perm issionCheck object, there is also a granted property. T his is initially set to false. Calling
grant() on a Perm issionCheck sets the granted property to true. T his 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. T he following rule shows one way this can be
expressed, by requiring that the target of the permission check be an instance of Mem berBlog, and
that the currently authenticated user be a member of the user role:
187
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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 Perm issionCheck in your rule, like so:
rule CanDoAnythingToCustomersIfYouAreAnAdmin
when
c: PermissionCheck(target == "customer")
Role(name == "admin")
then
c.grant();
end;
T his rule allows users with the adm in role to perform any action for any custom er permission check.
14.6.10. PersistentPermissionResolver
Another built-in permission resolver provided by Seam, PersistentPerm issionResolver, allows
permissions to be loaded from persistent storage, such as a relational database. T his 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
T o use PersistentPerm issionResolver, you must configure a valid Perm issionStore in
com ponents.xm l. If this is not configured, the PersistentPerm issionResolver will attempt to
use the default permission store, Section 14.4.2.4, “JpaIdentityStore Events”. T o use a permission store
other than the default, configure the perm ission-store property as follows:
<security:persistent-permission-resolver
permission-store="#{myCustomPermissionStore}"/>
14 .6.10.2. Permission Stores
PersistentPerm issionResolver requires a permission store to connect to the back-end storage
where permissions are persisted. Seam provides one Perm issionStore implementation out of the
box, JpaPerm issionStore, which stores permissions inside a relational database. You can write your
own permission store by implementing the Perm issionStore interface, which defines the following
methods:
188
Chapter 14. Security
T able 14 .8. PermissionStore interface
Return type
Method
Description
List<Perm ission>
listPerm issions(Object target)
T his method should
return a List of
Perm ission objects
representing all the
permissions granted
for the specified target
object.
List<Perm ission>
listPerm issions(Object target,
String action)
T his method should
return a List of
Perm ission objects
representing all the
permissions with the
specified action
granted for the
specified target object.
List<Perm ission>
listPerm issions(Set<Object>
targets, String action)
T his method should
return a List of
Perm ission objects
representing all the
permissions with the
specified action
granted for the
specified set of target
objects.
boolean
grantPerm ission(Perm ission)
T his method should
persist the specified
Perm ission object to
the back-end storage,
and return true if
successful.
boolean
grantPerm issions(List<Perm ission>
perm issions)
T his method should
persist all of the
Perm ission objects
contained in the
specified List, and
return true if
successful.
boolean
revokePerm ission(Perm ission
perm ission)
T his method should
remove the specified
Perm ission object
from persistent
storage.
boolean
revokePerm issions(List<Perm ission>
perm issions)
T his method should
remove all of the
Perm ission objects
in the specified list from
persistent storage.
List<String>
listAvailableActions(Object target)
T his 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
189
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
permission
management to build
the user interface for
granting specific class
permissions.
14 .6.10.3. JpaPermissionStore
T he Seam-provided default Perm issionStore 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. T hese 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-perm ission-class property. T o user
separate tables for user and role permission storage, you must also configure the roleperm ission-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"/>
T o 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
T he 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.perm ission package. T he
following table describes these annotations:
190
Chapter 14. Security
T able 14 .9. Entity Permission annotations
Annotation
T arget
Description
@ Perm issionT arget
FIELD,MET HOD
T his annotation identifies the entity
property containing the permission
target. T he property should be of type
java.lang.String.
@ Perm issionAction
FIELD,MET HOD
T his annotation identifies the entity
property containing the permission
action. T he property should be of type
java.lang.String.
@ Perm issionUser
FIELD,MET HOD
T his 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.
@ Perm issionRole
FIELD,MET HOD
T his 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.
@ Perm issionDiscrim inator
FIELD,MET HOD
T his 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
T he following is an example of an entity class that stores both user and role permissions, taken from the
Seamspace example.
191
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
@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 getDiscrim inator() method has been annotated with @ Perm issionDiscrim inator,
to allow JpaPerm issionStore to determine which records represent user permissions and which
represent role permissions. T he getRecipient() method is annotated with both @ Perm issionUser
and @ Perm issionRole. T his 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 discrim inator property.
14 .6.10.3.3. Class-specific Permission Configuration
T he permissions included in the org.jboss.seam .annotation.security.perm ission package
can be used to configure a specific set of allowable permissions for a target class.
192
Chapter 14. Security
T able 14 .10. Class Permission Annotations
Annotation
T arget
Description
@ Perm issions
T YPE
A container annotation, which can
contain an array of @ Perm ission
annotations.
@ Perm ission
T YPE
T his annotation defines a single
allowable permission action for the
target class. Its action property
must be specified, and an optional
m ask property may also be specified
if permission actions are to be
persisted as bitmasked values (see
section following).
T he following shows the above annotations in use. T hey can also be seen in the SeamSpace example.
@Permissions({
@Permission(action = "view"),
@Permission(action = "comment")
})
@Entity
public class MemberImage implements Serializable {...}
T his example demonstrates how two allowable permission actions, view and com m ent can be declared
for the entity class Mem berIm age.
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 com m ent permissions for a particular
Mem berIm age (an entity bean) instance, then by default the action property of the permission entity
will contain "view,com m ent", 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 {...}
T he 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
T he m ask values specified must be powers of 2.
14 .6.10.3.5. Identifier Policy
When storing or looking up permissions, JpaPerm issionStore must be able to uniquely identify
193
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
specific object instances. T o 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.
T he IdentifierStrategy interface is very simple, declaring only two methods:
public interface IdentifierStrategy {
boolean canIdentify(Class targetClass);
String getIdentifier(Object target);
}
T he first method, canIdentify(), returns true if the identifier strategy is capable of generating a
unique identifier for the specified target class. T he 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.
T o explicitly configure a specific identifier strategy for a particular class, annotate the strategy with
org.jboss.seam .annotations.security.perm ission.Identifier and set the value to a
concrete implementation of the IdentifierStrategy interface. You may also specify a nam e
property. (T he effect of this depends upon the IdentifierStrategy implementation used.)
14 .6.10.3.6. ClassIdentifierStrategy
T his identifier strategy generates unique identifiers for classes, and uses the value of the nam e (if
specified) in the @ Identifier annotation. If no nam e 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 custom er:
@Identifier(name = "customer")
public class Customer {...}
T he identifier for the following class will be custom erAction:
@Name("customerAction")
public class CustomerAction {...}
Finally, the identifier for the following class will be Custom er:
public class Customer {...}
14 .6.10.3.7. EntityIdentifierStrategy
T his 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. T he rules
for generating the name section of the identifier are similar to those in ClassIdentifierStrategy.
T he 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:
194
Chapter 14. Security
@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 Custom er instance with an id value of 1, the value of the identifier would be Custom er:1. If the
entity class is annotated with an explicit identifier name, like so:
@Entity @Identifier(name = "cust")
public class Customer {...}
T hen a Custom er 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
Perm issionManager component.
14.7.1. PermissionManager
T he Perm issionManager 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 JpaPerm issionStore. T o configure a custom permission store,
specify the perm ission-store property in com ponents.xm l:
<security:permission-manager permission-store="#{ldapPermissionStore}"/>
T he following table describes each of the methods provided by Perm issionManager:
195
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
T able 14 .11. PermissionManager API methods
Return type
Method
Description
List<Perm ission>
listPerm issions(Object target,
String action)
Returns a list of
Perm ission objects
representing all of the
permissions that have
been granted for the
specified target and
action.
List<Perm ission>
listPerm issions(Object target)
Returns a list of
Perm ission objects
representing all of the
permissions that have
been granted for the
specified target and
action.
boolean
grantPerm ission(Perm ission
perm ission)
Persists (grants) the
specified Perm ission
to the back-end
permission store.
Returns true if the
operation succeeds.
boolean
grantPerm issions(List<Perm ission>
perm issions)
Persists (grants) the
specified list of
Perm issions to the
back-end permission
store. Returns true if
the operation
succeeds.
boolean
revokePerm ission(Perm ission
perm ission)
Removes (revokes) the
specified Perm ission
from the back-end
permission store.
Returns true if the
operation succeeds.
boolean
revokePerm issions(List<Perm ission>
perm issions)
Removes (revokes) the
specified list of
Perm issions 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.
T he actions that this
method returns are
dependent on the
@ Perm ission
annotations configured
on the target object's
class.
14.7.2. Permission checks for PermissionManager operations
T o invoke Perm issionManager methods, the currently authenticated user must be authorized to
196
Chapter 14. Security
perform that management operation. T he following table lists the permissions required to invoke a
particular method.
T able 14 .12. Permission Management Security Permissions
Method
Permission T arget
Permission Action
listPerm issions()
T he specified target.
seam .read-perm issions
grantPerm ission()
T he target of the specified
Perm ission, or each of the
targets for the specified list of
Perm issions (depending on
the method called).
seam .grant-perm ission
grantPerm ission()
T he target of the specified
Perm ission.
seam .grant-perm ission
grantPerm issions()
Each of the targets of the
specified list of Perm issions.
seam .grant-perm ission
revokePerm ission()
T he target of the specified
Perm ission.
seam .revoke-perm ission
revokePerm issions()
Each of the targets of the
specified list of Perm issions.
seam .revoke-perm ission
14.8. SSL Security
Seam includes basic support for serving sensitive pages via the HT T PS protocol. T o configure this,
specify a schem e for the page in pages.xm l. T he following example shows how the view
/login.xhtm l can be configured to use HT T PS:
<page view-id="/login.xhtml" scheme="https"/>
T his 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 HT T PS protocol because /login.xhtm l 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 schem e="https" page with
HT T P triggers a redirect to the same page using HT T PS.
You can also configure a default scheme for all pages. T his is useful if you only want to use HT T PS for
a few pages. If no default scheme is specified, the current scheme will be used. So, once the user
accesses a page requiring HT T PS, then HT T PS continues to be used after the user has navigated to
other non-HT T PS pages. T his is good for security, but not for performance. T o define HT T P as the
default schem e, add this line to pages.xm l:
<page view-id="*" scheme="http" />
If none of the pages in your application use HT T PS, you need not define a default scheme.
You can configure Seam to automatically invalidate the current HT T P session each time the scheme
changes. T o do so, add this line to com ponents.xm l:
<web:session invalidate-on-scheme-change="true"/>
T his option offers more protection from session ID sniffing and sensitive data leakage from pages using
HT T PS to pages using HT T P.
197
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
14.8.1. Overriding the default ports
If you wish to configure the HT T P and HT T PS ports manually, you can do so in pages.xm l 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
T hough not strictly part of the security API, Seam provides a built-in CAPT CHA (Completely Automated
Public T uring test to tell Computers and Humans Apart) algorithm to prevent automated processes from
interacting with your application.
14.9.1. Configuring the CAPTCHA Servlet
T o use CAPT CHA, you need to configure the Seam Resource Servlet, which provides your pages with
CAPT CHA challenge images. Add the following to web.xm l:
<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 CAPT CHA 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"/>
T hat is all you need to do. T he graphicIm age control displays the CAPT CHA challenge, and the
inputT ext receives the user's response. T he response is automatically validated against the
CAPT CHA when the form is submitted.
14.9.3. Customizing the CAPTCHA algorithm
You can customize the CAPT CHA algorithm by overriding the built-in component:
198
Chapter 14. Security
@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
T he 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.
T able 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.rem em berMe
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. T his class allows either the Principal or Subject, or the
user's roles, to be overridden for a single set of operations.
T he following code demonstrates RunAsOperation usage. T he addRole() method is called to
provide a set of roles to 'borrow' for the duration of the operation. T he execute() method contains the
code that will be executed with the elevated privileges.
199
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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.
T he following example shows an Identity component extended with an additional com panyCode field to
handle credentials. (Usually this would be handled by a Credentials component.) T he install
precedence of APPLICAT ION 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
200
Chapter 14. Security
OpenID integration is a Technology Preview
T echnology 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 T echnology 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. T his 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.
T he ID takes the form of a URL — http://m axim oburrito.m yopenid.com , for example. (T he
http:// portion of the identifier can be omitted when logging into a site.) T he 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. T he local web application can
then assume that the user accessing the application owns the OpenID presented.
However, authentication does not imply authorization. T he web application must still determine how to
treat the OpenID authentication. T he 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. T his is a design decision for the local application.
14.13.1. Configuring OpenID
Seam uses the openid4 java package, and requires four additional JARs to make use of Seam
integration. T hese are htm lparser.jar, openid4 java.jar, openxri-client.jar and
openxri-syntax.jar.
OpenID processing requires the OpenIdPhaseListener, which should be added to your facesconfig.xm l file. T he phase listener processes the callback from the OpenID provider, allowing reentry into the local application.
<lifecycle>
<phase-listener>
org.jboss.seam.security.openid.OpenIdPhaseListener
</phase-listener>
</lifecycle>
T his configuration makes OpenID support available to your application. T he OpenID support component,
org.jboss.seam .security.openid.openid, is installed automatically if the openid4 java
classes are on the classpath.
14.13.2. Presenting an OpenIdLogin form
T o initiate an OpenID log in, present a form to the user requesting the user's OpenID. T he
#{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>
201
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
When the user submits the login form, they are redirected to their OpenID provider. T he user eventually
returns to your application through the Seam pseudo-view /openid.xhtm l, provided by the
OpenIdPhaseListener. Your application can handle the OpenID response with a pages.xm l
navigation from that view, just as if the user had never left your application.
14.13.3. Logging in immediately
T he simplest strategy is to simply log the user in immediately. T he following navigation rule shows how
to handle this using the #{openid.loginIm m ediately()} 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>
T he loginIm m ediately() 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 loginIm m ediately() action returns true. If
the OpenID is not validated, the method returns false, and the user re-enters the application unauthenticated. 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 CAPT CHA to avoid
programmatic registrations. When your process is complete, you can log the user in by calling the
loginIm m ediately 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.
202
Chapter 15. Internationalization, localization and themes
Chapter 15. Internationalization, localization and themes
T here 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 UT F-8 encoding. (Setting the correct encoding falls outside the scope
of this tutorial.)
15.1.1. Application server configuration
T o 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 Red Hat JBoss Enterprise
Application Platform, add the system properties org.apache.catalina.connector.URI_ENCODING and
org.apache.catalina.connector.USE_BODY_ENCODING_FOR_QUERY_ST RING to the server
configuration. For JBoss Enterprise Application Platform change
${JBOSS_HOME}/standalone/configuration/standalone.xm l:
<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. T herefore, 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. T he 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 UT F-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.
203
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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. T o do so, alter the encoding attribute value in the XML declaration <?xm l
version="1.0" encoding="UT F-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 com ponents.xm l:
<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 HT T P request (the browser locale), and that locale is in the list of
supported locales from faces-config.xm l, use that locale for the rest of the session.
Otherwise, if a default locale was specified in the faces-config.xm l, 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.xm l, 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. T hen, define the default locale and
supported locales as follows:
204
Chapter 15. Internationalization, localization and themes
<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 languagespecific 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 m essages 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 m essages, so you will need to define your labels in files named
m essages.properties, m essages_en.properties, m essages_en_AU.properties, etc. T hese
files usually belong in the WEB-INF/classes directory.
So, in m essages_en.properties:
Hello=Hello
And in m essages_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.bundleNam es. 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>
T o 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
welcom e/hello_en.properties if we only needed to display the message on
/welcom e/hello.xhtm l.
You can even specify an explicit bundle name in pages.xm l:
<page view-id="/welcome/hello.xhtml" bundle="HelloMessages"/>
T hen we could use messages defined in HelloMessages.properties on
/welcom e/hello.xhtm l.
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']}"/>
205
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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
T he facesMessages component is a convenient way to display success or failure messages to the
user. T he 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");
}
}
T his will display Hello, Gavin King or G'day, Gavin, depending upon the user's locale.
15.4. Timezones
T here is also a session-scoped instance of java.util.T im ezone, named
org.jboss.seam .international.tim ezone, and a Seam component for changing the timezone
named org.jboss.seam .international.tim ezoneSelector. By default, the timezone is the
default timezone of the server. Unfortunately, the JSF specification assumes all dates and times are
UT C, and displayed as UT C, unless a different timezone is explicitly specified with
<f:convertDateT im e>
.
Seam overrides this behavior, and defaults all dates and times to the Seam timezone. In addition, Seam
provides the <s:convertDateT im e>
tag, which always performs conversions in the Seam timezone.
Seam also provides a default date converter to convert a string value to a date. T his saves you from
having to specify a converter on input fields that capture dates. T he 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. T he 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:
206
Chapter 15. Internationalization, localization and 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>
T he first theme listed is the default theme.
T hemes 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
T he 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:com position>
:
<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
T he 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 com ponents.xm l:
<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"/>
207
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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:form attedT ext/>
control to display formatted text that conforms to the Seam Text language. Seam T ext is implemented
with an ANT LR-based parser. (Experience with ANT LR is not required.)
16.1. Basic formatting
Here is a simple example: It's easy to m ake * em phasized* , |m onospaced|, ~deleted~,
super^scripted^ or _underlined_ text.
If we display this using <s:form attedT ext/>
, the following HT ML 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. T his
is the HT ML 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>
T he # character creates items in an ordered list. Unordered lists use the = character:
208
Chapter 16. Seam Text
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 HT ML 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:
209
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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. T his 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 T ext parser to understand wikiword links written
in this syntax.
16.4. Entering HTML
T ext can include a certain limited subset of HT ML. (T he subset was selected to remain safe from crosssite scripting attacks.) T his 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
210
Chapter 16. Seam Text
T he <s:form attedT ext/>
JSF component uses the org.jboss.seam .text.Seam T extParser internally. You can use this
class directly to implement your own text parsing, rendering, and HT ML sanitation procedures. If you
have a custom front-end interface for entering rich text, such as a JavaScript-based HT ML 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.
T he following example defines a custom text parser, which overrides the default HT ML 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();
}
}
linkT ag() and paragraphOpenT ag() methods are two of the methods you can override in order to
customize rendered output. T hese methods usually return String output. For further details, refer to
the Java Documentation. T he org.jboss.seam .text.Seam T extParser.DefaultSanitizer
Java Documentation also contains more information about the HT ML elements, attributes, and attribute
values that are filtered by default.
211
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Chapter 17. iText PDF generation
Seam now includes a component set for generating documents with iT ext. T he primary focus of Seam's
iT ext document support is for the generation of PDF documents, but Seam also offers basic support for
RT F document generation.
17.1. Using PDF Support
iT ext support is provided by jboss-seam -pdf.jar. T his JAR contains the iT ext JSF controls (which
construct views that can render to PDF) and the DocumentStore component (which serves the rendered
documents to the user). T o include PDF support in your application, place jboss-seam -pdf.jar in
your WEB-INF/lib directory along with the iT ext JAR file. No further configuration is required to use
Seam's iT ext support.
T he Seam iT ext module requires that Facelets be used as the view technology. It also requires the use
of the seam -ui package.
T he exam ples/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:docum ent>
Description
Documents are generated by Facelet XHT ML files using tags in the
http://jboss.org/schem a/seam /pdf namespace. Documents should
always have the docum ent tag at the root of the document. T he
docum ent tag prepares Seam to generate a document into the
DocumentStore and renders an HT ML redirect to that stored content.
Attributes
type
T he type of the document to be produced. Valid values are PDF,
RT F and HT ML. Seam defaults to PDF generation, and many
features only work correctly when generating PDF documents.
pageSize
T he size of the page to be generated. T he most commonly used
values are LET T ER 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. T he value "612 792", for example, is equivalent to
the LET T ER page size.
orientation
T he orientation of the page. Valid values are portrait and
landscape. Landscape mode swaps the height and width page
values.
m argins
T he left, right, top and bottom margin values.
m arginMirroring
Indicates that margin settings should be reversed on alternating
pages.
disposition
When generating PDFs in a web browser, this determines the
HT T P Content-Disposition of the document. Valid values are
inline, which indicates the document should be displayed in the
212
Chapter 17. iText PD F generation
browser window if possible, and attachm ent, which indicates that
the document should be treated as a download. T he default value
is inline.
fileNam e
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:im age>
and <p:paragraph>
tags form the foundations of simple documents. T ags 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
m ultipliedLeading
spacingBefore — T he blank space to be inserted before the
element.
spacingAfter — T he blank space to be inserted after the element.
indentationLeft
indentationRight
keepT ogether
Usage
<p:paragraph alignment="justify">
This is a simple document. It is not very fancy.
</p:paragraph>
<p:text>
Description
T he text tag lets application data produce text fragments using normal JSF
converter mechanisms. It is very similar to the outputT ext tag used when
rendering HT ML documents.
Attributes
213
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
value — T he value to be displayed. T his 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:htm l>
Description
T he htm l tag renders HT ML content into the PDF.
Attributes
value — T he 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
T he font tag defines the default font to be used for all text inside of it.
Attributes
nam e — T he font name, for example: COURIER, HELVET ICA, T IMESROMAN, SYMBOL or ZAPFDINGBAT S.
size — T he point size of the font.
style — T he font styles. Any combination of : NORMAL, BOLD, IT ALIC,
OBLIQUE, UNDERLINE, LINE-T HROUGH
encoding — T he character set encoding.
Usage
<p:font name="courier" style="bold" size="24">
<p:paragraph>My Title</p:paragraph>
</p:font>
<p:textcolum n>
Description
p:textcolum n inserts a text column that can be used to control the flow of
text. T he most common case is to support right to left direction fonts.
Attributes
214
Chapter 17. iText PD F generation
left — T he left bounds of the text column
right — T he right bounds of the text column
direction — T he run direction of the text in the column: RT L, LT R,
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:im age>
Description
p:im age 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. T he
im ageData attribute can specify a value binding expression whose value
is a java.awt.Im age object.
Attributes
value — A resource name or a method expression that binds to an
application-generated image.
rotation — T he rotation of the image in degrees.
height — T he height of the image.
width — T he width of the image.
alignm ent — T he 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 — T he blank space to be inserted before the
element.
spacingAfter — T he blank space to be inserted after the element.
widthPercentage
initialRotation
dpi
scalePercent — T he scaling factor (as a percentage) to use for the
image. T his 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}" />
215
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<p:anchor>
Description
p:anchor defines clickable links from a document. It supports the following
attributes:
Attributes
nam e — T he name of an in-document anchor destination.
reference — T he 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 nam e of link1. T o 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>
Description
<p:footer>
T he 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
alignm ent — T he alignment of the header/footer box section. (See
Section 17.1.7.2, “Alignment Values” for alignment values.)
backgroundColor — T he background color of the header/footer box.
(See Section 17.1.7.1, “Color Values” for color values.)
borderColor — T he border color of the header/footer box. Individual
border sides can be set using borderColorLeft,
borderColorRight, borderColorT op and
borderColorBottom . (See Section 17.1.7.1, “Color Values” for color
values.)
borderWidth — T he width of the border. Individual border sides can
be specified using borderWidthLeft, borderWidthRight,
borderWidthT op 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:pageNum ber>
Description
T he current page number can be placed inside a header or footer with the
216
Chapter 17. iText PD F generation
p:pageNum ber tag. T he 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>
Description
<p:section>
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
alignm ent — T he alignment of the header/footer box section. (See
Section 17.1.7.2, “Alignment Values” for alignment values.)
num ber — T he chapter number. Every chapter should be assigned a
chapter number.
num berDepth — T he depth of numbering for section. All sections are
numbered relative to their surrounding chapters/sections. T he fourth
section of the first section of chapter three would be section 3.1.4, if
displayed at the default number depth of 3. T o 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. T he title will be displayed
next to the chapter or section number. T he 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-
217
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
nested sublists. List items may not be used outside of a list. T he 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 — T he ordering/bulleting style of the list. One of: NUMBERED,
LET T ERED, GREEK, ROMAN, ZAPFDINGBAT S,
ZAPFDINGBAT S_NUMBER. If no style is given, the list items are bulleted
by default.
listSym bol — For bulleted lists, specifies the bullet symbol.
indent — T he indentation level of the list.
lowerCase — For list styles using letters, indicates whether the letters
should be lower case.
charNum ber — For ZAPFDINGBAT S, indicates the character code of
the bullet character.
num berT ype — For ZAPFDINGBAT S_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
alignm ent — T he alignment of the header/footer box section. (See
Section 17.1.7.2, “Alignment Values” for alignment values.)
alignm ent — T he alignment of the list item. (See Section 17.1.7.2,
“Alignment Values” for possible values.)
indentationLeft — T he left indentation amount.
indentationRight — T he right indentation amount.
listSym bol — Overrides the default list symbol for this list item.
Usage
...
17.1.6. Tables
T able 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.
218
Chapter 17. iText PD F generation
<p:table>
Description
p:table supports the following attributes.
Attributes
colum ns — T he number of columns (cells) that make up a table row.
widths — T he relative widths of each column. T here 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 — T he initial number of rows considered to be header
and footer rows, which should be repeated if the table spans multiple
pages.
footerRows — T he number of rows considered to be footer rows. T his
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 — T he percentage of the page width spanned by
the table.
horizontalAlignm ent — T he horizontal alignment of the table. (See
Section 17.1.7.2, “Alignment Values” for possible values.)
skipFirstHeader
runDirection
lockedWidth
splitRows
spacingBefore — T he blank space to be inserted before the
element.
spacingAfter — T he blank space to be inserted after the element.
extendLastRow
headersInEvent
splitLate
keepT ogether
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.
horizontalAlignm ent — T he horizontal alignment of the cell. (See
Section 17.1.7.2, “Alignment Values” for possible values.)
verticalAlignm ent — T he 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, paddingT op and paddingBottom .
219
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
useBorderPadding
leading
m ultipliedLeading
indent
verticalAlignm ent
extraParagraphSpace
fixedHeight
noWrap
m inim um Height
followingIndent
rightIndent
spaceCharRatio
runDirection
arabicOptions
useAscender
grayFill
rotation
Usage
<p:cell>...</p:cell>
17.1.7. Document Constants
T his 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. T hey are: white, gray, lightgray, darkgray, black, red, pink, yellow, green,
m agenta, cyan and blue.
17.1.7.2. Alignment Values
Seam PDF supports the following horizontal alignment values: left, right, center, justify and
justifyall. T he vertical alignment values are top, m iddle, 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 HT ML page. T o use charting, you will need to add the JFreeChart library
(jfreechart.jar and jcom m on.jar) to the WEB-INF/lib directory. T hree 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 -- T he chart object to display
height -- T he height fo the chart
width -- T he width of the chart
Usage
220
Chapter 17. iText PD F generation
<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 — T he color of the border, if visible.
borderBackgroundPaint — T he default background color of the chart.
borderStroke
dom ainAxisLabel — T he text label for the domain axis.
dom ainLabelPosition — T he angle of the domain axis category labels.
Valid values are ST ANDARD, UP_4 5, UP_90, DOWN_4 5 and DOWN_90. T he
value can also be a positive or negative angle in radians.
dom ainAxisPaint — T he color of the domain axis label.
dom ainGridlinesVisible— Controls whether or not gridlines for the
domain axis are shown on the chart.
dom ainGridlinePaint— T he color of the domain gridlines, if visible.
dom ainGridlineStroke — T he stroke style of the domain gridlines, if
visible.
height — T he height of the chart.
width — T he 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.
legendItem Paint— T he default color of the text labels in the legend.
legendItem BackgoundPaint— T he background color for the legend, if
different from the chart background color.
legendOutlinePaint— T he color of the border around the legend.
orientation — T he orientation of the plot, either vertical (the default)
or horizontal.
plotBackgroundPaint — T he color of the plot background.
plotBackgroundAlpha — T he alpha (transparency) level of the plot
background. T his should be a number between 0 (completely transparent)
and 1 (completely opaque).
plotForegroundAlpha — T he alpha (transparency) level of the plot.
T his should be a number between 0 (completely transparent) and 1
(completely opaque).
plotOutlinePaint — T he color of the range gridlines, if visible.
plotOutlineStroke — T he stroke style of the range gridlines, if visible.
rangeAxisLabel — T he text label for the range axis.
rangeAxisPaint — T he color of the range axis label.
rangeGridlinesVisible — Controls whether or not gridlines for the
range axis are shown on the chart.
rangeGridlinePaint — T he color of the range gridlines, if visible.
rangeGridlineStroke — T he stroke style of the range gridlines, if
visible.
title — T he chart title text.
titlePaint — T he color of the chart title text.
titleBackgroundPaint — T he background color around the chart title.
width — T he width of the chart.
221
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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 — T he color of the border, if visible.
borderBackgroundPaint — T he default background color of the chart.
borderStroke —
dom ainAxisLabel — T he text label for the domain axis.
dom ainLabelPosition — T he angle of the domain axis category labels.
Valid values are ST ANDARD, UP_4 5, UP_90, DOWN_4 5 and DOWN_90. T he
value can also be a positive or negative angle in radians.
dom ainAxisPaint — T he color of the domain axis label.
dom ainGridlinesVisible— Controls whether or not gridlines for the
domain axis are shown on the chart.
dom ainGridlinePaint— T he color of the domain gridlines, if visible.
dom ainGridlineStroke — T he stroke style of the domain gridlines, if
visible.
height — T he height of the chart.
width — T he 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.
legendItem Paint — T he default color of the text labels in the legend.
legendItem BackgoundPaint — T he background color for the legend, if
different from the chart background color.
legendOutlinePaint — T he color of the border around the legend.
orientation — T he orientation of the plot, either vertical (the default)
or horizontal.
plotBackgroundPaint — T he color of the plot background.
plotBackgroundAlpha — T he alpha (transparency) level of the plot
background. It should be a number between 0 (completely transparent) and
1 (completely opaque).
plotForegroundAlpha — T he alpha (transparency) level of the plot. It
should be a number between 0 (completely transparent) and 1 (completely
opaque).
plotOutlinePaint — T he color of the range gridlines, if visible.
plotOutlineStroke — T he stroke style of the range gridlines, if visible.
rangeAxisLabel — T he text label for the range axis.
rangeAxisPaint — T he color of the range axis label.
rangeGridlinesVisible — Controls whether or not gridlines for the
range axis are shown on the chart.
222
Chapter 17. iText PD F generation
rangeGridlinePaint — T he color of the range gridlines, if visible.
rangeGridlineStroke — T he stroke style of the range gridlines, if
visible.
title — T he chart title text.
titlePaint — T he color of the chart title text.
titleBackgroundPaint — T he background color around the chart title.
width — T he 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 — T he chart title text.
label — T he 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 — T he link margin for labels.
labelLinkPaint — T he paint used for the label linking lines.
labelLinkStroke — T he stroke used for the label linking lines.
labelLinksVisible — A flag that controls whether or not the label links
are drawn.
labelOutlinePaint — T he paint used to draw the outline of the section
labels.
labelOutlineStroke — T he stroke used to draw the outline of the
section labels.
labelShadowPaint — T he paint used to draw the shadow for the
section labels.
labelPaint — T he color used to draw the section labels.
labelGap — T he gap between the labels and the plot, as a percentage of
the plot width.
labelBackgroundPaint — T he color used to draw the background of
the section labels. If this is null, the background is not filled.
startAngle — T he 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. T he default is true.
direction — T he direction in which the pie sections are drawn. One of:
clockwise or anticlockwise. T he default is clockwise.
sectionOutlinePaint — T he outline paint for all sections.
sectionOutlineStroke — T he outline stroke for all sections.
sectionOutlinesVisible — Indicates whether an outline is drawn for
each section in the plot.
baseSectionOutlinePaint — T he base section outline paint.
baseSectionPaint — T he base section paint.
baseSectionOutlineStroke — T he base section outline stroke.
223
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
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. T he series tag is used to
categorize a data set with a series and apply styling to the entire series.
Attributes
key — T he series name.
seriesPaint — T he color of each item in the series.
seriesOutlinePaint — T he outline color for each item in the series.
seriesOutlineStroke — T he 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
T he data tag describes each data point to be displayed in the graph.
Attributes
key — T he name of the data item.
series — T he series name, when not embedded inside a <p:series>
.
value — T he 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" />
224
Chapter 17. iText PD F generation
<p:color>
Description
T he color component declares a color or gradient for filled shapes.
Attributes
color — T he 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 — T he coordinates that mark the beginning of the gradient color.
point2 — T he 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 — T he width of the stroke.
cap — T he line cap type. Valid values are butt, round and square
join — T he line join type. Valid values are m iter, round and bevel
m iterLim it — For miter joins, this value is the limit of the size of the join.
dash — T he 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 — T he 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 iT ext to generate barcodes in a wide variety of formats. T hese 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 HT ML images.
<p:barCode>
Description
Displays a barcode image.
Attributes
type — A barcode type supported by iT ext. Valid values include:
EAN13, EAN8, UPCA, UPCE, SUPP2, SUPP5, POST NET , PLANET ,
CODE128, CODE128_UCC, CODE128_RAW and CODABAR.
code — T he 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.
barHeight — T he height of the bars in the barcode.
m inBarWidth — T he minimum bar width.
225
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
barMultiplier — T he bar multiplier for wide bars or the distance
between bars for POST NET and PLANET code.
barColor — T he color the bars should be drawn in.
textColor — T he color of any text on the barcode.
textSize — T he size of any text on the barcode.
altT ext — T he alt text for HT ML image links.
Usage
<p:barCode type="code128" barHeight="80" textSize="20"
code="(10)45566(17)040301" codeType="code128_ucc"
altText="My BarCode" />
17.4. Fill-in-forms
If you have a complex, pre-generated PDF with named fields, you can easily populate it with values from
your application and present it to the user.
<p:form >
Description
Defines a form template to populate.
Attributes
URL — A URL that points to the PDF file to use as a template. If the
value contains no protocol (://), the file is read locally.
filenam e — T he filename to use for the generated PDF file.
exportKey — If set, no redirect will occur when the generated PDF file
is placed in a DocumentData object under the specified key in the event
context.
<p:field>
Description
Connects a field name to its value.
Attributes
nam e — T he name of the field.
value — T he value of the field.
readOnly — Whether the field is read-only. T he default is true.
<p:form
xmlns:p="http://jboss.org/schema/seam/pdf"
URL="http://localhost/Concept/form.pdf">
<p:field name="person.name" value="Me, myself and I"/>
</p:form>
17.5. Rendering Swing/AWT components
Seam now provides experimental support to render Swing components into PDF images. Some Swing
look and feel supports, specifically those that use native widgets, will not render correctly.
<p:swing>
Description
Renders a Swing component into a PDF document.
Attributes
width — T he width of the component to be rendered.
226
Chapter 17. iText PD F generation
height — T he height of the component to be rendered.
com ponent — An expression whose value is a Swing or AWT
component.
Usage
<p:swing width="310" height="120" component="#{aButton}" />
17.6. Configuring iText
Document generation works out of the box with no additional configuration needed. However, there are a
few points of configuration that are needed for more serious applications.
T he default implementation serves PDF documents from a generic URL, /seam -doc.seam . Many
browsers (and users) would prefer to see URLs that contain the actual PDF name like
/m yDocum ent.pdf. T his capability requires some configuration. T o serve PDF files, all * .pdf
resources should be mapped to the DocumentStoreServlet:
<servlet>
<servlet-name>Document Store Servlet</servlet-name>
<servlet-class>org.jboss.seam.document.DocumentStoreServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Document Store Servlet</servlet-name>
<url-pattern>*.pdf</url-pattern>
</servlet-mapping>
T he use-extensions option on the document store component completes the functionality by
instructing the document store to generate URLs with the correct filename extension for the document
type being generated.
<components xmlns="http://jboss.org/schema/seam/components"
xmlns:document="http://jboss.org/schema/seam/document"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://jboss.org/schema/seam/document http://jboss.org/schema/seam/document-2.3.xsd
http://jboss.org/schema/seam/components http://jboss.org/schema/seam/components2.3.xsd">
<document:document-store use-extensions="true"/>
</components>
T he document store stores documents in conversation scope, and documents will expire when the
conversation ends. At that point, references to the document will be invalid. You can specify a default
view to be shown when a document does not exist using the error-page property of the
docum entStore.
<document:document-store use-extensions="true" error-page="/documentMissing.seam"
/>
17.7. Further documentation
For further information on iT ext, see:
iT ext Home Page
iT ext in Action
227
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Chapter 18. The Microsoft® Excel® spreadsheet application
Seam can generate Microsoft® Excel® spreadsheets through the JExcelAPI library. T he document
generated is compatible with Microsoft Excel versions 95, 97, 2000, XP, and 2003. At present, only a
limited subset of the library functionality is available. Refer to the JExcelAPI documentation for more
information about limitations and capabilities.
18.1. Microsoft Excel support
T o include Microsoft Excel support in your application, you must include jboss-seam -excel.jar
and jxl.jar in your WEB-INF/lib directory. jboss-seam -excel.jar contains the Microsoft
Excel JSF controls used to construct views for document rendering, and the DocumentStore component,
which serves the rendered document to the user. You will also need to configure the DocumentStore
Servlet in your web.xm l file. T he Microsoft Excel Seam module requires the seam -ui package, and
that Facelets be used as the view technology.
You can see an example of Microsoft Excel support in action in the exam ples/excel project. T his
demonstrates the exposed functionality of the support, as well as correct deployment packaging.
You can easily customize the module to support other kinds of Microsoft Excel spreadsheet.
Implement the ExcelWorkbook interface and register the following in com ponents.xm l:
<excel:excelFactory>
<property name="implementations">
<key>myExcelExporter</key>
<value>my.excel.exporter.ExcelExport</value>
</property>
</excel:excelFactory>
Register the Microsoft Excel namespace in the components tag like so:
xmlns:excel="http://jboss.org/schema/seam/excel"
T hen set the UIWorkbook type to m yExcelExporter to use your own preferred exporter. T he default
here is jxl, but you can also use CSV with the csv type.
See Section 17.6, “Configuring iT ext” for information about how to configure the document servlet for
serving documents with an .xls extension.
If you have trouble accessing the generated file under Microsoft® Internet Explorer® , especially with
HT T PS, check that your web.xm l or browser security constraints are not too strict.
18.2. Creating a simple workbook
T he worksheet support is used like a <h:dataT able>
, and can be bound to a List, Set, Map, Array or DataModel.
<e:workbook xmlns:e="http://jboss.org/schema/seam/excel">
<e:worksheet>
<e:cell column="0" row="0" value="Hello world!"/>
</e:worksheet>
</e:workbook>
T he following is a more common use case:
228
Chapter 18. The Microsoft® Excel® spreadsheet application
<e:workbook xmlns:e="http://jboss.org/schema/seam/excel">
<e:worksheet value="#{data}" var="item">
<e:column>
<e:cell value="#{item.value}"/>
</e:column>
</e:worksheet>
</e:workbook>
T he top-level workbook element serves as the container, and has no attributes. T he child element,
worksheet, has two attributes: value="#{data}" is the EL-binding to the data, and var="item " is
the name of the current item. T he worksheet contains a single colum n. Within this is the cell, which is
the final bind to the data in the currently iterated item.
Now you can bind your data into spreadsheets.
18.3. Workbooks
Workbooks are the top-level parents of worksheets and stylesheet links.
<e:workbook>
Attributes
type — Defines the export model. T he value is a string and can be
either jxl or csv. T he default is jxl.
tem plateURI — A template that forms the basis of the workbook. T he
value is a string (URI).
arrayGrowSize — T he amount of memory (in bytes) by which the
workbook data storage space should be increased. If your process
reads many small workbooks inside a web application server, you may
need to reduce the default size. T he default value is 1 MB.
autoFilterDisabled — A Boolean value determining whether
autofiltering is disabled.
cellValidationDisabled — A Boolean value determining whether
cell validation is ignored.
characterSet — T he character set used to read the spreadsheet.
Has no effect on the spreadsheet being written. T he value is a string
(character set encoding).
drawingsDisabled — A Boolean value determining whether drawings
are disabled.
excelDisplayLanguage — T he language that the generated file will
display in. T he value is a string (two character ISO 3166 country code).
excelRegionalSettings — T he regional settings for the generated
file. T he value is a string (two character ISO 3166 country code).
form ulaAdjust — A Boolean determining whether formulas are
adjusted.
gcDisabled — A Boolean determining whether garbage collection is
disabled.
ignoreBlanks — A Boolean value determining whether blanks are
ignored.
initialFileSize — T he initial amount of memory (in bytes)
allocated to workbook data storage when reading a worksheet. If your
process reads many small workbooks inside a web application server,
you may need to reduce the default size. T he default value is 5 MB.
locale — T he locale JExcelAPI uses to generate the spreadsheet.
T his value has no effect on the language or region of the generated file.
T he value is a string.
m ergedCellCheckingDisabled — A Boolean determining whether
merged cell checking is disabled.
nam esDisabled — A Boolean determining whether name handling is
229
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
disabled.
propertySets — A Boolean determining whether property sets (such
as macros) are copied with the workbook. If this feature is enabled, the
JXL process will use more memory.
rationalization — A Boolean determining whether cell formats are
rationalized before the sheet is written. Defaults to true.
supressWarnings — A Boolean determining whether warnings are
suppressed. Depending on the type of logger used, this sets the
warning behavior across the JVM.
tem poraryFileDuringWriteDirectory — A string value
containing the target directory for temporary files. Used in conjunction
with useT em poraryFileDuringWrite. If set to NULL, the default
temporary directory is used instead.
useT em poraryFileDuringWrite — A Boolean determining whether
a temporary file is used during workbook generation. If not set, the
workbook will be generated entirely in memory. Setting this flag involves
an assessment of the trade-offs between memory usage and
performance.
workbookProtected — A Boolean determining whether the workbook
is protected.
filenam e — A string value to be used as the download's filename. If
you map the DocumentServlet to some pattern, its file extension must
match.
exportKey — A key to store event-scoped data in a DocumentData
object. If used, there is no redirection.
Child elements
<e:link/>
— Z ero or more stylesheet links. (See Section 18.14.1, “Stylesheet
links”.)
<e:worksheet/>
— Z ero or more worksheets. (See Section 18.4, “Worksheets”.)
Facets
none
<e:workbook>
<e:worksheet>
<e:cell value="Hello World" row="0" column="0"/>
</e:worksheet>
<e:workbook>
T his defines a workbook with a worksheet and a greeting at cell A1.
18.4. Worksheets
Worksheets are the children of workbooks and the parent of columns and worksheet commands. T hey
can also contain explicitly placed cells, formulas, images and hyperlinks. T hey are the pages that make
up the workbook.
<e:worksheet>
230
value — An EL-expression string to the backing data. T he target of this
expression is examined for an Iterable. If the target is a Map, the
iteration is done over the Map.Entry entrySet(), so use a .key or .value to
target in your references.
var — T he current row iterator variable name to be referenced in cell
value attributes. T he value is a string.
Chapter 18. The Microsoft® Excel® spreadsheet application
nam e — T he name of the worksheet. T he value is a string. Defaults to
Sheet<replaceable>#</replaceable>
where # is the worksheet index. If the given worksheet name exists, that
sheet is selected. T his can be used to merge several data sets into a
single worksheet by defining each worksheet with the same name —
use startRow and startCol to make sure they do not occupy the
same space.
startRow — A number value that defines the starting row for the data.
T his is used to position data from places other than the upper-left
corner. T his is particularly useful when using multiple data sets for a
single worksheet. T he default value is 0.
startColum n — A number value that defines the starting column for
the data. T his is used to position data from places other than the upperleft corner. T his is particularly useful when using multiple data sets for a
single worksheet. T he default value is 0.
autom aticForm ulaCalculation — A Boolean determining whether
formulas are calculated automatically.
bottom Margin — A number value determining the bottom margin in
inches.
copies — A number value determining the number of copies.
defaultColum nWidth — A number value defining the default column
width, in characters * 256.
defaultRowHeight — A number value defining the default row height,
in 1/20ths of a point.
displayZeroValues — A Boolean determining whether zero values
are displayed.
fitHeight — A number value defining the number of pages vertically
that this sheet will print into.
fitT oPages — A Boolean determining whether printing fits to pages.
fitWidth — A number value defining the number of pages across that
this sheet will print into.
footerMargin — A number value defining the margin for any page
footer in inches.
headerMargin — A number value defining the margin for any page
header in inches.
hidden — A Boolean determining whether the worksheet is hidden.
horizontalCentre — A Boolean determining whether the worksheet
is centred horizontally.
horizontalFreeze — A number value defining the column at which
the pane is frozen horizontally.
horizontalPrintResolution — A number value defining the
horizontal print resolution.
leftMargin — A number value defining the left margin in inches.
norm alMagnification — A number value defining the normal
magnification factor as a percentage. T his is not the zoom or scale
factor.
orientation — A string value that determines the paper orientation
when this sheet is printed. Can be either landscape or portrait.
pageBreakPreviewMagnification — A number value defining the
page break preview magnification factor as a percentage.
pageBreakPreviewMode — A Boolean determining whether the page
is shown in preview mode.
pageStart — A number value defining the page number at which to
commence printing.
paperSize — A string value determining the paper size to be used
when printing. Possible values are a4 , a3, letter, legal, etc.
password — A string value determining the password for this sheet.
passwordHash — A string value determining the password hash. T his
231
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
is used only when copying sheets.
printGridLines — A Boolean determining whether grid lines are
printed.
printHeaders — A Boolean determining whether headers are printed.
sheetProtected — A Boolean determining whether the sheet is readonly.
recalculateForm ulasBeforeSave — A Boolean determining
whether formulas are recalculated when the sheet is saved. T he default
value is false.
rightMargin — A number value defining the right margin in inches.
scaleFactor — A number value defining the scale factor (as a
percentage) used when this sheet is printed.
selected — A Boolean value determining whether the sheet is
selected automatically when the workbook opens.
showGridLines — A Boolean determining whether grid lines are
shown.
topMargin — A number value defining the top margin in inches.
verticalCentre — A Boolean determining whether the sheet is
vertically centred.
verticalFreeze — A number value determining the row at which the
pane is frozen vertically.
verticalPrintResolution — A number value determining the
vertical print resolution.
zoom Factor — A number value determining the zoom factor. T his
relates to on-screen view, and should not be confused with the scale
factor.
Child elements
<e:printArea/>
— Z ero or more print area definitions. (See Section 18.11, “Print areas
and titles”.)
<e:printT itle/>
— Z ero or more print title definitions. (See Section 18.11, “Print areas
and titles”.)
<e:headerFooter/>
— Z ero or more header/footer definitions. (See Section 18.10, “Headers
and footers”.)
Z ero or more worksheet commands. (See Section 18.12, “Worksheet
Commands”.)
Facets
header— Contents placed at the top of the data block, above the
column headers (if any).
footer— Contents placed at the bottom of the data block, below the
column footers (if any).
<e:workbook>
<e:worksheet name="foo" startColumn="1" startRow="1">
<e:column value="#{personList}" var="person">
<f:facet name="header">
<e:cell value="Last name"/>
</f:facet>
<e:cell value="#{person.lastName}"/>
</e:column>
</e:worksheet>
<e:workbook>
232
Chapter 18. The Microsoft® Excel® spreadsheet application
T his defines a worksheet with the name "foo", starting at B2.
18.5. Columns
Columns are the children of worksheets and the parents of cells, images, formulas and hyperlinks. T hey
control the iteration of the worksheet data. See Section 18.14.5, “Column settings” for formatting.
<e:colum n>
Attributes
none
Child elements
<e:cell/>
— Z ero or more cells. (See Section 18.6, “Cells”.)
<e:form ula/>
— Z ero or more formulas. (See Section 18.7, “Formulas”.)
<e:im age/>
— Z ero or more images. (See Section 18.8, “Images”.)
<e:hyperLink/>
— Z ero or more hyperlinks (see Section 18.9, “Hyperlinks” ).
Facets
header — T his facet can/will contain one <e:cell>
, <e:form ula>
, <e:im age>
or <e:hyperLink>
, which will be used as header for the column.
footer — T his facet can/will contain one <e:cell>
, <e:form ula>
, <e:im age>
or <e:hyperLink>
, which will be used as footer for the column.
<e:workbook>
<e:worksheet>
<e:column value="#{personList}" var="person">
<f:facet name="header">
<e:cell value="Last name"/>
</f:facet>
<e:cell value="#{person.lastName}"/>
</e:column>
</e:worksheet>
<e:workbook>
T his defines a column with a header and an iterated output.
18.6. Cells
Cells are nested within columns (for iteration) or inside worksheets (for direct placement using the
colum n and row attributes) and are responsible for outputting the value, usually through an ELexpression involving the var attribute of the datatable. See Section 18.14.6, “Cell settings”.
<e:cell>
Attributes
colum n — A number value denoting the column that the cell belongs to.
T he default is the internal counter. Note that the value is 0-based.
row — A number value denoting the row where to place the cell. T he
233
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
default is the internal counter. Note that the value is 0-based.
value — A string defining the display value. Usually an EL-expression
referencing the var-attribute of the containing datatable.
com m ent — A string value defining a comment attached to the cell.
com m entHeight — T he comment height in pixels.
com m entWidth — T he comment width in pixels.
Child elements
Z ero or more validation conditions. (See Section 18.6.1, “Validation”.)
Facets
none
<e:workbook>
<e:worksheet>
<e:column value="#{personList}" var="person">
<f:facet name="header">
<e:cell value="Last name"/>
</f:facet>
<e:cell value="#{person.lastName}"/>
</e:column>
</e:worksheet>
</e:workbook>
T his defines a column with a header and an iterated output.
18.6.1. Validation
Validations are nested inside cells or formulas. T hey add constraints to cell data.
<e:num ericValidat
ion>
Attributes
value — A number value denoting the limit (or lower limit where
applicable) of the validation.
value2 — A number value denoting the upper limit (where applicable)
of the validation.
condition — A string value defining the validation condition.
equal — requires the cell value to match the one defined in the
value-attribute.
greater_equal — requires the cell value to be greater than or
equal to the value defined in the value-attribute.
less_equal — requires the cell value to be less than or equal to
the value defined in the value-attribute.
less_than — requires the cell value to be less than the value
defined in the value-attribute.
not_equal — requires the cell value to not match the one defined
in the value-attribute.
between — requires the cell value to be between the values defined
in the value and value2 attributes.
not_between — requires the cell value not to be between the
values defined in the value- and value2 attributes.
Child elements
none
Facets
none
234
Chapter 18. The Microsoft® Excel® spreadsheet application
<e:workbook>
<e:worksheet>
<e:column value="#{personList}" var="person">
<e:cell value="#{person.age">
<e:numericValidation condition="between" value="4" value2="18"/>
</e:cell>
</e:column>
</e:worksheet>
</e:workbook>
T his adds numeric validation to a cell, specifying that the value must be between 4 and 18.
<e:rangeValidatio
n>
Attributes
startColum n — A number value denoting the first column to validate
against.
startRow — A number value denoting the first row to validate against.
endColum n — A number value denoting the last column to validate
against.
endRow — A number value denoting the last row to validate against.
Child elements
none
Facets
none
<e:workbook>
<e:worksheet>
<e:column value="#{personList}" var="person">
<e:cell value="#{person.position">
<e:rangeValidation startColumn="0" startRow="0" endColumn="0"
endRow="10"/>
</e:cell>
</e:column>
</e:worksheet>
</e:workbook>
T his adds validation to a cell, specifying that the value must exist within the values specified in range
A1:A10.
<e:listValidation
>
Attributes
none
Child elements
Zero or m ore list validation item s.
Facets
none
e:listValidation is a just a container for holding multiple e:listValidationItem tags.
<e:listValidation
Item >
Attributes
235
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Item >
value — A value to validate against.
Child elements
none
Facets
none
<e:workbook>
<e:worksheet>
<e:column value="#{personList}" var="person">
<e:cell value="#{person.position">
<e:listValidation>
<e:listValidationItem value="manager"/>
<e:listValidationItem value="employee"/>
</e:listValidation>
</e:cell>
</e:column>
</e:worksheet>
</e:workbook>
T his adds validation to a cell, specifying that the value must be "manager" or "employee".
18.6.2. Format masks
Format masks are defined in the m ask attribute in a cell or formula. T here are two types of format
masks: one for numbers, and one for dates.
18.6.2.1. Number masks
When a format mask is encountered, a check is executed to see if the mask follows an internal form, for
example, form at1, accounting_float, etc.
If the mask is not part of the internal list, it is treated as a custom mask (for example, 0.00), and
automatically converted to the closest match.
18.6.2.2. Date masks
When a format mask is encountered, a check is executed to see if the mask follows an internal form, for
example, form at1, form at2, etc.
If the mask is not part of the internal list, it is treated as a custom mask (for example, dd.MM.yyyy), and
automatically converted to the closest match.
18.7. Formulas
Formulas are nested within columns (for iteration) or inside worksheets (for direct placement using the
colum n and row attributes), and add calculations or functions to ranges of cells. T hey are essentially
cells, see Section 18.6, “Cells” for available attributes. T hey can apply templates and have their own font
definitions, etc., just as normal cells can.
T he formula of the cell is placed in the value attribute as a normal Microsoft Excel notation. When
doing cross-sheet formulas, the worksheets must exist before referencing a formula against them. T he
value is a string.
236
Chapter 18. The Microsoft® Excel® spreadsheet application
<e:workbook>
<e:worksheet name="fooSheet">
<e:cell column="0" row="0" value="1"/>
</e:worksheet>
<e:worksheet name="barSheet">
<e:cell column="0" row="0" value="2"/>
<e:formula column="0" row="1" value="fooSheet!A1+barSheet1!A1">
<e:font fontSize="12"/>
</e:formula>
</e:worksheet>
</e:workbook>
T his defines a formula in B2 summing cells A1 in worksheets FooSheet and BarSheet.
18.8. Images
Images are nested within columns (for iteration) or inside worksheets (for direct placement using the
startColum n/startRow and rowSpan/colum nSpan attributes). Span tags are optional, and the
image will be inserted without resizing if they are omitted.
<e:im age>
Attributes
startColum n — A number value denoting the column in which the
image starts. T he default is the internal counter. T he number value is 0based.
startRow — A number value denoting the row in which the image
starts. T he default is the internal counter. T he number value is 0-based.
colum nSpan — A float value denoting the column span of the image.
T he default uses the default width of the image.
rowSpan — A float value denoting the row span of the image. T he
default uses the default height of the image.
URI — A string value denoting the URI to the image.
Child elements
none
Facets
none
<e:workbook>
<e:worksheet>
<e:image startRow="0" startColumn="0" rowSpan="4" columnSpan="4"
URI="http://foo.org/logo.jpg"/>
</e:worksheet>
</e:workbook>
T his defines an image in A1:E5 based on the given data.
18.9. Hyperlinks
Hyperlinks are nested within columns (for iteration) or inside worksheets (for direct placement using the
startColum n/startRow and endColum n/endRow attributes). T hey add link navigation to URIs.
<e:hyperlink>
Attributes
startColum n — A number value denoting the column in which the
hyperlink starts. T he default is the internal counter. T he number value is
237
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
0-based.
startRow — A number value denoting the row in which the hyperlink
starts. T he default is the internal counter. T he number value is 0-based.
endColum n — A number value denoting the column in which the
hyperlink ends. T he default is the internal counter. T he number value is
0-based.
endRow — A number value denoting the row in which the hyperlink
ends. T he default is the internal counter. T he number value is 0-based.
URL — A string value denoting the URL to link.
description — A string value describing the link. string.
Child elements
none
Facets
none
<e:workbook>
<e:worksheet>
<e:hyperLink startRow="0" startColumn="0" endRow="4" endColumn="4"
URL="http://seamframework.org" description="The Seam Framework"/>
</e:worksheet>
</e:workbook>
T his defines a described hyperlink pointing to Seam Framework in the area A1:E5.
18.10. Headers and footers
Headers and footers are children of worksheets, and contain facets, which contain strings to be parsed
as commands.
<e:header>
Attributes
none
Child elements
none
Facets
left — T he contents of the left header part.
center — T he contents of the central header part.
right — T he contents of the right header part.
<e:footer>
Attributes
none
Child elements
none
Facets
left — T he contents of the left footer part.
center — T he contents of the central footer part.
right — T he contents of the right footer part.
238
Chapter 18. The Microsoft® Excel® spreadsheet application
Because facets contain string values, they can contain various #-delimited commands, like the following:
#date#
Inserts the current date.
#page_number#
Inserts the current page number.
#time#
Inserts the current time.
#total_pages#
Inserts the total page count.
#worksheet_name#
Inserts the worksheet name.
#workbook_name#
Inserts the workbook name.
#bold#
T oggles bold font. One use turns bold text on; a second use turns bold text
off.
#italics#
T oggles italic font. One use turns italic text on; a second use turns italic text
off.
#underline#
T oggles underlined font. One use turns underlined text on; a second use
turns underlined text off.
#double_underline#
T oggles double-underlined font. One use turns double-underlying text on; a
second use turns double-underlined text off.
#outline#
T oggles outlined font. One use turns outlined text on; a second use turns
outlined text off.
#shadow#
T oggles shadowed font. One use turns shadowed text on; a second use
turns shadowed text off.
#strikethrough#
T oggles struck-through font. One use turns struck-through text on; a
second use turns struck-through text off.
#subscript#
T oggles subscript font. One use turns subscript text on; a second use
turns subscript text off.
#superscript#
T oggles superscript font. One use turns superscript text on; a second use
turns superscript text off.
#font_name#
Sets font name. T o set Verdana as the font, use #font_nam e=Verdana#.
#font_size#
Sets font size. T o set 12 as the font size, use #font_size=12#.
<e:workbook>
<e:worksheet>
<e:header>
<f:facet name="left">
This document was made on #date# and has #total_pages# pages.
</f:facet>
<f:facet name="right"> #time# </f:facet>
</e:header>
<e:worksheet>
</e:workbook>
18.11. Print areas and titles
Print areas and titles are the children of worksheets and worksheet templates, and provide print areas
and titles.
<e:printArea>
Attributes
firstColum n — A number value denoting column that holds the topleft corner of the area. T he value is 0-based.
firstRow — A number value denoting the row that holds the top left
corner of the area.T he value is 0-based.
lastColum n — A number value denoting the column that holds the
bottom-right corner of the area. T he value is 0-based.
lastRow — A number value denoting the row that holds the bottomright corner of the area. T he value is 0-based.
239
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Child elements
none
Facets
none
<e:workbook>
<e:worksheet>
<e:printTitles firstRow="0" firstColumn="0" lastRow="0"
lastColumn="9"/>
<e:printArea firstRow="1" firstColumn="0" lastRow="9" lastColumn="9"/>
</e:worksheet>
</e:workbook>
T his defines a print title between A1:A10 and a print area between B2:J10.
18.12. Worksheet Commands
Worksheet commands are the children of workbooks and are usually executed only once.
18.12.1. Grouping
Provides grouping of columns and rows.
<e:groupRows>
Attributes
startRow — A number value denoting the row at which to begin the
grouping. T he value is 0-based.
endRow — A number value denoting the row at which to end the
grouping. T he value is 0-based.
collapse — A Boolean determining whether the grouping is initially
collapsed.
Child elements
none
Facets
none
<e:groupColum ns>
Attributes
startColum n — A number value denoting the column at which to begin
the grouping. T he value is 0-based.
endColum n — A number value denoting the column at which to end the
grouping. T he value is 0-based.
collapse — A Boolean determining whether the grouping is initially
collapsed.
Child elements
none
Facets
none
24 0
Chapter 18. The Microsoft® Excel® spreadsheet application
<e:workbook>
<e:worksheet>
<e:groupRows startRow="4" endRow="9" collapse="true"/>
<e:groupColumns startColumn="0" endColumn="9" collapse="false"/>
</e:worksheet>
</e:workbook>
T his groups rows 5 through 10 and columns 5 through 10 so that the rows are initially collapsed (but
not the columns).
18.12.2. Page breaks
Provides page breaks
<e:rowPageBreak>
Attributes
row — A number value denoting the row at which a page break should
occur. T he value is 0-based.
Child elements
none
Facets
none
<e:workbook>
<e:worksheet>
<e:rowPageBreak row="4"/>
</e:worksheet>
</e:workbook>
T his causes a page break at row 5.
18.12.3. Merging
Provides cell merging
<e:m ergeCells>
Attributes
startRow — A number value denoting the row at which to begin the
merge. T he value is 0-based.
startColum n — A number value denoting the column at which to begin
the merge. T he value is 0-based.
endRow — A number value denoting the row at which to end the merge.
T he value is 0-based.
endColum n — A number value denoting the column at which to end the
merge. T he value is 0-based.
Child elements
none
Facets
none
24 1
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<e:workbook>
<e:worksheet>
<e:mergeCells startRow="0" startColumn="0" endRow="9" endColumn="9"/>
</e:worksheet>
</e:workbook>
T his merges the cells in the range A1:J10.
18.13. Datatable exporter
If you prefer to export an existing JSF datatable instead of writing a dedicated XHT ML document, you can
execute the org.jboss.seam .excel.excelExporter.export component, passing in the ID of the
datatable as an Seam EL parameter. For example, say you have the following datatable:
<h:form id="theForm">
<h:dataTable id="theDataTable" value="#{personList.personList}"
var="person">
...
</h:dataTable>
</h:form>
If you want to view this as a Microsoft Excel spreadsheet, place the following in the form:
<h:commandLink value="Export"
action="#{excelExporter.export('theForm:theDataTable')}" />
You can also execute the exporter with a button, s:link, or other preferred method.
See Section 18.14, “Fonts and layout” for formatting.
18.14. Fonts and layout
Output appearance is controlled with a combination of CSS style and tag attributes. CSS style attributes
flow from parent to child, and let you use one tag to apply all attributes defined for that tag in the
styleClass and style sheets.
If you have format masks or fonts that use special characters, such as spaces and semicolons, you can
escape the CSS string with '' characters such as xls-form at-m ask:'$;$'.
18.14.1. Stylesheet links
External stylesheets are referenced with the e:link tag. T hey are placed within the document as if they
are children of the workbook tag.
<e:link>
Attributes
URL — T he URL of the stylesheet.
Child elements
none
Facets
none
24 2
Chapter 18. The Microsoft® Excel® spreadsheet application
<e:workbook>
<e:link URL="/css/excel.css"/>
</e:workbook>
T his references a stylesheet located at /css/excel.css.
18.14.2. Fonts
T his group of XLS-CSS attributes define a font and its attributes.
xls-font-family
T he name of the font. Make sure the font you enter here is supported by
your system.
xls-font-size
A plain number value denoting the font size.
xls-font-color
T he color of the font.
xls-font-bold
A Boolean determining whether the font is bold. Valid values are true and
false.
xls-font-italic
A Boolean determining whether the font is italicized. Valid values are true
and false.
xls-font-script-style
T he script style of the font.
xls-font-underline-style
T he underline style of the font.
xls-font-struck-out
A Boolean determining whether the font is struck-through. Valid values are
true and false.
xls-font
A shorthand notation for setting all values associated with font. Place the
font name last. (If you wish to use a font with spaces in its name, use tick
marks to surround the font. For example, 'T im es New Rom an'.) Here,
defined italicized, bold, or struck-through text with italic, bold, or
struckout.
For example: style="xls-font: red bold italic 22 Verdana"
18.14.3. Borders
T his group of XLS-CSS attributes defines the borders of the cell.
xls-border-left-color
T he border color of the left edge of the cell.
xls-border-left-line-style
T he border line style of the left edge of the cell.
xls-border-left
A shorthand notation for setting the line style and color of the left edge of
the cell. Use like so: style="xls-border-left: thick red"
xls-border-top-color
T he border color of the top edge of the cell.
xls-border-top-linestyle
T he border line style of the top edge of the cell.
xls-border-top
A shorthand notation for setting the line style and color of the top edge of
the cell. Use like so: style="xls-border-top: red thick"
xls-border-right-color
T he border color of the right edge of the cell.
xls-border-right-linestyle
T he border line style of the right edge of the cell.
xls-border-right
A shorthand notation for setting the line style and color of the right edge of
the cell. Use like so: style="xls-border-right: thick red"
xls-border-bottom-color
T he border color of the bottom edge of the cell.
xls-border-bottom-linestyle
T he border line style of the bottom edge of the cell.
xls-border-bottom
A shorthand notation for setting the line style and color of the bottom edge
of the cell. Use like so: style="xls-border-bottom : thick red"
xls-border
A shorthand notation for setting the line style and color for all edges of the
24 3
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
cell. Use like so: style="xls-border: thick red"
18.14.4. Background
T his group of XLS-CSS attributes defines the background of the cell.
xls-background-color
T he color of the background.
xls-background-pattern
T he pattern of the background.
xls-background
A shorthand for setting the background color and pattern.
18.14.5. Column settings
T his group of XLS-CSS attributes defines column properties.
xls-column-width
T he width of a column. We recommend beginning with values of
approximately 5000, and adjusting as required. Used by the e:colum n in
XHT ML mode.
xls-column-widths
T he width of each column, respectively. We recommend beginning with
values of approximately 5000, and adjusting as required. Used by the excel
exporter, and placed in the datatable style attribute. Use numerical values,
or * to bypass a column.
For example: style="xls-colum n-widths: 5000, 5000, * ,
10000"
xls-column-autosize
Determines whether the column should be autosized. Valid values are true
and false.
xls-column-hidden
Determines whether the column is hidden. Valid values are true and
false.
xls-column-export
Determines whether the column is shown in export. Valid values are true
and false. Defaults to true.
18.14.6. Cell settings
T his group of XLS-CSS attributes defines the cell properties.
xls-alignment
T he alignment of the cell value.
xls-force-type
A string value determining the forced type of data in the cell. Valid values
are general, num ber, text, date, form ula, and bool. T he type is
automatically detected so there is rarely any use for this attribute.
xls-format-mask
T he format mask of the cell. (See Section 18.6.2, “Format masks”.)
xls-indentation
A number value determining the indentation of the cell's contents.
xls-locked
Determines whether a cell is locked. Used with workbook level locked.
Valid values are true or false.
xls-orientation
T he orientation of the cell value.
xls-vertical-alignment
T he vertical alignment of the cell value.
xls-shrink-to-fit
Determines whether cell values shrink to fit. Valid values are true and
false.
xls-wrap
Determines whether the cell wraps new lines. Valid values are true and
false.
18.14.7. The datatable exporter
T he datatable exporter uses the same XLS-CSS attributes as the XHT ML document, with the exception
that column widths are defined with the xls-colum n-widths attribute on the datatable (since the
UIColumn doesn't support the style or styleClass attributes).
18.14.8. Limitations
24 4
Chapter 18. The Microsoft® Excel® spreadsheet application
T here are some known limitations to CSS support in the current version of Seam.
When using .xhtm l documents, stylesheets must be referenced through the <e:link>
tag.
When using the datatable exporter, CSS must be entered through style-attributes — external
stylesheets are not supported.
18.15. Internationalization
Only two resource bundle keys are used. Both are for invalid data formats, and both take a parameter
that defines the invalid value.
org.jboss.seam .excel.not_a_num ber — When a value thought to be a number could not be
treated as such.
org.jboss.seam .excel.not_a_date — When a value thought to be a date could not be treated
as such.
18.16. Links and further documentation
T he core of Microsoft Excel functionality in Seam is based on the JExcelAPI library, which can be found
on http://jexcelapi.sourceforge.net/. Most features and limitations are inherited from the JExcelAPI.
Note
JExcelAPI is not Seam. Any Seam-based issues are best reported in the JBoss Seam JIRA under
the Excel module.
24 5
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Chapter 19. Email
Seam now includes an optional component for templating and sending email.
Email support is provided by jboss-seam -m ail.jar. T his JAR contains the mail JSF controls, used
to construct emails, and the m ailSession manager component.
For a demonstration of the email support available in Seam, see the exam ples/m ail project. T his
demonstrates proper packaging, and contains a number of currently-supported key features.
19.1. Creating a message
Seam uses Facelets to template emails.
<m:message xmlns="http://www.w3.org/1999/xhtml"
xmlns:m="http://jboss.org/schema/seam/mail"
xmlns:h="http://java.sun.com/jsf/html">
<m:from name="Peter" address="[email protected]" />
<m:to name="#{person.firstname} #{person.lastname}">
#{person.address}
</m:to>
<m:subject>Try out Seam!</m:subject>
<m:body>
<p><h:outputText value="Dear #{person.firstname}" />,</p>
<p>You can try out Seam by visiting
<a href="http://labs.jboss.com/jbossseam">
http://labs.jboss.com/jbossseam
</a>.
</p>
<p>Regards,</p>
<p>Pete</p>
</m:body>
</m:message>
T he <m :m essage>
tag wraps the whole message, and tells Seam to start rendering an email. Inside the <m :m essage>
tag, we use an <m :from >
tag to specify the sender, a <m :to>
tag to specify a recipient, and a <m :subject>
tag. (Note that EL is used as it would be in a normal Facelet.)
T he <m :body>
tag wraps the body of the email. You can use regular HT ML tags inside the body, as well as JSF
components.
Once the m :m essage is rendered, the m ailSession is called to send the email. T o send your email,
have Seam render the view:
@In(create=true)
private Renderer renderer;
public void send() {
try {
renderer.render("/simple.xhtml");
facesMessages.add("Email sent successfully");
} catch (Exception e) {
facesMessages.add("Email sending failed: " + e.getMessage());
}
}
24 6
Chapter 19. Email
If, for example, you entered an invalid email address, then an exception is thrown, caught and then
displayed to the user.
19.1.1. Attachments
Seam supports most standard Java types when working with files, so it is easy to attach files to an
email.
For example, to email the jboss-seam -m ail.jar:
<m:attachment value="/WEB-INF/lib/jboss-seam-mail.jar"/>
Seam loads the file from the classpath and attaches it to the email. By default, this file is attached as
jboss-seam -m ail.jar, but you can change the attachment name by adding and editing the
fileNam e attribute:
<m:attachment value="/WEB-INF/lib/jboss-seam-mail.jar"
fileName="this-is-so-cool.jar"/>
You can also attach a java.io.File, a java.net.URL:
<m:attachment value="#{numbers}"/>
Or a byte[] or a java.io.InputStream :
<m:attachment value="#{person.photo}" contentType="image/png"/>
For byte[] and java.io.InputStream , you will need to specify the MIME type of the attachment,
since this information is not carried as part of the file.
You can attach a Seam-generated PDF, or any standard JSF view, by wrapping a <m :attachm ent>
tag around your normal tags:
<m:attachment fileName="tiny.pdf">
<p:document>
A very tiny PDF
</p:document>
</m:attachment>
T o attach a set of files — for example, a set of pictures loaded from a database — you can use a
<ui:repeat>
:
<ui:repeat value="#{people}" var="person">
<m:attachment value="#{person.photo}" contentType="image/jpeg"
fileName="#{person.firstname}_#{person.lastname}.jpg"/>
</ui:repeat>
T o display an attached image inline:
<m:attachment value="#{person.photo}" contentType="image/jpeg"
fileName="#{person.firstname}_#{person.lastname}.jpg"
status="personPhoto" disposition="inline" />
<img src="cid:#{personPhoto.contentId}" />
T he cid:#{...} tag specifies that the attachments will be examined when attempting to locate the
image. T he cid — Content-ID — must match.
You must declare the attachment before trying to access the status object.
19.1.2. HTML/Text alternative part
24 7
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Although most mail readers support HT ML, some do not. You can add a plain text alternative to your
email body:
<m:body>
<f:facet name="alternative">
Sorry, your email reader can not show our fancy email. Please go to
http://labs.jboss.com/jbossseam to explore Seam.
</f:facet>
</m:body>
19.1.3. Multiple recipients
Often you will want to send an email to a group of recipients, such as your users. All recipient mail tags
can be placed inside a <ui:repeat>
:
<ui:repeat value="#{allUsers} var="user">
<m:to name="#{user.firstname} #{user.lastname}"
address="#{user.emailAddress}"/>
</ui:repeat>
19.1.4. Multiple messages
Sometimes — for example, during a password reset — you will need to send a slightly different message
to each recipient. T he best way to do this is to place the whole message inside a <ui:repeat>
:
<ui:repeat value="#{people}" var="p">
<m:message>
<m:from name="#{person.firstname} #{person.lastname}">
#{person.address}
</m:from>
<m:to name="#{p.firstname}">#{p.address}</m:to>
...
</m:message>
</ui:repeat>
19.1.5. Templating
T he mail templating example shows that Facelets templating works with the Seam mail tags.
Our tem plate.xhtm l contains:
<m:message>
<m:from name="Seam" address="[email protected]" />
<m:to name="#{person.firstname} #{person.lastname}">
#{person.address}
</m:to>
<m:subject>#{subject}</m:subject>
<m:body>
<html>
<body>
<ui:insert name="body">
This is the default body, specified by the template.
</ui:insert>
</body>
</html>
</m:body>
</m:message>
Our tem plating.xhtm l contains:
24 8
Chapter 19. Email
<ui:param name="subject" value="Templating with Seam Mail"/>
<ui:define name="body">
<p>
This example demonstrates that you can easily use
<i>facelets templating</i> in email!
</p>
</ui:define>
You can also use Facelets source tags in your email. T hese must be placed in a JAR in WEB-INF/lib
because referencing the .taglib.xm l from web.xm l is not reliable when using Seam Mail. (When
mail is sent asynchronously, Seam Mail cannot access the full JSF or Servlet context, so it does not
acknowledge web.xm l configuration parameters.)
T o configure Facelets or JSF further when sending mail, you will need to override the Renderer
component and perform the configuration programmatically. T his should only be done by advanced
users.
19.1.6. Internationalization
Seam supports sending internationalized messages. By default, Seam uses encoding provided by JSF,
but this can be overridden on the template:
<m:message charset="UTF-8">
...
</m:message>
T he body, subject, and recipient and sender names are encoded. You will need to make sure that
Facelets parses your page with the correct character set by setting the encoding of the template:
<?xml version="1.0" encoding="UTF-8"?>
19.1.7. Other Headers
Seam also provides support for some additional email headers. (See Section 19.3, “T ags”.) You can set
the importance of the email, and ask for a read receipt:
<m:message xmlns:m="http://jboss.org/schema/seam/mail"
importance="low" requestReadReceipt="true"/>
Otherwise, you can add any header to the message by using the <m :header>
tag:
<m:header name="X-Sent-From" value="JBoss Seam"/>
19.2. Configuration
Include jboss-seam -m ail.jar in your WEB-INF/lib directory to include email support in your
application. If you use Red Hat JBoss Enterprise Application Platform, no further configuration is
required. If you do not use JBoss Enterprise Application Platform, make sure you have the JavaMail API
and a copy of the Java Active Framework. T he versions distributed with Seam are lib/m ail.jar and
lib/activation.jar respectively.
Note
T he Seam Mail module requires both the use of the seam -ui package, and that Facelets be
used as the view technology.
T he m ailSession component uses JavaMail to talk to a 'real' SMT P server.
24 9
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
19.2.1. mailSession
If you are working in a Java EE 5 environment, a JavaMail session may be available through a JNDI look
up. Otherwise, you can use a Seam-configured session.
T he m ailSession component's properties are described in more detail in Section 29.8, “Mail-related
components”.
19.2.1.1. JNDI look up in Enterprise Application Platform
T he JBoss Enterprise Application Platform deploy/m ail-service.xm l configures a JavaMail
session binding into JNDI. T he default service configuration must be altered for your network.
http://wiki.jboss. org/wiki/Wiki.jsp?page=JavaMail describes the service in more detail.
<components xmlns="http://jboss.org/schema/seam/components"
xmlns:core="http://jboss.org/schema/seam/core"
xmlns:mail="http://jboss.org/schema/seam/mail">
<mail:mail-session session-jndi-name="java:/Mail"/>
</components>
Here, we tell Seam to retrieve the mail session bound to java:/Mail from JNDI.
19.2.1.2. Seam-configured Session
A mail session can be configured via com ponents.xm l. Here we tell Seam to use
sm tp.exam ple.com as the SMT P server:
<components xmlns="http://jboss.org/schema/seam/components"
xmlns:core="http://jboss.org/schema/seam/core"
xmlns:mail="http://jboss.org/schema/seam/mail">
<mail:mail-session host="smtp.example.com"/>
</components>
19.3. Tags
Emails are generated using tags in the http://jboss.org/schem a/seam /m ail namespace.
Documents should always have the m essage tag at the root of the message. T he message tag
prepares Seam to generate an email.
Facelets standard templating tags can be used as normal. Inside the body, you can use any JSF tag. If
the tag requires access to external resources such as stylesheets or JavaScript, be sure to set the
urlBase.
<m:message>
Root tag of a mail message.
im portance — Sets the importance of the mail message. Valid values are low, norm al,
or high. Defaults to norm al.
precedence — Sets the precedence of the message, for example, bulk.
requestReadReceipt — If set, a read receipt request will be added, and the read receipt
will be sent to the From : address. Defaults to false.
urlBase — If set, the value is prepended to the requestContextPath, allowing you to
use components such as <h:graphicIm age>
in your emails.
m essageId — Explicitly sets the Message-ID.
<m:from>
Sets the From : address for the email. Only one exists per email.
250
Chapter 19. Email
nam e — T he name that the email comes from.
address — T he email address that the email comes from.
<m:replyT o>
Sets the Reply-to: address for the email. Only one exists per email.
address — the email address the email comes from.
<m:to>
Adds a recipient to the email. Use multiple <m :to>
tags for multiple recipients. T his tag can be safely placed inside a repeat tag such as
<ui:repeat>
.
nam e — T he name of the recipient.
address — T he email address of the recipient.
<m:cc>
Adds a CC recipient to the email. Use multiple <m :cc>
tags for multiple CCs. T his tag can be safely placed inside a iterator tag such as <ui:repeat> .
nam e — T he name of the recipient.
address — T he email address of the recipient.
<m:bcc>
Adds a BCC recipient to the email. Use multiple <m :bcc>
tags for multiple bccs. T his tag can be safely placed inside a repeat tag such as <ui:repeat>
.
nam e — T he name of the recipient.
address — T he email address of the recipient.
<m:header>
Adds a header to the email. (For example, X-Sent-From : JBoss Seam .)
nam e — T he name of the header to add. (For example, X-Sent-From .)
value — T he value of the header to add. (For example, JBoss Seam .)
<m:attachment>
Adds an attachment to the email.
value — T he file to attach:
String — A String is interpreted as a path to file within the classpath.
java.io.File — An EL expression can reference a File object.
java.net.URL — An EL expression can reference a URL object.
java.io.InputStream — An EL expression can reference an InputStream . In this
case both a fileNam e and a contentT ype must be specified.
byte[] — An EL expression can reference a byte[]. In this case both a fileNam e
and a contentT ype must be specified.
If the value attribute is omitted:
If this tag contains a <p:docum ent>
tag, the document described will be generated and attached to the email. A fileNam e
251
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
should be specified.
If this tag contains other JSF tags, a HT ML document will be generated from them and
attached to the email. A fileNam e should be specified.
fileNam e — Specifies the file name to use for the attached file.
contentT ype — Specifies the MIME type of the attached file.
<m:subject>
Sets the subject for the email.
<m:body>
Sets the body for the email. Supports an alternative facet which, if a HT ML email is
generated, can contain alternative text for a mail reader which does not support HT ML.
type — If set to plain, a plain text email will be generated. Otherwise, a HT ML email is
generated.
252
Chapter 20. Asynchronicity and messaging
Chapter 20. Asynchronicity and messaging
Seam makes it easy to perform work asynchronously from a web request. Asynchronicity in Java EE is
usually linked with JMS, and where your quality of service requirements are strict and well-defined, this is
logical. It is easy to send JMS messages through Seam components.
However, for many use cases, JMS is more powerful than necessary. Seam layers a simple,
asynchronous method and event facility over your choice of dispatchers:
java.util.concurrent.ScheduledT hreadPoolExecutor (by default)
the EJB timer service (for EJB 3.0 environments)
Quartz
20.1. Asynchronicity
Asynchronous events and method calls have the same quality of service expectations as the underlying
dispatcher mechanism. T he default dispatcher, based upon a ScheduledT hreadPoolExecutor
performs efficiently but provides no support for persistent asynchronous tasks, and hence no guarantee
that a task will ever actually be executed. If you are working in an environment that supports EJB 3.0,
add the following line to com ponents.xm l to ensure that your asynchronous tasks are processed by
the container's EJB timer service:
<async:timer-service-dispatcher/>
If you want to use asynchronous methods in Seam, you do not need to interact directly with the T imer
service. However, it is important that your EJB3 implementation has the option of using persistent timers,
which give some guarantee that the task will eventually be processed.
Alternatively, you can use the open source Quartz library to manage asynchronous method. T o do so,
bundle the Quartz library JAR (found in the lib directory) in your EAR, and declare it as a Java module
in application.xm l. You can configure the Quartz dispatcher by adding a Quartz property file to the
classpath —this file must be named seam .quartz.properties. T o install the Quartz dispatcher, you
will also need to add the following line to com ponents.xm l:
<async:quartz-dispatcher/>
Since the Seam API for the default ScheduledT hreadPoolExecutor, the EJB3 T im er, and the
Quartz Scheduler are very similar, you can "plug and play" by adding a line to com ponents.xm l.
20.1.1. Asynchronous methods
An asynchronous call allows a method call to be processed asynchronously (in a different thread) to the
caller. Usually, asynchronous calls are used when we want to send an immediate response to the client,
and simultaneously process expensive work in the background. T his pattern works well in AJAX
applications, where the client can automatically poll the server for the result of the work.
For EJB components, annotate the implementation of the bean to specify that a method be processed
asynchronously. For JavaBean components, annotate the component implementation class:
import org.jboss.seam.annotations.async.Asynchronous;
@Stateless
@Name("paymentHandler")
public class PaymentHandlerBean implements PaymentHandler
{
@Asynchronous
public void processPayment(Payment payment) {
//do some work!
}
}
253
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Asynchronicity is transparent to the bean class. It is also transparent to the client:
@Stateful
@Name("paymentAction")
public class CreatePaymentAction
{
@In(create=true) PaymentHandler paymentHandler;
@In Bill bill;
public String pay() {
paymentHandler.processPayment( new Payment(bill) );
return "success";
}
}
T he asynchronous method is processed in a fresh event context, and has no access to the session or
conversation context state of the caller.
You can schedule asynchronous method calls for delayed execution with the @ Duration,
@ Expiration and @ IntervalDuration annotations.
import org.jboss.seam.annotations.async.Asynchronous;
@Local
public interface PaymentHandler {
@Asynchronous
public void processScheduledPayment(Payment payment,
@Expiration Date date);
@Asynchronous
public void processRecurringPayment(Payment payment,
@Expiration Date date,
@IntervalDuration Long interval);
}
@Stateful
@Name("paymentAction")
public class CreatePaymentAction
{
@In(create=true) PaymentHandler paymentHandler;
@In Bill bill;
public String schedulePayment() {
paymentHandler.processScheduledPayment(new Payment(bill),
bill.getDueDate() );
return "success";
}
public String scheduleRecurringPayment() {
paymentHandler.processRecurringPayment(new Payment(bill),
bill.getDueDate(), ONE_MONTH );
return "success";
}
}
Both client and server can access the T im er object associated with the invocation. T he T im er shown
below is the EJB3 timer used with the EJB3 dispatcher. For the default
ScheduledT hreadPoolExecutor, the timer returns Future from the JDK. For the Quartz
dispatcher, it returns QuartzT riggerHandle, which will be discussed in the next section.
254
Chapter 20. Asynchronicity and messaging
import org.jboss.seam.annotations.async.Asynchronous;
@Local
public interface PaymentHandler
{
@Asynchronous
public Timer processScheduledPayment(Payment payment,
@Expiration Date date);
}
@Stateless
@Name("paymentHandler")
public class PaymentHandlerBean implements PaymentHandler {
@In Timer timer;
public Timer processScheduledPayment(Payment payment,
@Expiration Date date) {
//do some work!
return timer; //note that return value is completely ignored
}
}
@Stateful
@Name("paymentAction")
public class CreatePaymentAction
{
@In(create=true) PaymentHandler paymentHandler;
@In Bill bill;
public String schedulePayment() {
Timer timer =
paymentHandler.processScheduledPayment(new Payment(bill),
bill.getDueDate());
return "success";
}
}
Asynchronous methods cannot return any other value to the caller.
20.1.2. Asynchronous methods with the Quartz Dispatcher
T he Quartz dispatcher lets you use the @ Asynchronous, @ Duration, @ Expiration, and
@ IntervalDuration annotations, as above, but it also supports several additional annotations.
T he @ FinalExpiration annotation specifies an end date for a recurring task. Note that you can
inject the QuartzT riggerHandle.
255
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
@In QuartzTriggerHandle timer;
// Defines the method in the "processor" component
@Asynchronous
public QuartzTriggerHandle schedulePayment(@Expiration Date when,
@IntervalDuration Long interval,
@FinalExpiration Date endDate,
Payment payment) {
// do the repeating or long running task until endDate
}
... ...
// Schedule the task in the business logic processing code
// Starts now, repeats every hour, and ends on May 10th, 2010
Calendar cal = Calendar.getInstance ();
cal.set (2010, Calendar.MAY, 10);
processor.schedulePayment(new Date(), 60*60*1000, cal.getTime(), payment);
Note that this method returns the QuartzT riggerHandle object, which can be used to stop, pause,
and resume the scheduler. T he QuartzT riggerHandle object is serializable, so it can be saved into
the database if required for an extended period of time.
QuartzTriggerHandle handle=
processor.schedulePayment(payment.getPaymentDate(),
payment.getPaymentCron(),
payment);
payment.setQuartzTriggerHandle( handle );
// Save payment to DB
// later ...
// Retrieve payment from DB
// Cancel the remaining scheduled tasks
payment.getQuartzTriggerHandle().cancel();
T he @ IntervalCron annotation supports Unix cron job syntax for task scheduling. For example, the
following asynchronous method runs at 2:10pm and at 2:44pm every Wednesday in the month of March.
import org.jboss.seam.annotations.async.Asynchronous;
// Define the method
@Asynchronous
public QuartzTriggerHandle schedulePayment(@Expiration Date when,
@IntervalCron String cron,
Payment payment) {
// do the repeating or long running task
}
... ...
// Schedule the task in the business logic processing code
QuartzTriggerHandle handle =
processor.schedulePayment(new Date(), "0 10,44 14 ? 3 WED", payment);
T he @ IntervalBusinessDay annotation supports invocation in the "nth Business Day" scenario. For
instance, the following asynchronous method runs at 14:00 on the 2nd business day of each month. All
weekends and US Federal holidays are excluded from the business days by default.
256
Chapter 20. Asynchronicity and messaging
import org.jboss.seam.annotations.async.Asynchronous;
// Define the method
@Asynchronous
public QuartzTriggerHandle schedulePayment(@Expiration Date when,
@IntervalBusinessDay NthBusinessDay nth,
Payment payment) {
// do the repeating or long running task
}
... ...
// Schedule the task in the business logic processing code
QuartzTriggerHandle handle =
processor.schedulePayment(new Date(),
new NthBusinessDay(2, "14:00", WEEKLY),
payment);
T he NthBusinessDay object contains the configuration of the invocation trigger. You can specify more
holidays (company holidays and non-US holidays, for example) in the additionalHolidays property.
public class NthBusinessDay implements Serializable {
int n;
String fireAtTime;
List<Date> additionalHolidays;
BusinessDayIntervalType interval;
boolean excludeWeekends;
boolean excludeUsFederalHolidays;
public enum BusinessDayIntervalType { WEEKLY, MONTHLY, YEARLY }
public NthBusinessDay () {
n = 1;
fireAtTime = "12:00";
additionalHolidays = new ArrayList<Date> ();
interval = BusinessDayIntervalType.WEEKLY;
excludeWeekends = true;
excludeUsFederalHolidays = true;
}
... ...
}
T he @ IntervalDuration, @ IntervalCron, and @ IntervalNthBusinessDay annotations are
mutually exclusive. Attempting to use them in the same method will cause a Runtim eException error.
20.1.3. Asynchronous events
Component-driven events can also be asynchronous. T o raise an event for asynchronous processing,
call the raiseAsynchronousEvent() method of the Events class. T o schedule a timed event, call
the raisedT im edEvent() method and pass a schedule object. (For the default dispatcher or timer
service dispatcher, use T im erSchedule.)
20.1.4. Handling exceptions from asynchronous calls
Each asynchronous dispatcher behaves differently when an exception propagates through it. For
example, the java.util.concurrent suspends further executions of a repeating call, and the EJB3
timer service swallows the exception, so Seam catches any exception that propagates from the
asynchronous call before it reaches the dispatcher.
By default, any exception that propagates from an asynchronous execution will be caught and logged at
error level. You can customize this behavior globally by overriding the
org.jboss.seam .async.asynchronousExceptionHandler component:
257
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
@Scope(ScopeType.STATELESS)
@Name("org.jboss.seam.async.asynchronousExceptionHandler")
public class MyAsynchronousExceptionHandler
extends AsynchronousExceptionHandler {
@Logger Log log;
@In Future timer;
@Override
public void handleException(Exception exception) {
log.debug(exception);
timer.cancel(false);
}
}
Here, with java.util.concurrent dispatcher, we inject its control object and cancel all future
invocations when an exception is encountered.
You can alter this behavior for an individual component by implementing the public void
handleAsynchronousException(Exception exception); method on that component, like so:
public void handleAsynchronousException(Exception exception) {
log.fatal(exception);
}
20.2. Messaging in Seam
It is easy to send and receive JMS messages to and from Seam components.
20.2.1. Configuration
T o configure Seam infrastructure to send JMS messages, you must first tell Seam about the topics and
queues you want to send messages to, and where to find the QueueConnectionFactory and
T opicConnectionFactory, depending on your requirements.
By default, Seam uses UIL2ConnectionFactory, the default connection factory with JBossMQ. If you
use another JMS provider, you must set one or both of
queueConnection.queueConnectionFactoryJndiNam e and
topicConnection.topicConnectionFactoryJndiNam e, in either seam .properties,
web.xm l, or com ponents.xm l.
T o install Seam-managed T opicPublishers and QueueSenders, you must also list topics and
queues in com ponents.xm l:
<jms:managed-topic-publisher name="stockTickerPublisher"
auto-create="true" topic-jndi-name="topic/stockTickerTopic"/>
<jms:managed-queue-sender name="paymentQueueSender"
auto-create="true" queue-jndi-name="queue/paymentQueue"/>
20.2.2. Sending messages
Once configuration is complete, you can inject a JMS T opicPublisher and T opicSession into any
component:
258
Chapter 20. Asynchronicity and messaging
@Name("stockPriceChangeNotifier")
public class StockPriceChangeNotifier {
@In private TopicPublisher stockTickerPublisher;
@In private TopicSession topicSession;
public void publish(StockPrice price) {
try {
stockTickerPublisher.publish(topicSession
.createObjectMessage(price));
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
Or, to work with a queue:
@Name("paymentDispatcher")
public class PaymentDispatcher {
@In private QueueSender paymentQueueSender;
@In private QueueSession queueSession;
public void publish(Payment payment) {
try {
paymentQueueSender.send(queueSession.createObjectMessage(payment));
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
20.2.3. Receiving messages using a message-driven bean
You can process messages with any EJB3 message-driven bean. Message-driven beans can
sometimes be Seam components, in which case, you can inject other event- and application-scoped
Seam components. T he following is an example of the payment receiver, which delegates to the payment
processor.
Note
You may need to set the create attribute on the @ In annotation to true so that Seam can
create an instance of the component to be injected. (T his is necessary only if the component
does not support auto-creation — that is, it is not annotated with @ Autocreate.)
First, create a message-driven bean to receive the message:
259
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
@MessageDriven(activationConfig =
{@ActivationConfigProperty(propertyName =
propertyValue
@ActivationConfigProperty(propertyName =
propertyValue
})
"destinationType",
= "javax.jms.Queue"),
"destination",
= "queue/paymentQueue")
@Name("paymentReceiver")
public class PaymentReceiver implements MessageListener
{
@Logger private Log log;
@In(create = true) private PaymentProcessor paymentProcessor;
@Override
public void onMessage(Message message)
{
try {
paymentProcessor.processPayment((Payment) ((ObjectMessage)
message).getObject());
} catch (JMSException ex) {
log.error("Message payload did not contain a Payment object", ex);
}
}
}
Next, implement the Seam component to which the receiver will delegate payment processing:
@Name("paymentProcessor")
public class PaymentProcessor {
@In private EntityManager entityManager;
public void processPayment(Payment payment) {
// perhaps do something more fancy
entityManager.persist(payment);
}
}
If you want to perform transaction operations in your message-driven bean, ensure that you are working
with an XA datasource, or you will not be able to roll back database changes in the event that a
database transaction commits, but a subsequent message operation fails.
20.2.4. Receiving messages in the client
Seam Remoting lets you subscribe to a JMS topic from client-side JavaScript. You can find more
information in Chapter 23, Remoting.
260
Chapter 21. Caching
Chapter 21. Caching
Database is the primary bottleneck in most enterprise applications, and the least scalable tier of the
runtime environment. We can dramatically improve the application performance by reducing the number
of times the database is accessed.
A well-designed Seam application features a rich and multi-layered caching strategy that impacts every
layer of the application, including:
A cache for the database. T his is vital, but cannot scale like a cache in the application tier.
A secondary cache of data from the database, provided by the ORM solution (Hibernate, or another
JPA implementation). In a clustered environment, it can be very expensive to effectively implement a
cache whose data is transactionally consistent with the database and the rest of the cluster.
T herefore, secondary cache is best used to store data that is rarely updated, and shared between
many users. In traditional stateless architectures, secondary cache is often used (ineffectively) to
store conversational state.
T he Seam conversational context, which is a cache of conversational state. Components in the
conversation context, store the state related to the current user interaction.
T he Seam-managed persistence context, which acts as a cache of data read in the current
conversation. (An Enterprise JavaBean [EJB] container-managed persistence context associated
with a conversation-scoped stateful session bean can be used instead of a Seam-managed
persistence context.) Seam optimizes the replication of Seam-managed persistence contexts in a
clustered environment, and optimistic locking provides sufficient transactional consistency with the
database. T he performance implications of this cache are minimal, unless you read thousands of
objects into a single persistence context.
T he Seam application context, which can be used to cache non-transactional state. T he state held in
the Seam application context is not visible to other nodes in the cluster.
T he Seam cacheProvider component within the application, which integrates Infinispan, or
Ehcache into the Seam environment. T he state held in the Seam cacheProvider component is
visible to other nodes in the cluster if your cache is configured to run in clustered mode.
Seam cache that renders fragments of a JSF page. Unlike the ORM secondary cache, this cache is
not automatically invalidated when data is updated. You have to write application code to perform
explicit invalidation of this cache, or set appropriate expiry policies.
For more information about secondary cache, refer to the documentation of your ORM solution, since this
can be quite complex.
T his chapter discusses the use of caching through the cacheProvider component, and caching as
stored page fragments through the <s:cache> control.
21.1. Using Caching in Seam
T he built-in cacheProvider component manages an instance of:
Infinispan 5.x (suitable for use in Red Hat JBoss Enterprise Application Platform and
other containers)
org.infninispan.tree.T reeCache
EHCache (suitable for use in any container)
net.sf.ehcache.CacheManager
261
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Note
T he built-in cacheProvider component that uses Infinispan T ree module as JBoss Cache
successor is not supported in Seam 2.3.1.Final-redhat-2 as part of JBoss Web Framework Kit
2.3.0.
You can add an immutable Java object in the cache. T he immutable object is stored in the cache and
replicated across the cluster (assuming that replication is supported and enabled). T o add mutable
objects to the cache, read the documentation of the underlying caching project to know how to notify the
cache of changes to the cache.
T o use the cacheProvider component, include the jars of cache implementation in your project:
Infinispan 5.x
infinispan-core.jar - Infinispan Core 5.1.x.Final
infinispan-tree.jar - Infinispan T reeCache 5.1.x.Final
jgroups.jar - JGroups 3.0
EHCache
ehcache.jar - EHCache 1.2.3
For an EAR deployment of Seam, we recommend you to add the infinispan jars, and configuration directly
into the EAR.
Note
JBoss Enterprise Application Platform 6 already provides Infinispan and JGroups jars. T urn ON
the dependencies in your deployment file or modify MET A-INF/Manifest.m f file to get the
dependencies. Refer to the relevant JBoss Enterprise Application Platform documentation for
further information.
You can find a sample cache configuration at: exam ples/blog/blogweb/src/m ain/resources/infinispan.xm l.
Ehcache runs in its default configuration without a configuration file.
T o alter the configuration file in use, configure your cache in com ponents.xm l:
<components xmlns="http://jboss.org/schema/seam/components"
xmlns:cache="http://jboss.org/schema/seam/cache">
<cache:infinispan-cache-provider configuration="infinispan.xml" />
</components>
Now you can inject the cache into any Seam component:
262
Chapter 21. Caching
@Name("chatroomUsers")
@Scope(ScopeType.STATELESS)
public class ChatroomUsers
{
@In CacheProvider cacheProvider;
@Unwrap
public Set<String> getUsers() throws CacheException
{
Set<String> userList = (Set<String>) cacheProvider.get("chatroom", "userList");
if (userList==null)
{
userList = new HashSet<String>();
cacheProvider.put("chatroom", "userList", userList);
}
return userList;
}
}
T o make multiple cache configurations available to your application, use com ponents.xm l to configure
multiple cache providers:
<components xmlns="http://jboss.org/schema/seam/components"
xmlns:cache="http://jboss.org/schema/seam/cache">
<cache:infinispan-cache-provider name="myCache"
configuration="myown/cache.xml"/>
<cache:infinispan-cache-provider name="myOtherCache"
configuration="myother/cache.xml"/>
</components>
21.2. Page fragment caching
Seam uses the <s:cache> tag to solve the problem of page fragment caching in JSF. T he<s:cache>
tag uses pojoCache internally, so follow the previous steps — place the JARs in the EAR and edit
additional configuration options — before using the <s:cache> tag.
T he <s:cache> tag stores some rendered content that is rarely updated. For example, the welcome
page of our blog displays recent blog entries:
<s:cache key="recentEntries-#{blog.id}" region="welcomePageFragments">
<h:dataTable value="#{blog.recentEntries}" var="blogEntry">
<h:column>
<h3>#{blogEntry.title}</h3>
<div>
<s:formattedText value="#{blogEntry.body}"/>
</div>
</h:column>
</h:dataTable>
</s:cache>
T he key parameter allows you to store multiple versions of each page fragment. In the above example,
there is one cached version per blog. T he region parameter determines the cache or region node
where all the versions are stored. Different nodes may have differing expiry policies.
T he <s:cache> tag does not indicate when the underlying data is updated, so manually remove the
cached fragment when a change occurs:
263
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
public void post()
{
...
entityManager.persist(blogEntry);
cacheProvider.remove("welcomePageFragments", "recentEntries-" + blog.getId() );
}
If changes need not be immediately visible to the user, set up a short expiry period on the cache node.
264
Chapter 22. Web Services
Chapter 22. Web Services
Seam integrates with JBossWS (JWS) to allow standard Java EE web services to take full advantage of
Seam's contextual framework, including conversational web service support. T his chapter guides you
through web service configuration for a Seam environment.
22.1. Configuration and Packaging
T o allow Seam to intercept web service requests so that the necessary Seam contexts can be created
for the request, a special SOAP handler must be configured;
org.jboss.seam .webservice.SOAPRequestHandler is a SOAPHandler implementation that
does the work of managing Seam's lifecycle during the scope of a web service request.
A special configuration file, soap-handlers.xm l should be placed into the MET A-INF directory of the
jar file that contains the web service classes. T his file contains the following SOAP handler
configuration:
<?xml version="1.0" encoding="UTF-8"?>
<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
<handler-chain>
<handler>
<handler-name>SOAP Request Handler</handler-name>
<handler-class>org.jboss.seam.webservice.SOAPRequestHandler</handler-class>
</handler>
</handler-chain>
</handler-chains>
22.2. Conversational Web Services
Seam uses a SOAP header element in both SOAP request and response messages to carry the
conversation ID between the consumer and the service. One example of a web service request
containing a conversation ID is:
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:seam="http://seambay.example.seam.jboss.org/">
<soapenv:Header>
<seam:conversationId xmlns:seam='http://www.jboss.org/seam/webservice'>
2
</seam:conversationId>
</soapenv:Header>
<soapenv:Body>
<seam:confirmAuction/>
</soapenv:Body>
</soapenv:Envelope>
T he above SOAP message contains a conversationId element, which contains the conversation ID
for the request — in this case, 2. Because web services can be consumed by a variety of web service
clients written in a variety of languages, the developer is responsible for implementing conversation ID
propagation between individual web services to be used in a single conversation's scope.
T he conversationId header element must be qualified with a namespace of
http://www.jboss.org/seam /webservice, or Seam will be unable to read the conversation ID
from the request. An example response to the above request message is:
265
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<env:Envelope xmlns:env='http://schemas.xmlsoap.org/soap/envelope/'>
<env:Header>
<seam:conversationId xmlns:seam='http://www.jboss.org/seam/webservice'>
2
</seam:conversationId>
</env:Header>
<env:Body>
<confirmAuctionResponse
xmlns="http://seambay.example.seam.jboss.org/"/>
</env:Body>
</env:Envelope>
Note that the response message contains the same conversationId element as the request.
22.2.1. A Recommended Strategy
Since web services must be implemented as either stateless session beans or POJOs, we recommend
that conversational web services implement the web service as a facade for a conversational Seam
component.
If the web service is written as a stateless session bean, it can be transformed into a Seam component
by annotating it with @ Nam e. T his allows Seam bijection, and other features, to be used in the web
service class itself.
22.3. An example web service
T he example code that follows is from the seamBay example application, which can be found in Seam's
/exam ples directory, and follows the recommended strategy outlined in the previous section. First, we
will look at the web service class and one of its web service methods:
@Stateless
@Name("auctionService")
@WebService(name = "AuctionService")
@HandlerChain(file = "soap-handlers.xml")
public class AuctionService implements AuctionServiceRemote
{
@WebMethod
public boolean login(String username, String password)
{
Identity.instance().setUsername(username);
Identity.instance().setPassword(password);
Identity.instance().login();
return Identity.instance().isLoggedIn();
}
// snip
}
Here, the web service is a stateless session bean annotated with the JWS annotations from the
266
Chapter 22. Web Services
javax.jws package, as defined by JSR-181. T he @ WebService annotation tells the container that this
class implements a web service. T he @ WebMethod annotation on the login() method identifies the
method as a web service method. T he nam e and serviceNam e attributes in the @ WebService
annotation are optional.
When the web service is a stateless session bean, each method that will be exposed as a web service
method must also be declared in the remote interface of the web service class. In the previous example,
since the AuctionServiceRem ote interface is annotated as a @ WebService, it must declare the
login() method.
In the previous example, the web service implements a login() method that delegates to Seam's builtin Identity component. As our recommended strategy suggests, the web service is written as a
simple facade. T he real work takes place in a Seam component. T his means that business logic is
reused efficiently between web services and other clients.
In the following example, the web service method begins a new conversation by delegating to the
AuctionAction.createAuction() method:
@WebMethod
public void createAuction(String title, String description, int categoryId)
{
AuctionAction action =
(AuctionAction) Component.getInstance(AuctionAction.class, true);
action.createAuction();
action.setDetails(title, description, categoryId);
}
T he code from AuctionAction is as follows:
@Begin
public void createAuction()
{
auction = new Auction();
auction.setAccount(authenticatedAccount);
auction.setStatus(Auction.STATUS_UNLISTED);
durationDays = DEFAULT_AUCTION_DURATION;
}
Here, we see how web services can participate in long-running conversations by acting as a facade and
delegating the real work to a conversational Seam component.
22.4. RESTful HTTP web services with RESTEasy
Seam integrates the REST Easy implementation of the JAX-RS specification (JSR 311). You can decide
which of the following features are integrated with your Seam application:
REST Easy bootstrap and configuration, with automatic resource detection. and providers.
SeamResourceServlet-served HT T P/REST requests, without the need for an external servlet or
configuration in web.xm l.
Resources written as Seam components with full Seam life cycle management and bijection.
22.4.1. RESTEasy configuration and request serving
First, download the REST Easy libraries and the jaxrs-api.jar, and deploy them alongside the
integration library (jboss-seam -resteasy.jar) and any other libraries your application requires.
In seam-gen based projects, this can be done by appending jaxrs-api.jar, resteasy-jaxrs.jar
and jboss-seam -resteasy.jar to the deployed-jars.list (war deployment) or deployedjars-ear.list (ear deployment) file. For a JBDS based project, copy the libraries mentioned above to
the EarContent/lib (ear deployment) or WebContent/WEB-INF/lib (war deployment) folder and
reload the project in the IDE.
267
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
All classes annotated with @ javax.ws.rs.Path will automatically be discovered and registered as
HT T P resources at start up. Seam automatically accepts and serves HT T P requests with its built-in
Seam ResourceServlet. T he URI of a resource is built like so:
T he URI begins with the pattern mapped in web.xm l for the Seam ResourceServlet — in the
examples provided, /seam /resource. Change this setting to expose your REST ful resources
under a different base. Remember that this is a global change, and other Seam resources
(s:graphicIm age) will also be served under this base path.
Seam's REST Easy integration then appends a configurable string to the base path (/rest by
default). So, in the example, the full base path of your resources would be /seam /resource/rest.
We recommend changing this string in your application to something more descriptive — add a
version number to prepare for future REST API upgrades. T his allows old clients to keep the old URI
base.
Finally, the resource is made available under the defined @ Path. For example, a resource mapped
with @ Path("/custom er") would be available under /seam /resource/rest/custom er.
T he following resource definition would return a plain text representation for any GET request using the
URI http://your.hostnam e/seam /resource/rest/custom er/123:
@Path("/customer")
public class MyCustomerResource {
@GET
@Path("/{customerId}")
@Produces("text/plain")
public String getCustomer(@PathParam("customerId") int id) {
return ...;
}
}
If these defaults are acceptable, there is no need for additional configuration. However, if required, you
can configure REST Easy in your Seam application. First, import the resteasy namespace into your XML
configuration file header:
<components
xmlns="http://jboss.org/schema/seam/components"
xmlns:resteasy="http://jboss.org/schema/seam/resteasy"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://jboss.org/schema/seam/resteasy
http://jboss.org/schema/seam/resteasy-2.3.xsd
http://jboss.org/schema/seam/components
http://jboss.org/schema/seam/components-2.3.xsd">
<resteasy:application resource-path-prefix="/restv1"/>
T he full base path to your resources is now /seam /resource/restv1/{resource}. Note that your
@ Path definitions and mappings do not change. T his is an application-wide switch, usually used for
versioning of the HT T P API.
If you want to map the full path in your resources, you can disable base path stripping:
<resteasy:application strip-seam-resource-path="false"/>
Here, the path of a resource is now mapped with @ Path("/seam /resource/rest/custom er").
Disabling this feature binds your resource class mappings to a particular deployment scenario. T his is
not recommended.
268
Chapter 22. Web Services
Seam scans your classpath for any deployed @ javax.ws.rs.Path resources or
@ javax.ws.rs.ext.Provider classes. You can disable scanning and configure these classes
manually like so:
<resteasy:application
scan-providers="false"
scan-resources="false"
use-builtin-providers="true">
<resteasy:resource-class-names>
<value>org.foo.MyCustomerResource</value>
<value>org.foo.MyOrderResource</value>
<value>org.foo.MyStatelessEJBImplementation</value>
</resteasy:resource-class-names>
<resteasy:provider-class-names>
<value>org.foo.MyFancyProvider</value>
</resteasy:provider-class-names>
</resteasy:application>
T he use-built-in-providers switch enables (default) or disables the REST Easy built-in providers.
Since these provide plain text, JSON and JAXB marshaling, we recommend that these are left enabled.
REST Easy supports plain EJBs (EJBs that are not Seam components) as resources. Instead of
configuring the JNDI names in a non-portable fashion in web.xm l (see REST Easy documentation), you
can simply list the EJB implementation classes, not the business interfaces, in com ponents.xm l as
shown above. Note that you have to annotate the @ Local interface of the EJB with @ Path, @ GET , and
so on - not the bean implementation class. T his allows you to keep your application deployment-portable
with the global Seam jndi-pattern switch on <core:init/>. Note that plain (non-Seam
component) EJB resources will not be found even if scanning of resources is enabled, you always have
to list them manually. Again, this whole paragraph is only relevant for EJB resources that are not also
Seam components and that do not have an @ Nam e annotation.
Finally, you can configure media type and language URI extensions:
<resteasy:application>
<resteasy:media-type-mappings>
<key>txt</key>
<value>text/plain</value>
</resteasy:media-type-mappings>
<resteasy:language-mappings>
<key>deutsch</key><value>de-DE</value>
</resteasy:language-mappings>
</resteasy:application>
T his definition would map the URI suffix of .txt.deutsch to the additional Accept and AcceptLanguage header values, text/plain and de-DE.
22.4.2. Resources and providers as Seam components
Resource and provider instances are, by default, managed by REST Easy. A resource class will be
instantiated by REST Easy and serve a single request, after which it will be destroyed. T his is the default
JAX-RS life cycle. Providers are instantiated once for the entire application. T hese are stateless
singletons.
Resources and providers can also be written as Seam components to take advantage of Seam's richer
life cycle management, and bijection and security abilities. Make your resource class into a Seam
component like so:
269
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
@Name("customerResource")
@Path("/customer")
public class MyCustomerResource {
@In
CustomerDAO customerDAO;
@GET
@Path("/{customerId}")
@Produces("text/plain")
public String getCustomer(@PathParam("customerId") int id) {
return customerDAO.find(id).getName();
}
}
A custom erResource instance is now handled by Seam when a request hits the server. T his
component is event-scoped, so its life cycle is identical to that of the JAX-RS. However, the Seam
JavaBean component gives you full injection support, and full access to all other components and
contexts. Session, application, and stateless resource components are also supported. T hese three
scopes allow you to create an effectively stateless Seam middle-tier HT T P request-processing
application.
You can annotate an interface and keep the implementation free from JAX-RS annotations:
@Path("/customer")
public interface MyCustomerResource {
@GET
@Path("/{customerId}")
@Produces("text/plain")
public String getCustomer(@PathParam("customerId") int id);
}
@Name("customerResource")
@Scope(ScopeType.STATELESS)
public class MyCustomerResourceBean implements MyCustomerResource {
@In
CustomerDAO customerDAO;
public String getCustomer(int id) {
return customerDAO.find(id).getName();
}
}
You can use SESSION-scoped Seam components. By default, the session will however be shortened to
a single request. In other words, when an HT T P request is being processed by the REST Easy
integration code, an HT T P session will be created so that Seam components can utilize that context.
When the request has been processed, Seam will look at the session and decide if the session was
created only to serve that single request (no session identifier has been provided with the request, or no
session existed for the request). If the session has been created only to serve this request, the session
will be destroyed after the request!
Assuming that your Seam application only uses event, application, or stateless components, this
procedure prevents exhaustion of available HT T P sessions on the server. T he REST Easy integration
with Seam assumes by default that sessions are not used, hence anemic sessions would add up as
every REST request would start a session that will only be removed when timed out.
If your REST ful Seam application has to preserve session state across REST HT T P requests, disable
this behavior in your configuration file:
270
Chapter 22. Web Services
<resteasy:application destroy-session-after-request="false"/>
Every REST HT T P request will now create a new session that will only be removed by timeout or explicit
invalidation in your code through Session.instance().invalidate(). It is your responsibility to
pass a valid session identifier along with your HT T P requests, if you want to utilize the session context
across requests.
Conversation-scoped resource components and conversation mapping are not currently supported.
Provider classes can also be Seam components. T hey must be either application-scoped or stateless.
Resources and providers can be EJBs or JavaBeans, like any other Seam component.
EJB Seam components are supported as REST resources. Always annotate the local business
interface, not the EJB implementation class, with JAX-RS annotations. T he EJB has to be ST AT ELESS.
Sub-resources as defined in the JAX RS specification, section 3.4.1, can also be Seam component
instances:
@Path("/garage")
@Name("garage")
public class GarageService
{
...
@Path("/vehicles")
public VehicleService getVehicles() {
return (VehicleService) Component.getInstance(VehicleService.class);
}
}
Note
REST Easy components do not support hot redeployment. As a result, the components should
never be placed in the src/hot folder. T he src/m ain folder should be used instead.
Note
Sub-resources as defined in the JAX RS specification, section 3.4.1, can not be Seam component
instances at this time. Only root resource classes can be registered as Seam components. In
other words, do not return a Seam component instance from a root resource method.
22.4.3. Securing resources
You can enable the Seam authentication filter for HT T P Basic and Digest authentication in
com ponents.xm l:
<web:authentication-filter url-pattern="/seam/resource/rest/*" auth-type="basic"/>
See the Seam security chapter on how to write an authentication routine.
After successful authentication, authorization rules with the common @ Restrict and
@ Perm issionCheck annotations are in effect. You can also access the client Identity, work with
permission mapping, and so on. All regular Seam security features for authorization are available.
22.4.4. Mapping exceptions to HTTP responses
Section 3.3.4 of the JAX-RS specification defines how JAX RS handles checked and unchecked
271
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
exceptions. Integrating REST Easy with Seam allows you to map exceptions to HT T P response codes
within Seam's pages.xm l. If you use pages.xm l already, this is easier to maintain than many JAX RS
exception mapper classes.
For exceptions to be handled within Seam, the Seam filter must be executed for your HT T P request. You
must filter all requests in your web.xm l, not as a request URI pattern that does not cover your REST
requests. T he following example intercepts all HT T P requests and enables Seam exception handling:
<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>/*</url-pattern>
</filter-mapping>
T o convert the unchecked UnsupportedOperationException thrown by your resource methods to
a 501 Not Im plem ented HT T P status response, add the following to your pages.xm l descriptor:
<exception class="java.lang.UnsupportedOperationException">
<http-error error-code="501">
<message>The requested operation is not supported</message>
</http-error>
</exception>
Custom or checked exceptions are handled in the same way:
<exception class="my.CustomException" log="false">
<http-error error-code="503">
<message>Service not available:
#{org.jboss.seam.handledException.message}</message>
</http-error>
</exception>
You do not have to send a HT T P error to the client if an exception occurs. Seam lets you map the
exception as a redirect to a view of your Seam application. Since this feature is typically used for human
clients (web browsers) and not for REST API remote clients, you should pay attention to conflicting
exception mappings in pages.xm l.
T he HT T P response does pass through the servlet container, so an additional mapping may apply if
you have <error-page> mappings in your web.xm l configuration. T he HT T P status code would then
be mapped to a rendered HT ML error page with status 200 OK.
22.4.5. Exposing entities via RESTful API
Seam makes it really easy to use a REST ful approach for accessing application data. One of the
improvements that Seam introduces is the ability to expose parts of your SQL database for remote
access via plain HT T P calls. For this purpose, the Seam/REST Easy integration module provides two
components: ResourceHom e and ResourceQuery, which benefit from the API provided by the Seam
Application Framework (Chapter 12, The Seam Application Framework). T hese components allow you to
bind domain model entity classes to an HT T P API.
22.4 .5.1. ResourceQuery
ResourceQuery exposes entity querying capabilities as a REST ful web service. By default, a simple
underlying Query component, which returns a list of instances of a given entity class, is created
automatically. Alternatively, the ResourceQuery component can be attached to an existing Query
component in more sophisticated cases. T he following example demonstrates how easily
ResourceQuery can be configured:
272
Chapter 22. Web Services
<resteasy:resource-query
path="/user"
name="userResourceQuery"
entity-class="com.example.User"/>
With this single XML element, a ResourceQuery component is set up. T he configuration is
straightforward:
T he component will return a list of com .exam ple.User instances.
T he component will handle HT T P requests on the URI path /user.
T he component will by default transform the data into XML or JSON (based on client's preference).
T he set of supported mime types can be altered by using the m edia-types attribute, for example:
<resteasy:resource-query
path="/user"
name="userResourceQuery"
entity-class="com.example.User"
media-types="application/fastinfoset"/>
Alternatively, if you do not like configuring components using XML, you can set up the component by
extension:
@Name("userResourceQuery")
@Path("user")
public class UserResourceQuery extends ResourceQuery<User>
{
}
Queries are read-only operations, the resource only responds to GET requests. Furthermore,
ResourceQuery allows clients of a web service to manipulate the resultset of a query using the following
path parameters:
Parameter name
Example
Description
start
/user?start=20
Returns a subset of a database
query result starting with the
20th entry.
show
/user?show=10
Returns a subset of the
database query result limited to
10 entries.
For example, you can send an HT T P GET request to /user?start=30& show=10 to get a list of
entries representing 10 rows starting with row 30.
Note
REST Easy uses JAXB to marshall entities. T hus, in order to be able to transfer them over the
wire, you need to annotate entity classes with @ XMLRootElem ent. Consult the JAXB and
REST Easy documentation for more information.
22.4 .5.2. ResourceHome
Just as ResourceQuery makes Query's API available for remote access, so does ResourceHome for the
Home component. T he following table describes how the two APIs (HT T P and Home) are bound
together.
273
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
T able 22.1. Bindings in ResourceHome
HT T P method
Path
Function
ResourceHome
method
GET
{path}/{id}
Read
getResource()
POST
{path}
Create
postResource()
PUT
{path}/{id}
Update
putResource()
DELET E
{path}/{id}
Delete
deleteResource()
You can GET , PUT , and DELET E a particular user instance by sending HT T P requests to
/user/{userId}
Sending a POST request to /user creates a new user entity instance and persists it. Usually, you
leave it up to the persistence layer to provide the entity instance with an identifier value and thus an
URI. T herefore, the URI is sent back to the client in the Location header of the HT T P response.
T he configuration of ResourceHome is very similar to ResourceQuery except that you need to explicitly
specify the underlying Home component and the Java type of the entity identifier property.
<resteasy:resource-home
path="/user"
name="userResourceHome"
entity-home="#{userHome}"
entity-id-class="java.lang.Integer"/>
Again, you can write a subclass of ResourceHome instead of XML:
@Name("userResourceHome")
@Path("user")
public class UserResourceHome extends ResourceHome<User, Integer>
{
@In
private EntityHome<User> userHome;
@Override
public Home<?, User> getEntityHome()
{
return userHome;
}
}
For more examples of ResourceHome and ResourceQuery components, take a look at the Seam Tasks
example application, which demonstrates how Seam/REST Easy integration can be used together with a
jQuery web client. In addition, you can find more code example in the Restbay example, which is used
mainly for testing purposes.
274
Chapter 23. Remoting
Chapter 23. Remoting
Seam uses Asynchronous JavaScript and XML (AJAX) to remotely access components from a web page.
T he framework for this functionality requires very little development effort — you can make your
components AJAX-accessible with simple annotations. T his chapter describes the steps required to
build an AJAX-enabled web page, and explains the Seam Remoting framework in further detail.
23.1. Configuration
T o use remoting, you must first configure your Seam Resource Servlet in your web.xm l file:
<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>
Next, import the necessary JavaScript into your web page. A minimum of two scripts must be imported.
T he first contains all client-side framework code, which enables remoting functionality:
<script
type="text/javascript"src="seam/resource/remoting/resource/remote.js"></script>
T he second contains the stubs and type definitions for the components you wish to call. T his is
generated dynamically, based on the local interface of your components, and includes type definitions for
all classes that can be used to call the remotable methods of the interface. T he script name reflects your
component name. For example, if you annotate a stateless session bean with
@ Nam e("custom erAction"), your script tag should look like this:
<script type="text/javascript"src="seam/resource/remoting/interface.js?
customerAction"></script>
If you want to access more than one component from the same page, include them all as parameters of
your script tag:
<script type="text/javascript"src="seam/resource/remoting/interface.js?
customerAction&accountAction"></script>
You can also use the s:rem ote tag to import the required JavaScript. Separate each component or
class name that you want to import with a comma:
<s:remote include="customerAction,accountAction"/>
23.2. The Seam object
Client-side component interaction is performed with the Seam JavaScript object defined in rem ote.js.
T his is used to make asynchronous calls against your component. It is split into two areas of
functionality: Seam .Com ponent contains methods for working with components and Seam .Rem oting
contains methods for executing remote requests. T he easiest way to become familiar with this object is
to start with a simple example.
23.2.1. A Hello World example
Procedure 23.1. Hello World Example
1. T o show you how the Seam object works, we will first create a new Seam component called
helloAction:
275
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
@Stateless
@Name("helloAction")
public class HelloAction {
@WebRemote
public String sayHello(String name) {
return "Hello, " + name;
}
}
T ake special note of the @ WebRem ote annotation, as it's required to make our method accessible
via remoting:
T hat's all the server-side code we need to write.
Note
If you are performing a persistence operation in the method marked @ WebRem ote you will
also need to add a @ T ransactional annotation to the method. Otherwise, your method
would execute outside of a transaction without this extra hint.T hat's because unlike a JSF
request, Seam does not wrap the remoting request in a transaction automatically.
2. Next, create a new web page and import the helloAction component:
<s:remote include="helloAction"/>
3. Add a button to the page to make this an interactive user experience:
<button onclick="javascript:sayHello()">Say Hello</button>
4. You will also need script that performs an action when the button is clicked:
<script type="text/javascript">
//<![CDATA[
function sayHello() {
var name = prompt("What is your name?");
Seam.Component.getInstance("helloAction").sayHello(name, sayHelloCallback);
}
function sayHelloCallback(result) {
alert(result);
}
//
]]>
</script>
5. Now deploy your application and browse to your page. Click the button, and enter a name when
prompted. A message box will display the "Hello" message, confirming the call's success. (You
can find the full source code for this Hello World example in Seam's
/exam ples/rem oting/helloworld directory.)
You can see from the JavaScript code listing that we have implemented two methods. T he first method
prompts the user for their name, and makes a remote request. Look at the following line:
Seam.Component.getInstance("helloAction").sayHello(name, sayHelloCallback);
T he first section of this line (Seam .Com ponent.getInstance("helloAction")) returns a proxy,
or stub, for our helloAction component. T he remainder of the line
(sayHello(nam e,sayHelloCallback);) invokes our component methods against the stub.
276
Chapter 23. Remoting
T he whole line invokes the sayHello method of our component, passing in nam e as a parameter. T he
second parameter, sayHelloCallback, is not a parameter of our component's sayHello method —
it tells the Seam Remoting framework that, once a response to the request is received, the response
should be passed to the sayHelloCallback JavaScript method. (T his callback parameter is optional;
you can leave it out if you are calling a method with a void return type, or if the result of the request is
not important.)
When the sayHelloCallback method receives the response to our remote request, it displays an
alert message with the result of our method call.
23.2.2. Seam.Component
T he Seam .Com ponent JavaScript object provides a number of client-side methods for working with
your Seam components. T he two main methods, newInstance() and getInstance() are
documented more thoroughly in the sections following. T he main difference between them is that
newInstance() will always create a new instance of a component type, and getInstance() will
return a singleton instance.
23.2.2.1. Seam.Component.newInstance()
Use this method to create a new instance of an entity or JavaBean component. T he object returned will
have the same getter/setter methods as its server-side counterpart. You can also access its fields
directly. For example:
@Name("customer")
@Entity
public class Customer implements Serializable
{
private Integer customerId;
private String firstName;
private String lastName;
@Column public Integer getCustomerId() {
return customerId;
}
public void setCustomerId(Integer customerId} {
this.customerId = customerId;
}
@Column public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
@Column public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
T o create a client-side Customer you would write the following code:
var customer = Seam.Component.newInstance("customer");
From here, you can set the fields of the customer object.
277
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
customer.setFirstName("John");
// Or you can set the fields directly
customer.lastName = "Smith";
23.2.2.2. Seam.Component.getInstance()
T he getInstance() method is used to refer to a Seam session bean component stub, which can then
be used to remotely execute methods against your component. T his method returns a singleton for the
specified component, so calling it twice in a row with the same component name will return the same
instance of the component.
T o continue the previous example, if we have created a new custom er and we want to save it, we pass
it to the saveCustom er() method of our custom erAction component:
Seam.Component.getInstance("customerAction").saveCustomer(customer);
23.2.2.3. Seam.Component.getComponentName()
Passing an object into this method returns the component name, if it is a component, or null if it is not.
if (Seam.Component.getComponentName(instance) == "customer")
alert("Customer");
else if (Seam.Component.getComponentName(instance) == "staff")
alert("Staff member");
23.2.3. Seam.Remoting
Most of the client side functionality for Seam Remoting is held within the Seam .Rem oting object. You
should not need to directly call many of its methods, but there are several that are useful:
23.2.3.1. Seam.Remoting.createT ype()
If your application contains or uses JavaBean classes that are not Seam components, you may need to
create these types on the client side to pass as parameters into your component method. Use the
createT ype() method to create an instance of your type. Pass in the fully-qualified Java class name
as a parameter:
var widget = Seam.Remoting.createType("com.acme.widgets.MyWidget");
23.2.3.2. Seam.Remoting.getT ypeName()
T his method is the non-component equivalent of Seam .Com ponent.getCom ponentNam e(). It
returns the name of the type for an object instance, or null if the type is not known. T he name is the
fully-qualified name of the type's Java class.
23.3. Evaluating EL Expressions
Seam Remoting also supports EL expression evaluation, which is another convenient method of
retrieving data from the server. T he Seam .Rem oting.eval() function lets the EL expression be
remotely evaluated on the server, and returns the resulting value to a client-side callback method. T his
function accepts two parameters: the EL expression to evaluate, and the callback method to invoke with
the expression value. For example:
278
Chapter 23. Remoting
function customersCallback(customers) {
for (var i = 0; i < customers.length; i++) {
alert("Got customer: " + customers[i].getName());
}
}
Seam.Remoting.eval("#{customers}", customersCallback);
Here, Seam evaluates the #{custom ers} expression, and the value of the expression (in this case, a
list of Custom er objects) is returned to the custom ersCallback() method. Remember, objects
returned this way must have their types imported with s:rem ote for you to work with them in JavaScript.
T o work with a list of custom er objects, you must be able to import the custom er type:
<s:remote include="customer"/>
23.4. Client Interfaces
In the previous configuration section, the stub for our component is imported into our page with either
seam /resource/rem oting/interface.js, or with the s:rem ote tag:
<script type="text/javascript"src="seam/resource/remoting/interface.js?
customerAction">
</script>
<s:remote include="customerAction"/>
Including this script generates the interface definitions for our component, plus any other components or
types required to execute the methods of our component, and makes them available for the remoting
framework's use.
T wo types of stub can be generated: executable stubs, and type stubs. Executable stubs are behavioral,
and execute methods against your session bean components. T ype stubs contain state, and represent
the types that can be passed in as parameters or returned as results.
T he type of stub that is generated depends upon the type of your Seam component. If the component is
a session bean, an executable stub will be generated. If it is an entity or JavaBean, a type stub will be
generated. However, if your component is a JavaBean and any of its methods are annotated with
@ WebRem ote, an executable stub will be generated. T his lets you call your JavaBean component's
methods in a non-EJB environment, where you do not have access to session beans.
23.5. The Context
T he Seam Remoting Context contains additional information that is sent and received as part of a
remoting request or response cycle. At this point, it contains only the conversation ID, but may be
expanded in future.
23.5.1. Setting and reading the Conversation ID
If you intend to use remote calls within a conversation's scope, then you must be able to read or set the
conversation ID in the Seam Remoting context. T o read the conversation ID after making a remote
request, call Seam .Rem oting.getContext().getConversationId(). T o set the conversation ID
before making a request, call Seam .Rem oting.getContext().setConversationId().
If the conversation ID has not been explicitly set with
Seam .Rem oting.getContext().setConversationId(), then the first valid conversation ID
returned by any remoting call is assigned automatically. If you are working with multiple conversations
within your page, you may need to set your conversation ID explicitly before each call. Single
279
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
conversations do not require explicit ID setting.
23.5.2. Remote calls within the current conversation scope
Under some circumstances, you may need to make a remote call within the scope of the current view's
conversation. T o do so, you must explicitly set the conversation ID to that of the view before making the
remote call. T he following JavaScript will set the conversation ID being used for remote calls to the
current view's conversation ID:
Seam.Remoting.getContext().setConversationId( #{conversation.id} );
23.6. Batch Requests
Seam Remoting lets you execute multiple component calls with a single request. We recommend using
this feature when you need to reduce network traffic.
T he Seam .Rem oting.startBatch() method starts a new batch. Any component calls executed after
starting a batch are queued, rather than being sent immediately. When all the desired component calls
have been added to the batch, the Seam .Rem oting.executeBatch() method sends a single
request containing all of the queued calls to the server, where they will be executed in order. After the
calls have been executed, a single response containing all return values is returned to the client, and the
callback functions are triggered in their execution order.
If you begin a batch, and then decide you do not want to send it, the
Seam .Rem oting.cancelBatch() method discards any queued calls and exits the batch mode.
For an example of batch use, see /exam ples/rem oting/chatroom .
23.7. Working with Data types
23.7.1. Primitives / Basic Types
T his section describes the support for basic data types. On the server side, these values are generally
compatible with either their primitive type, or their corresponding wrapper class.
23.7.1.1. String
Use JavaScript String objects to set String parameter values.
23.7.1.2. Number
Seam supports all Java-supported number types. On the client side, number values are always
serialized as their String representation. T hey are converted to the correct destination type on the
server side. Conversion into either a primitive or wrapper type is supported for Byte, Double, Float,
Integer, Long and Short types.
23.7.1.3. Boolean
Booleans are represented client-side by JavaScript Boolean values, and server-side by a Java Boolean.
23.7.2. JavaBeans
In general, these are either Seam entity or JavaBean components, or some other non-component class.
Use the appropriate method to create a new instance of the object —
Seam .Com ponent.newInstance() for Seam components, or Seam .Rem oting.createT ype() for
anything else.
Only objects created by either of these two methods should be used as parameter values, where the
parameter is not one of the preexisting valid types. You may encounter component methods where the
exact parameter type cannot be determined, such as:
280
Chapter 23. Remoting
@Name("myAction")
public class MyAction implements MyActionLocal {
public void doSomethingWithObject(Object obj) {
// code
}
}
In this case, the interface for m yAction will not include m yWidget, because it is not directly referenced
by any of its methods. T herefore, you cannot pass in an instance of your m yWidget component unless
you import it explicitly:
<s:remote include="myAction,myWidget"/>
T his allows a m yWidget object to be created with Seam .Com ponent.newInstance("m yWidget"),
which can then be passed to m yAction.doSom ethingWithObject().
23.7.3. Dates and Times
Date values are serialized into a String representation that is accurate to the millisecond. On the client
side, use a JavaScript Date object to work with date values. On the server side, use any
java.util.Date class (or a descendant class, such as java.sql.Date or
java.sql.T im estam p.)
23.7.4. Enums
On the client side, enums are treated similarly to Strings. When setting the value for an enum parameter,
use the String representation of the enum. T ake the following component as an example:
@Name("paintAction")
public class paintAction implements paintLocal {
public enum Color {red, green, blue, yellow, orange, purple};
public void paint(Color color) {
// code
}
}
T o call the paint() method with the color red, pass the parameter value as a String literal:
Seam.Component.getInstance("paintAction").paint("red");
T he inverse is also true. T hat is, if a component method returns an enum parameter (or contains an
enum field anywhere in the returned object graph), then on the client-side it will be represented as a
String.
23.7.5. Collections
23.7.5.1. Bags
Bags cover all collection types, including arrays, collections, lists, and sets, but excluding maps — see
the section following. T hey are implemented client-side as a JavaScript array, both when called and
returned. T he remoting framework on the server side can convert the bag to an appropriate type for the
component method call.
23.7.5.2. Maps
T he Seam Remoting framework provides simple map support where no native support is available in
JavaScript. T o create a map that can be used as a parameter to a remote call, create a new
Seam .Rem oting.Map object:
var map = new Seam.Remoting.Map();
281
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
T his JavaScript implementation provides basic methods for working with Maps: size(), isEm pty(),
keySet(), values(), get(key), put(key, value), rem ove(key) and contains(key). Each of
these methods is equivalent to the Java method of the same name. Where the method returns a
collection, as in keySet() and values(), a JavaScript array object will be returned that contains the
key or value objects (respectively).
23.8. Debugging
T o help you track down bugs, you can enable a debug mode, which displays the contents of all packets
sent between client and server in a pop-up window. T o enable debug mode, either execute the
setDebug() method in JavaScript, like so:
Seam.Remoting.setDebug(true);
Or configure it in com ponents.xm l:
<remoting:remoting debug="true"/>
T o turn off debug mode, call setDebug(false). If you want to write your own messages to the debug
log, call Seam .Rem oting.log(m essage).
23.9. Handling Exceptions
When invoking a remote component method, you can specify an exception handler to process the
response in the event of an exception during component invocation. T o specify an exception handler
function, include a reference to it after the callback parameter in your JavaScript:
var callback = function(result) { alert(result); };
var exceptionHandler = function(ex) { alert("An exception occurred: " +
ex.getMessage()); };
Seam.Component.getInstance("helloAction").sayHello(name, callback,
exceptionHandler);
If you do not have a callback handler defined, you must specify null in its place:
var exceptionHandler = function(ex) { alert("An exception occurred: " +
ex.getMessage()); };
Seam.Component.getInstance("helloAction").sayHello(name, null, exceptionHandler);
T he exception object that is passed to the exception handler exposes one method, getMessage(),
which returns the exception message belonging to the exception thrown by the @ WebRem ote method.
23.10. The Loading Message
You can modify, define custom rendering for, or even remove the default loading message that appears
in the top right corner of the screen.
23.10.1. Changing the message
T o change the message from the default "Please Wait...", set the value of
Seam .Rem oting.loadingMessage:
Seam.Remoting.loadingMessage = "Loading...";
23.10.2. Hiding the loading message
T o completely suppress the display of the loading message, override the implementation of
displayLoadingMessage() and hideLoadingMessage() with actionless functions:
282
Chapter 23. Remoting
// don't display the loading indicator
Seam.Remoting.displayLoadingMessage = function() {};
Seam.Remoting.hideLoadingMessage = function() {};
23.10.3. A Custom Loading Indicator
It is also possible to override the loading indicator to display an animated icon, or anything else that you
want. T o do so, override the displayLoadingMessage() and hideLoadingMessage() messages
with your own implementations:
Seam.Remoting.displayLoadingMessage = function() {
// Write code here to display the indicator
};
Seam.Remoting.hideLoadingMessage = function() {
// Write code here to hide the indicator
};
23.11. Controlling what data is returned
When a remote method is executed, the result is serialized into an XML response, which is returned to
the client. T his response is then unmarshaled by the client into a JavaScript object. For complex types
(such as JavaBeans) that include references to other objects, all referenced objects are also serialized
as part of the response. T hese objects can reference other objects, which can reference other objects,
and so on — so, if left unchecked, this object "graph" can be enormous.
For this reason, and to prevent sensitive information being exposed to the client, Seam Remoting lets
you constrain the object graph by specifying the exclude field of the remote method's @ WebRem ote
annotation. T his field accepts a String array containing one or more paths specified with dot notation.
When invoking a remote method, the objects in the result's object graph that match these paths are
excluded from the serialized result packet.
T he examples that follow are all based on this Widget class:
@Name("widget")
public class Widget {
private String value;
private String secret;
private Widget child;
private Map<String,Widget> widgetMap;
private List<Widget> widgetList;
// getters and setters for all fields
}
23.11.1. Constraining normal fields
If your remote method returns an instance of Widget, but you do not want to expose the secret field
because it contains sensitive information, you would constrain it like so:
@WebRemote(exclude = {"secret"})
public Widget getWidget();
T he value "secret" refers to the secret field of the returned object.
Now, note that the returned Widget value has a field child that is also a Widget. If we want to hide
the child's secret value, rather than the field itself, we can use dot notation to specify this field's path
within the result object's graph:
@WebRemote(exclude = {"child.secret"})
public Widget getWidget();
283
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
23.11.2. Constraining Maps and Collections
Objects within an object graph can also exist in a Map or a Collection (that is, a List, a Set, an Array,
etc.). Collections are treated like any other field — for example, if our Widget contained a list of other
Widgets in its widgetList field, we would constrain the secret field of the Widgets in this list with
the following notation:
@WebRemote(exclude = {"widgetList.secret"})
public Widget getWidget();
T o constrain a Map's key or value, the notation is slightly different. Appending [key] after the Map's
field name constrains the Map's key object values, while [value] constrains the value object values.
T he following example demonstrates how the values of the widgetMap field have their secret field
constrained:
@WebRemote(exclude = {"widgetMap[value].secret"})
public Widget getWidget();
23.11.3. Constraining objects of a specific type
You can use square brackets to constrain the fields of an object type regardless of its location in the
object graph. If the object is a Seam component, use the name of the component; if not, use the fullyqualified class name, like so:
@WebRemote(exclude = {"[widget].secret"})
public Widget getWidget();
23.11.4. Combining Constraints
Constraints can also be combined to filter objects from multiple paths within the object graph:
@WebRemote(exclude = {"widgetList.secret", "widgetMap[value].secret"})
public Widget getWidget();
23.12. Transactional Requests
By default, no transaction is active during a remoting request. If you wish to update the database during
a remoting request, you must annotate the @ WebRem ote method with @ T ransactional, like so:
@WebRemote @Transactional(TransactionPropagationType.REQUIRED)
public void updateOrder(Order order) {
entityManager.merge(order);
}
23.13. JMS Messaging
Seam Remoting provides experimental support for JMS Messaging. T his section describes currentlyimplemented JMS support. Note that this may change in the future. At present, we do not recommend
using this feature within a production environment.
23.13.1. Configuration
Before you can subscribe to a JMS topic, you must first configure a list of the topics that Seam Remoting
can subscribe to. List the topics under
org.jboss.seam .rem oting.m essaging.subscriptionRegistry. allowedT opics in
seam .properties, web.xm l or com ponents.xm l:
<remoting:remoting poll-timeout="5" poll-interval="1"/>
284
Chapter 23. Remoting
23.13.2. Subscribing to a JMS Topic
T he following example demonstrates how to subscribe to a JMS T opic:
function subscriptionCallback(message) {
if (message instanceof Seam.Remoting.TextMessage)
alert("Received message: " + message.getText());
}
Seam.Remoting.subscribe("topicName", subscriptionCallback);
T he Seam .Rem oting.subscribe() method accepts two parameters: the name of the JMS topic to
subscribe to, and the callback function to invoke when a message is received.
T wo message types are supported: T ext messages, and Object messages. T o test for the message
type that is passed to your callback function, use the instanceof operator. T his tests whether the
message is a Seam .Rem oting.T extMessage or Seam .Rem oting.ObjectMessage. A
T extMessage contains the text value in its text field. (You can also fetch this value by calling the
object's getT ext() method.) An ObjectMessage contains its object value in its value field. (You can
also fetch this value by calling the object's getValue() method.)
23.13.3. Unsubscribing from a Topic
T o unsubscribe from a topic, call Seam .Rem oting.unsubscribe() and pass in the topic name:
Seam.Remoting.unsubscribe("topicName");
23.13.4. Tuning the Polling Process
Polling can be controlled and modified with two parameters.
Seam .Rem oting.pollInterval controls how long to wait between subsequent polls for new
messages. T his parameter is expressed in seconds, and its default setting is 10.
Seam .Rem oting.pollT im eout is also expressed in seconds. It controls how long a request to the
server should wait for a new message before timing out and sending an empty response. Its default is 0
seconds, which means that when the server is polled, if there are no messages ready for delivery, an
empty response will be immediately returned.
Use caution when setting a high pollT im eout value. Each request that has to wait for a message
uses a server thread until either the message is received, or the request times out. If many such
requests are served simultaneously, a large number of server threads will be used.
We recommend setting these options in com ponents.xm l, but they can be overridden with JavaScript
if desired. T he following example demonstrates a more aggressive polling method. Set these parameters
to values that suit your application:
In com ponents.xm l:
<remoting:remoting poll-timeout="5" poll-interval="1"/>
With Java:
// Only wait 1 second between receiving a poll response and sending the next poll
request.
Seam.Remoting.pollInterval = 1;
// Wait up to 5 seconds on the server for new messages
Seam.Remoting.pollTimeout = 5;
285
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
286
Chapter 24. Seam and the Google Web Toolkit
Chapter 24. Seam and the Google Web Toolkit
Google Web Toolkit integration is a Technology Preview
T echnology 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 T echnology Preview features generally available, we will
provide commercially reasonable efforts to resolve any reported issues that customers
experience when using these features.
If you prefer to develop dynamic AJAX (Asynchronous Java and XML) applications with the Google Web
T oolkit (GWT ), Seam provides an integration layer that allows GWT widgets to interact directly with
Seam components.
In this section, we assume you are already familiar with GWT T ools, and focus only on the Seam
integration. You can find more information at http://code.google.com/webtoolkit/ .
24.1. Configuration
You do not need to make any configuration changes to use GWT in a Seam application — all you need
to do is install the Seam Resource Servlet. See Chapter 27, Configuring Seam and packaging Seam
applications for details.
24.2. Preparing your component
T o prepare a Seam component to be called with GWT , you must first create both synchronous and
asynchronous service interfaces for the methods you wish to call. Both interfaces should extend the
GWT interface com .google.gwt.user.client.rpc.Rem oteService:
public interface MyService extends RemoteService {
public String askIt(String question);
}
T he asynchronous interface should be identical, except for an additional AsyncCallback parameter
for each of the methods it declares:
public interface MyServiceAsync extends RemoteService {
public void askIt(String question, AsyncCallback callback);
}
T he asynchronous interface (in this case, MyServiceAsync) is implemented by GWT , and should
never be implemented directly.
T he next step is to create a Seam component that implements the synchronous interface:
287
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
@Name("org.jboss.seam.example.remoting.gwt.client.MyService")
public class ServiceImpl implements MyService {
@WebRemote
public String askIt(String question) {
if (!validate(question)) {
throw new IllegalStateException("Hey, this should not happen, " +
"I checked on the client, but " +
"it's always good to double check.");
}
return "42. Its the real question that you seek now.";
}
public boolean validate(String q) {
ValidationUtility util = new ValidationUtility();
return util.isValid(q);
}
}
T he Seam component's name must match the fully-qualified name of the GWT client interface (as
shown), or the Seam Resource Servlet will not be able to find it when a client makes a GWT call.
Methods that GWT will make accessible must be annotated with @ WebRem ote.
24.3. Hooking up a GWT widget to the Seam component
Next, write a method that returns the asynchronous interface to the component. T his method can be
located inside the widget class, and will be used by the widget to obtain a reference to the asynchronous
client stub:
private MyServiceAsync getService() {
String endpointURL = GWT.getModuleBaseURL() + "seam/resource/gwt";
MyServiceAsync svc = (MyServiceAsync) GWT.create(MyService.class);
((ServiceDefTarget) svc).setServiceEntryPoint(endpointURL);
return svc;
}
Finally, write the widget code that invokes the method on the client stub. T he following example creates a
simple user interface with a label, text input, and a button:
288
Chapter 24. Seam and the Google Web Toolkit
public class AskQuestionWidget extends Composite {
private AbsolutePanel panel = new AbsolutePanel();
public AskQuestionWidget() {
Label lbl = new Label("OK, what do you want to know?");
panel.add(lbl);
final TextBox box = new TextBox();
box.setText("What is the meaning of life?");
panel.add(box);
Button ok = new Button("Ask");
ok.addClickListener(new ClickListener() {
public void onClick(Widget w) {
ValidationUtility valid = new ValidationUtility();
if (!valid.isValid(box.getText())) {
Window.alert("A question has to end with a '?'");
} else {
askServer(box.getText());
}
}
});
panel.add(ok);
initWidget(panel);
}
private void askServer(String text) {
getService().askIt(text, new AsyncCallback() {
public void onFailure(Throwable t) {
Window.alert(t.getMessage());
}
public void onSuccess(Object data) {
Window.alert((String) data);
}
});
}
...
When clicked, this button invokes the askServer() method, passing the contents of the input text. In
this example, it also validates that the input is a valid question. T he askServer() method acquires a
reference to the asynchronous client stub (returned by the getService() method) and invokes the
askIt() method. T he result (or error message, if the call fails) is shown in an alert window.
24.4. GWT Ant Targets
T o deploy GWT applications, you must also perform compilation to JavaScript. T his compacts and
obfuscates the code. You can use an Ant utility instead of the command line or GUI utility provided by
GWT . T o do so, you must have GWT downloaded, and the Ant task JAR in your Ant classpath.
Place the following near the top of your Ant file:
289
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<taskdef uri="antlib:de.samaflost.gwttasks"
resource="de/samaflost/gwttasks/antlib.xml"
classpath="./lib/gwttasks.jar"/>
<property file="build.properties"/>
Create a build.properties file containing:
gwt.home=/gwt_home_dir
T his must point to the directory in which GWT is installed. Next, create a target:
<!-- the following are handy utilities for doing GWT development.
To use GWT, you will of course need to download GWT seperately -->
<target name="gwt-compile">
<!-- in this case, we are "re homing" the gwt generated stuff, so
in this case we can only have one GWT module - we are doing this
deliberately to keep the URL short -->
<delete>
<fileset dir="view"/>
</delete>
<gwt:compile outDir="build/gwt"
gwtHome="${gwt.home}"
classBase="${gwt.module.name}"
sourceclasspath="src"/>
<copy todir="view">
<fileset dir="build/gwt/${gwt.module.name}"/>
</copy>
</target>
When called, this target compiles the GWT application and copies it to the specified directory (likely in
the webapp section of your WAR).
Note
Never edit the code generated by gwt-com pile — if you need to edit, do so in the GWT source
directory.
We highly recommend using the hosted mode browser included in the GWT if you plan to develop
applications with the GWT .
24.5. GWT Maven plugin
For a deployment of GWT apps, there is a set of maven GWT goals which does everything what GWT
supports. T he maven-gwt-plugin usage is in more details at GWT .
Basic set up is for instance here:
290
Chapter 24. Seam and the Google Web Toolkit
<build>
<plugins>
[...]
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>gwt-maven-plugin</artifactId>
<version>1.2</version>
<!-- version 1.2 allows us to specify gwt version by gwt-user dependency -->
<configuration>
<generateDirectory>${project.build.outoutDirectory}/${project.build.finalName}</ge
nerateDirectory>
<inplace>false</inplace>
<logLevel>TRACE</logLevel>
<extraJvmArgs>-Xmx512m -DDEBUG</extraJvmArgs>
<soyc>false</soyc>
</configuration>
<executions>
<execution>
<goals>
<goal>resources</goal>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
[...]
</plugins>
[...]
</build>
More can be seen here http://mojo.codehaus.org/gwt-maven-plugin/user-guide/compile.html
291
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Chapter 25. Spring Framework integration
T he Spring Framework is part of the Seam inversion-of-control (IoC) module. It allows easy migration of
Spring-based projects to Seam, and benefits Spring applications with Seam features, such as
conversations and a more sophisticated persistence context management.
Note
T he Spring integration code is included in the jboss-seam -ioc library. T his library is a required
dependency for all Seam-Spring integration techniques covered in this chapter.
Seam's support for Spring gives you:
Seam component injection into Spring beans,
Spring bean injection into Seam components,
Spring bean to Seam component transformation,
the ability to place Spring beans in any Seam context,
the ability to start a spring WebApplicationContext with a Seam component,
support for using Spring PlatformT ransactionManagement with your Seam-based applications,
support for using a Seam-managed replacement for Spring's OpenEntityManagerInViewFilter
and OpenSessionInViewFilter, and
support for backing @ Asynchronous calls with Spring T askExecutors.
25.1. Injecting Seam components into Spring beans
Inject Seam component instances into Spring beans with the <seam :instance/>
namespace handler. T o enable the Seam namespace handler, the Seam namespace must first be added
to the Spring beans definition file:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:seam="http://jboss.org/schema/seam/spring-seam"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://jboss.org/schema/seam/spring-seam
http://jboss.org/schema/seam/spring-seam-2.3.xsd">
Any Seam component can now be injected into any Spring bean, like so:
<bean id="someSpringBean" class="SomeSpringBeanClass" scope="prototype">
<property name="someProperty">
<seam:instance name="someComponent"/>
</property>
</bean>
You can use an EL expression instead of a component name:
<bean id="someSpringBean" class="SomeSpringBeanClass" scope="prototype">
<property name="someProperty">
<seam:instance name="#{someExpression}"/>
</property>
</bean>
You can inject a Seam component instance into a Spring bean by using a Spring bean ID, like so:
292
Chapter 25. Spring Framework integration
<seam:instance name="someComponent" id="someSeamComponentInstance"/>
<bean id="someSpringBean" class="SomeSpringBeanClass" scope="prototype">
<property name="someProperty" ref="someSeamComponentInstance">
</bean>
However, Spring, unlike Seam, was not designed to support a stateful component model with multiple
contexts. Spring injection does not occur at method invocation time, but when the Spring bean is
instantiated.
T he instance available when the bean is instantiated will be used for the entire life of the bean. Say you
inject a Seam conversation-scoped component instance directly into a singleton Spring bean — that
singleton will hold a reference to the same instance long after the conversation is over. T his is called
scope impedance.
Seam bijection maintains scope impedance naturally as an invocation flows through the system. In
Spring, we must inject a proxy of the Seam component, and resolve the reference when the proxy is
invoked.
T he <seam :instance/>
tag lets us automatically proxy the Seam component.
<seam:instance id="seamManagedEM"
name="someManagedEMComponent"
proxy="true"/>
<bean id="someSpringBean" class="SomeSpringBeanClass">
<property name="entityManager" ref="seamManagedEM">
</bean>
Here, we see one example of using a Seam-managed persistence context from a Spring bean. See the
section on Section 25.6, “Using a Seam-Managed Persistence Context in Spring” for a more robust way
to use Seam-managed persistence contexts as a replacement for the Spring
OpenEntityManagerInView filter.
25.2. Injecting Spring beans into Seam components
You can inject a Spring bean into a Seam component instance either by using an EL expression, or by
making the Spring bean a Seam component.
T he simplest approach is to access the Spring beans with EL.
T he Spring DelegatingVariableResolver assists Spring integration with JavaServer Faces (JSF).
T his VariableResolver uses EL with bean IDs to make Spring beans available to JSF. You will need
to add the DelegatingVariableResolver to faces-config.xm l:
<application>
<variable-resolver>
org.springframework.web.jsf.DelegatingVariableResolver
</variable-resolver>
</application>
You can then inject Spring beans using @ In:
@In("#{bookingService}")
private BookingService bookingService;
Spring beans are not limited to injection. T hey can be used wherever EL expressions are used in Seam.
293
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
25.3. Making a Spring bean into a Seam component
T he <seam :com ponent/>
namespace handler can be used to transform any Spring bean into a Seam component. Just add the
<seam :com ponent/>
tag to the declaration of the bean that you want to make into a Seam component:
<bean id="someSpringBean" class="SomeSpringBeanClass" scope="prototype">
<seam:component/>
</bean>
By default, <seam :com ponent/>
creates a stateless Seam component with the class and name provided in the bean definition.
Occasionally — when a FactoryBean is used, for example — the Spring bean class may differ from
the class listed in the bean definition. In this case, specify the class explicitly. You should also explicitly
specify a Seam component name where there is a potential naming conflict.
If you want the Spring bean to be managed in a particular Seam scope, use the scope attribute of
<seam :com ponent/>
. If the Seam scope specified is anything other than ST AT ELESS, you must scope your Spring bean to
prototype. Pre-existing Spring beans usually have a fundamentally stateless character, so this
attribute is not usually necessary.
25.4. Seam-scoped Spring beans
With the Seam integration package, you can also use Seam's contexts as Spring 2.0-style custom
scopes, which lets you declare any Spring bean in any Seam context. However, because Spring's
component model was not built to support statefulness, this feature should be used with care. In
particular, there are problems with clustering session- or conversation-scoped Spring beans, and care
must be taken when injecting a bean or component from a wider scope into a bean of narrower scope.
Specify <seam :configure-scopes/>
in a Spring bean factory configuration to make all Seam scopes available to Spring beans as custom
scopes. T o associate a Spring bean with a particular Seam scope, specify the desired scope in the
scope attribute of the bean definition.
<!-- Only needs to be specified once per bean factory-->
<seam:configure-scopes/>
...
<bean id="someSpringBean" class="SomeSpringBeanClass"
scope="seam.CONVERSATION"/>
You can change the scope name's prefix by specifying the prefix attribute in the configurescopes definition. (T he default prefix is seam ..)
By default, a Spring component instance that is registered this way is not created automatically when
referenced with @ In. T o automatically create an instance, you must either specify @ In(create=true)
at the injection point (to auto-create a specific bean), or use the default-auto-create attribute of
configure-scopes to auto-create all Seam-scoped Spring beans.
T he latter approach lets you inject Seam-scoped Spring beans into other Spring beans without using
<seam :instance/>
. However, you must be careful to maintain scope impedance. Normally, you would specify
<aop:scoped-proxy/>
in the bean definition, but Seam-scoped Spring beans are not compatible with <aop:scoped-proxy/>
. T herefore, to inject a Seam-scoped Spring bean into a singleton, use <seam :instance/>
:
294
Chapter 25. Spring Framework integration
<bean id="someSpringBean" class="SomeSpringBeanClass" scope="seam.CONVERSATION"/>
...
<bean id="someSingleton">
<property name="someSeamScopedSpringBean">
<seam:instance name="someSpringBean" proxy="true"/>
</property>
</bean>
25.5. Using Spring PlatformTransactionManagement
Spring's extensible transaction management provides support for many transaction APIs, including the
Java Persistence API (JPA), Hibernate, Java Data Objects (JDO), and Java T ransaction API (JT A). It
also exposes support for many advanced features such as nested transactions. Spring also provides
tight integration with many application server T ransactionManagers, such as Websphere and Weblogic,
and supports full Java EE transaction propagation rules, such as REQUIRES_NEW and
NOT _SUPPORT ED.
T o configure Seam to use Spring transactions, enable the SpringT ransaction component, like so:
<spring:spring-transaction
platform-transaction-manager="#{transactionManager}"/>
T he spring:spring-transaction component will utilizes Spring's transaction synchronization
capabilities for synchronization callbacks.
25.6. Using a Seam-Managed Persistence Context in Spring
Some of Seam's most powerful features are its conversation scope, and the ability to keep an
EntityManager open for the life of a conversation. T hese eliminate many problems associated with
detaching and reattaching entities, and mitigate the occurrence of LazyInitializationException.
Spring does not provide a way to manage persistence contexts beyond the scope of a single web
request (OpenEntityManagerInViewFilter).
Seam brings conversation-scoped persistence context capabilities to Spring applications by allowing
Spring developers to access a Seam-managed persistence context with the JPA tools provided with
Spring (PersistenceAnnotationBeanPostProcessor, JpaT em plate, etc.)
T his integration work provides:
transparent access to a Seam-managed persistence context using Spring-provided tools
access to Seam conversation-scoped persistence contexts in a non-web request — for example, an
asynchronous Quartz job
the ability to use Seam-managed persistence contexts with Spring-managed transactions. T his
requires manual flushing of the persistent context.
Spring's persistence context propagation model allows only one open EntityManager per
EntityManagerFactory, so the Seam integration works by wrapping an EntityManagerFactory
around a Seam-managed persistence context, like so:
<bean id="seamEntityManagerFactory"
class="org.jboss.seam.ioc.spring.SeamManagedEntityManagerFactoryBean">
<property name="persistenceContextName" value="entityManager"/>
</bean>
Here, persistenceContextNam e is the name of the Seam-managed persistence context component.
By default, this EntityManagerFactory has a unitNam e equal to the Seam component name — in
this case, entityManager. If you wish to provide a different unitNam e, you can provide a
295
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
persistenceUnitNam e like so:
<bean id="seamEntityManagerFactory"
class="org.jboss.seam.ioc.spring.SeamManagedEntityManagerFactoryBean">
<property name="persistenceContextName" value="entityManager"/>
<property name="persistenceUnitName" value="bookingDatabase:extended"/>
</bean>
T his EntityManagerFactory can now be used in any Spring-provided tools; in this case, you can
use Spring's PersistenceAnnotationBeanPostProcessor just as you would in Spring.
<bean class="org.springframework.orm.jpa.support
.PersistenceAnnotationBeanPostProcessor"/>
If you define your real EntityManagerFactory in Spring, but wish to use a Seam-managed
persistence context, you can tell the PersistenceAnnotationBeanPostProcessor your desired
default persistenctUnitNam e by specifying the defaultPersistenceUnitNam e property.
T he applicationContext.xm l might look like:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="bookingDatabase"/>
</bean>
<bean id="seamEntityManagerFactory"
class="org.jboss.seam.ioc.spring.SeamManagedEntityManagerFactoryBean">
<property name="persistenceContextName" value="entityManager"/>
<property name="persistenceUnitName" value="bookingDatabase:extended"/>
</bean>
<bean class="org.springframework.orm.jpa
.support.PersistenceAnnotationBeanPostProcessor">
<property name="defaultPersistenceUnitName"
value="bookingDatabase:extended"/>
</bean>
T he com ponent.xm l might look like:
<persistence:managed-persistence-context name="entityManager"
auto-create="true" entity-manager-factory="#{entityManagerFactory}"/>
JpaT em plate and JpaDaoSupport have an identical configuration in a Spring-based persistence
context and in a normal Seam-managed persistence context.
<bean id="bookingService"
class="org.jboss.seam.example.spring.BookingService">
<property name="entityManagerFactory" ref="seamEntityManagerFactory"/>
</bean>
25.7. Using a Seam-Managed Hibernate Session in Spring
Spring integration into Seam also provides support for complete Spring tool access to a Seam-managed
Hibernate session. T his integration is very similar to the JPA integration — see Section 25.6, “Using a
Seam-Managed Persistence Context in Spring” for details.
Spring's propagation model allows only one open EntityManager per EntityManagerFactory to
be available to Spring tools, so Seam integrates by wrapping a proxy SessionFactory around a
Seam-managed Hibernate session context.
296
Chapter 25. Spring Framework integration
<bean id="seamSessionFactory"
class="org.jboss.seam.ioc.spring.SeamManagedSessionFactoryBean">
<property name="sessionName" value="hibernateSession"/>
</bean>
Here, sessionNam e is the name of the persistence:m anaged-hibernate-session component.
T his SessionFactory can then be used with any Spring-provided tool. T he integration also provides
support for calls to SessionFactory.getCurrentInstance(), provided that
getCurrentInstance() is called on the Seam ManagedSessionFactory.
25.8. Spring Application Context as a Seam Component
Although it is possible to use the Spring ContextLoaderListener to start your application's Spring
ApplicationContext, there are some limitations: the Spring ApplicationContext must be
started after the Seam Listener, and starting a Spring ApplicationContext for use in Seam unit
and integration tests can be complicated.
T o overcome these limitations, the Spring integration includes a Seam component that can start a Spring
ApplicationContext. T o use this component, place the <spring:context-loader/>
definition in the com ponents.xm l file. Specify your Spring context file location in the configlocations attribute. If more than one configuration file is required, you can place them in the nested
<spring:config-locations/>
element, as per standard com ponents.xm l multi-value practices.
<components xmlns="http://jboss.org/schema/seam/components"
xmlns:spring="http://jboss.org/schema/seam/spring"
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/spring
http://jboss.org/schema/seam/spring-2.3.xsd">
<spring:context-loader config-locations=
"/WEB-INF/applicationContext.xml"/>
</components>
25.9. Using a Spring TaskExecutor for @Asynchronous
Spring provides an abstraction for executing code asynchronously, called a T askExecutor. T he
Spring-Seam integration lets you use a Spring T askExecutor to execute immediate @ Asynchronous
method calls. T o enable this functionality, install the SpringT askExecutorDispatchor and provide
a Spring -bean defined taskExecutor like so:
<spring:task-executor-dispatcher
task-executor="#{springThreadPoolTaskExecutor}"/>
Because a Spring T askExecutor does not support scheduling asynchronous events, you can provide
handling with a fallback Seam Dispatcher, like so:
297
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<!-Install a ThreadPoolDispatcher to handle scheduled asynchronous event
-->
<core:thread-pool-dispatcher name="threadPoolDispatcher"/>
<!-- Install the SpringDispatcher as default -->
<spring:task-executor-dispatcher
task-executor="#{springThreadPoolTaskExecutor}"
schedule-dispatcher="#{threadPoolDispatcher}"/>
298
Chapter 26. Hibernate Search
Chapter 26. Hibernate Search
26.1. Introduction
Full text search engines like Apache™ Lucene™ bring full text and efficient queries to applications.
Hibernate Search, which makes use of Apache Lucene, can index your domain model with a few added
annotations, handle database or index synchronization, and return regular managed objects that are
matched by full text queries. T here are some limitations to dealing with an object domain model over a
text index — such as maintaining index accuracy, consistency between index structure and the domain
model, and avoiding query mismatches — but these limitations are far outweighed by the advantages of
speed and efficiency.
Hibernate Search has been designed to integrate as naturally as possible with the Java Persistence API
(JPA) and Hibernate. As a natural extension, JBoss Seam provides Hibernate Search integration.
Refer to the Hibernate Search guide distributed with this Red Hat JBoss Web Framework Kit for
information specific to the Hibernate Search project.
26.2. Configuration
Hibernate Search is configured either in the MET A-INF/persistence.xm l or hibernate.cfg.xm l
file.
Hibernate Search configuration has sensible defaults for most configuration parameters. T he following is
an example of a minimal persistence unit configuration:
<persistence-unit name="sample">
<jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
<properties>
[...]
<!-- use a file system based index -->
<property name="hibernate.search.default.directory_provider"
value="filesystem"/>
<!-- directory where the indexes will be stored -->
<property name="hibernate.search.default.indexBase"
value="/Users/prod/apps/dvdstore/dvdindexes"/>
</properties>
</persistence-unit>
T he following JARs must be deployed alongside the configuration file:
hibernate-search.jar
hibernate-search-orm.jar
hibernate-search-engine.jar
lucene-core.jar
Maven coordinates for using Hibernate Search:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-search</artifactId>
<version>4.3.0.Final-redhat-wfk-2</version>
</dependency>
Some Hibernate Search extensions require additional dependencies. Commonly used is hibernatesearch-analyzers.jar. For details, see your Hibernate Search documentation for details.
299
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Note
If you deploy these in an EAR, remember to update application.xm l.
26.3. Usage
Hibernate Search uses annotations to map entities to a Lucene index.
Hibernate Search is completely integrated with the API, and semantic of JPA and Hibernate. Switching
from a HQL- or Criteria-based query requires little code. T he application interacts primarily with the
FullT extSession API, which is a subclass of Hibernate's Session.
When Hibernate Search is present, JBoss Seam injects a FullT extSession:
@Stateful
@Name("search")
public class FullTextSearchAction implements FullTextSearch, Serializable {
@In FullTextSession session;
public void search(String searchString) {
org.apache.lucene.search.Query luceneQuery = getLuceneQuery();
org.hibernate.Query query session.createFullTextQuery(luceneQuery,
Product.class);
searchResults = query
.setMaxResults(pageSize + 1)
.setFirstResult(pageSize * currentPage)
.list();
}
[...]
}
Note
Here, FullT extSession extends org.hibernate.Session so that it can be used as a
regular Hibernate Session.
A smoother integration is proposed if the JPA is used:
@Stateful
@Name("search")
public class FullTextSearchAction implements FullTextSearch, Serializable {
@In FullTextEntityManager em;
public void search(String searchString) {
org.apache.lucene.search.Query luceneQuery = getLuceneQuery();
javax.persistence.Query query = em.createFullTextQuery(luceneQuery,
Product.class);
searchResults = query
.setMaxResults(pageSize + 1)
.setFirstResult(pageSize * currentPage)
.getResultList();
}
[...]
}
Here, a FulltextEntityManager is injected where Hibernate Search is present.
FullT extEntityManager extends EntityManager with search specific methods, the same way
FullT extSession extends Session.
300
Chapter 26. Hibernate Search
When an EJB 3.0 Session or Message Driven Bean injection is used (that is, where injection uses the
@ PersistenceContext annotation), the EntityManager interface cannot be replaced by using the
FullT extEntityManager interface in the declaration statement. However, the implementation
injected will be a FullT extEntityManager implementation, which allows downcasting.
@Stateful
@Name("search")
public class FullTextSearchAction implements FullTextSearch, Serializable {
@PersistenceContext EntityManager em;
public void search(String searchString) {
org.apache.lucene.search.Query luceneQuery = getLuceneQuery();
FullTextEntityManager ftEm = (FullTextEntityManager) em;
javax.persistence.Query query =
ftEm.createFullTextQuery(luceneQuery, Product.class);
searchResults = query
.setMaxResults(pageSize + 1)
.setFirstResult(pageSize * currentPage)
.getResultList();
}
[...]
}
Note
If you are accustomed to using Hibernate Search outside Seam, remember that you do not need
to use Search.createFullT extSession when Hibernate Search is integrated with Seam.
For a working example of Hibernate Search, check the Blog example in the distribution.
301
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Chapter 27. Configuring Seam and packaging Seam
applications
Configuration can be complex and tedious, but for the most part you will not need to write configuration
data from scratch. Only a few lines of XML are required to integrate Seam with your JavaServer Faces
(JSF) implementation and Servlet container, and for the most part you can either use seam-gen to start
your application, or simply copy and paste from the example applications provided with Seam.
27.1. Basic Seam configuration
First, the basic configuration required whenever Seam is used with JSF.
27.1.1. Integrating Seam with JSF and your servlet container
First, define a Faces Servlet.
<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>
(You can adjust the URL pattern as you like.)
Seam also requires the following entry in your web.xm l file:
<listener>
<listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
</listener>
T his listener is responsible for bootstrapping Seam, and for destroying session and application
contexts.
Some JSF implementations do not implement server-side state saving correctly, which interferes with
Seam's conversation propagation. If you have problems with conversation propagation during form
submissions, try switching to client-side state saving. T o do so, add the following to web.xm l:
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
302
Chapter 27. Configuring Seam and packaging Seam applications
Warning
Setting of javax.faces.ST AT E_SAVING_MET HOD to client can lead to security issues and
it should be set environment entry com .sun.faces.ClientStateSavingPassword in
web.xm l like:
<env-entry>
<env-entry-name>com.sun.faces.ClientStateSavingPassword</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>INSERT_YOUR_PASSWORD</env-entry-value>
</env-entry>
T he JSF specification is unclear about the mutability of view state values. Since Seam uses the JSF view
state to back its PAGE scope, this can be problematic. If you use server-side state saving with the JSF-RI
(JSF Reference Implementation), and you want a page-scoped bean to retain its exact value for a given
page view, you must specify the context parameter as follows:
<context-param>
<param-name>com.sun.faces.serializeServerState</param-name>
<param-value>true</param-value>
</context-param>
If this is not specified, a page-scoped component will contain the latest value of the page, and not the
value of the "back" page, when the "back" button is used. T his setting is not enabled by default because
serializing the JSF view with every request lowers overall performance.
27.1.2. Seam Resource Servlet
T he Seam Resource Servlet provides resources used by Seam Remoting, CAPT CHAs (see the Security
chapter) and some JSF UI controls. Configuring the Seam Resource Servlet requires the following entry
in web.xm l:
<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>
27.1.3. Seam Servlet filters
Seam does not require Servlet filters for basic operation, but there are several features that depend
upon filter use. Seam lets you add and configure Servlet filters as you would configure other built-in
Seam components. T o use this configuration method, you must first install a master filter in web.xm l:
<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>/*</url-pattern>
</filter-mapping>
303
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
T o ensure that it is run first, the Seam master filter must be the first filter specified in web.xm l.
T he Seam filters share a number of common attributes, which can be set in com ponents.xm l, along
with any parameters discussed below:
url-pattern — Specifies which requests are filtered. T he default is all requests. url-pattern is
a pattern which allows a wildcard suffix.
regex-url-pattern — Specifies which requests are filtered. T he default is all requests. regexurl-pattern is a true regular expression match for request path.
disabled — Disables a built in filter.
T hese patterns are matched against the URI path of the request (see
HttpServletRequest.getURIPath()), and the name of the Servlet context is removed before
matching occurs.
Adding the master filter enables the following built-in filters:
27.1.3.1. Exception handling
T his filter is required by most applications, and provides the exception mapping functionality in
pages.xm l. It also rolls back uncommitted transactions when uncaught exceptions occur. (T he web
container should do this automatically, but this does not occur reliably in some application servers.)
By default, the exception handling filter will process all requests, but you can adjust this behavior by
adding a <web:exception-filter>
entry to com ponents.xm l, like so:
<components xmlns="http://jboss.org/schema/seam/components"
xmlns:web="http://jboss.org/schema/seam/web">
<web:exception-filter url-pattern="*.seam"/>
</components>
27.1.3.2. Conversation propagation with redirects
T his filter allows Seam to propagate the conversation context across browser redirects. It intercepts any
browser redirects and adds a request parameter that specifies the Seam conversation identifier.
T he redirect filter processes all requests by default, but this behavior can also be adjusted in
com ponents.xm l:
<web:redirect-filter url-pattern="*.seam"/>
27.1.3.3. URL rewriting
T his filter lets Seam apply URL rewriting for views, depending on its configuration in pages.xm l. T his
filter is not active by default, but can be activated by adding the following configuration to
com ponents.xm l:
<web:rewrite-filter view-mapping="*.seam"/>
T he view-m apping parameter must match the Servlet mapping defined for the Faces Servlet in the
web.xm l file. If omitted, the rewrite filter assumes the pattern * .seam .
27.1.3.4 . Multipart form submissions
T his feature is required when you use the Seam file upload JSF control. It detects multipart form
requests and processes them according the multipart/form-data specification (RFC-2388). Add the
following to com ponents.xm l to override settings:
304
Chapter 27. Configuring Seam and packaging Seam applications
<web:multipart-filter create-temp-files="true"
max-request-size="1000000" url-pattern="*.seam"/>
create-tem p-files — If true, uploaded files are written to a temporary file, rather than being
held in memory. T his can be important if you expect large file uploads. By default, this is set to
false.
m ax-request-size — If the size of a file upload request exceeds this value, the request will be
aborted. T he default setting is 0 (no size limit). (T he size of a file upload is determined by reading
the Content-Length header in the request.)
27.1.3.5. Character encoding
T his filter sets the character encoding of submitted form data. It is not installed by default, and requires
an entry in com ponents.xm l to enable it:
<web:character-encoding-filter encoding="UTF-16"
override-client="true" url-pattern="*.seam"/>
encoding — T he type of encoding to use.
override-client — If set to true, the request encoding will be set to that specified by
encoding, regardless of whether the request specifies a particular encoding. If set to false, the
request encoding will only be set if the client has not already specified the request encoding. By
default, this is set to false.
27.1.3.6. RichFaces
If RichFaces is used in your project, Seam automatically installs the RichFaces AJAX filter before all
other built-in filters, so there is no need to add it to web.xm l manually.
T he RichFaces Ajax filter is installed only if the RichFaces JARs are present in your project.
T o override the default settings, add the following entry to com ponents.xm l. T he options are the
same as those specified in the RichFaces Developer Guide:
<web:ajax4jsf-filter force-parser="true" enable-cache="true"
log4j-init-file="custom-log4j.xml" url-pattern="*.seam"/>
force-parser — forces all JSF pages to be validated by RichFaces's XML syntax checker. If
false, only AJAX responses are validated and converted to well-formed XML. Setting forceparser to false improves performance, but can provide visual artifacts on AJAX updates.
enable-cache — enables caching of framework-generated resources, such as JavaScript, CSS,
images, etc. When developing custom JavaScript or CSS, setting this to true prevents the browser
from caching the resource.
log4 j-init-file — is used to set up per-application logging. A path, relative to web application
context, to the log4 j.xm l configuration file should be provided.
27.1.3.7. Identity Logging
T his filter adds the authenticated username to the log4 j mapped diagnostic context, so that it can be
included in formatted log output by adding %X{usernam e} to the pattern.
By default, the logging filter processes all requests. You can adjust this behavior by adding a
<web:logging-filter>
entry to com ponents.xm l, like so:
305
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<components xmlns="http://jboss.org/schema/seam/components"
xmlns:web="http://jboss.org/schema/seam/web">
<web:logging-filter url-pattern="*.seam"/>
</components>
27.1.3.8. Context management for custom servlets
Requests that are sent directly to Servlets other than the JSF Servlet are not processed in the JSF life
cycle, so Seam provides a Servlet filter that can be applied to any other Servlet requiring access to Seam
components.
T his filter lets custom Servlets interact with Seam contexts. It sets up Seam contexts at the beginning of
each request, and removes them at the end of the request. T his filter should never be applied to the JSF
FacesServlet — Seam uses the phase listener to manage context in a JSF request.
T his filter is not installed by default, and must be enabled in com ponents.xm l:
<web:context-filter url-pattern="/media/*"/>
T he context filter expects the conversation ID of any conversation context to be defined in the
conversationId request parameter. You are responsible for ensuring that this is included in the
request.
You are also responsible for ensuring that any new conversation ID propagates back to the client. Seam
exposes the conversation ID as a property of the built in component conversation.
27.1.3.9. Enabling HT T P cache-control headers
Seam does not automatically add cache-control HT T P headers to any resources served by the
Seam resource servlet, or directly from your view directory by the servlet container. T his means that your
images, Javascript and CSS files, and resource representations from Seam resource servlet such as
Seam Remoting Javascript interfaces are usually not cached by the browser. T his is convenient in
development but should be changed in production when optimizing the application.
You can configure a Seam filter to enable automatic addition of cache-control headers depending on
the requested URI in com ponents.xm l:
<web:cache-control-filter name="commonTypesCacheControlFilter"regex-urlpattern=".*(\.gif|\.png|\.jpg|\.jpeg|\.css|\.js)"value="max-age=86400"/>
<!-- 1 day -->
<web:cache-control-filter name="anotherCacheControlFilter"urlpattern="/my/cachable/resources/*"value="max-age=432000"/>
<!-- 5 days -->
You do not have to name the filters unless you have more than one filter enabled.
27.1.3.10. Adding custom filters
Seam can install your filters for you. T his allows you to specify your filter's placement in the chain — the
Servlet specification does not provide a well-defined order when you specify your filters in web.xm l.
Add a @ Filter annotation to your Seam component. (Your Seam component must implement
javax.servlet.Filter.)
306
Chapter 27. Configuring Seam and packaging Seam applications
@Startup
@Scope(APPLICATION)
@Name("org.jboss.seam.web.multipartFilter")
@BypassInterceptors
@Filter(within="org.jboss.seam.web.ajax4jsfFilter")
public class MultipartFilter extends AbstractFilter {...}
Adding the @ Startup annotation makes the component available during Seam start up. Bijection is not
available here (@ BypassInterceptors), and the filter should be further down the chain than the
RichFaces filter (@ Filter(within="org.jboss.seam .web.ajax4 jsfFilter")).
27.1.4. Integrating Seam with your EJB container
EJB components in a Seam application are managed by both Seam and the EJB container. Seam
resolves EJB component references, manages the lifetime of stateful session bean components, and
participates in each method call via interceptors. T o integrate Seam with your EJB container, you must
first configure the interceptor chain.
Apply the Seam Interceptor to your Seam EJB components. T his interceptor delegates to a set of
built-in server-side interceptors that handle operations like bijection and conversation demarcation. T he
simplest way to do this across an entire application is to add the following interceptor configuration in
ejb-jar.xm l:
<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>
You must tell Seam where to find session beans in JNDI. You could do this by specifying a @ JndiNam e
annotation on every session bean Seam component. A better approach is to specify a pattern with which
Seam can calculate the JNDI name from the EJB name. However, the EJB3 specification does not define
a standard method of mapping to global JNDI — this mapping is vendor-specific, and can also depend
upon your naming conventions. We specify this option in com ponents.xm l:
For Red Hat JBoss Enterprise Application Platform 6, the following pattern is correct:
<core:init jndi-name="java:app/<ejb-module-name>/#{ejbName}" />
In this case, <ejb-m odule-nam e> is the name of the EJB module (by default it is filename of ejb jar) in
which the bean is deployed, Seam replaces #{ejbNam e} with the name of the EJB.
How these JNDI names are resolved and somehow locate an EJB component might appear a bit like
black magic at this point, so let's dig into the details. First, let's talk about how the EJB components get
into JNDI.
First, we will talk about how the EJB component is transferred to JNDI. T o avoid using XML, JBoss
Enterprise Application Platform uses the aforementioned pattern (that is, EAR name/EJB name/interface
type) to automatically assign an EJB component a global JNDI name. T he EJB name will be the first nonempty value out of the following:
307
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
the <ejb-nam e>
element in ejb-jar.xm l,
the nam e attribute in the @ Stateless or @ Stateful annotation, or
the simple name of the bean class.
For example, assume that you have the following EJB bean and interface defined:
package com.example.myapp;
import javax.ejb.Local;
@Local
public class Authenticator
{
boolean authenticate();
}
package com.example.myapp;
import javax.ejb.Stateless;
@Stateless
@Name("authenticator")
public class AuthenticatorBean implements Authenticator
{
public boolean authenticate() { ... }
}
Assuming that your EJB bean class is deployed in an EAR named m yapp, the global JNDI name
assigned on the JBoss Enterprise Application Platform will be m yapp/AuthenticatorBean/local.
You can refer to this EJB component as a Seam component with the name authenticator, and Seam
will use the JNDI pattern (or the @ JndiNam e annotation) to locate it in JNDI.
For other application servers, you must declare an EJB reference for your EJB so that it is assigned a
JNDI name. T his does require some XML, and means that you must establish your own JNDI naming
convention so that you can use the Seam JNDI pattern. It may be useful to follow the JBoss convention.
You must define the EJB references in two locations when using Seam with a non-JBoss application
server. If you look up the Seam EJB component with JSF (in a JSF view, or as a JSF action listener) or a
Seam JavaBean component, then you must declare the EJB reference in web.xm l. T he EJB reference
that would be required for our example is:\
<ejb-local-ref>
<ejb-ref-name>myapp/AuthenticatorBean/local</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<local>org.example.vehicles.action.Authenticator</local>
</ejb-local-ref>
T his reference covers most uses of the component in a Seam application. If you want to be able to inject
a Seam EJB component into another Seam EJB component with the @ In annotation, you must define this
EJB reference in a second location: ejb-jar.xm l. T his is slightly more complicated.
When Seam looks for a Seam EJB component to satisfy an injection point defined with @ In, the
component will only be found if it is referenced in JNDI. JBoss automatically registers EJBs to the JNDI
so that they are always available to the web and EJB containers. Other containers require you to define
your EJBs explicitly.
Application servers that adhere to the EJB specification require that EJB references are always explicitly
defined. T hese cannot be declared globally — you must specify each JNDI resource for an EJB
component individually.
Assuming that you have an EJB with a resolved name of RegisterAction, the following Seam
injection applies:
@In(create = true) Authenticator authenticator;
308
Chapter 27. Configuring Seam and packaging Seam applications
For this injection to work, you must also establish the link in ejb-jar.xm l, like so:
<ejb-jar>
<enterprise-beans>
<session>
<ejb-name>RegisterAction</ejb-name>
<ejb-local-ref>
<ejb-ref-name>myapp/AuthenticatorAction/local</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<local>com.example.myapp.Authenticator</local>
</ejb-local-ref>
</session>
</enterprise-beans>
...
</ejb-jar>
T he component is referenced here just as it was in web.xm l. Identifying it here brings the reference into
the EJB context, where it can be used by the RegisterAction bean. You must add one reference for
each injection (via @ In) of one Seam EJB component into another Seam EJB component. You can see
an example of this setup in the jee6 example.
It is possible to inject one EJB into another with the @ EJB annotation, but this injects the EJB reference
rather than the Seam EJB component instance. Because Seam's interceptor is invoked on any method
call to an EJB component, and using @ EJB only invokes Seam's server-side interceptor chain, some
Seam features will not work with @ EJB injection. (Seam's state management and Seam's client-side
interceptor chain, which handles security and concurrency, are two affected features.) When a stateful
session bean is injected using the @ EJB annotation, it will not necessarily bind to the active session or
conversation, either, so we recommend injecting with @ In.
For transaction management, we recommend using a special built-in component that is fully aware of
container transactions, and can correctly process transaction success events registered with the
Events component. T o tell Seam when container-managed transactions end, add the following line to
your com ponents.xm l file:
<transaction:ejb-transaction/>
27.1.5. Remember
T he final requirement for integration is that a seam .properties, MET A-INF/seam .properties or
MET A-INF/com ponents.xm l file be placed in any archive in which your Seam components are
deployed. For web archive (WAR) files, place a seam .properties file inside the WEB-INF/classes
directory in which your components are deployed.
Seam scans any archive with seam .properties files for Seam components at start up. T he
seam .properties file can be empty, but it must be included so that the component is recognized by
Seam. T his is a workaround for Java Virtual Machine (JVM) limitations — without the
seam .properties file, you would need to list every component explicitly in com ponents.xm l.
27.2. Using Alternate JPA Providers
Seam comes packaged and configured with Hibernate as the default JPA provider. T o use a different
JPA provider, you must configure it with Seam.
309
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Note
T his is a workaround — future versions of Seam will not require configuration changes to use
alternative JPA providers, unless you add a custom persistence provider implementation.
T here are two ways to tell Seam about your JPA provider. T he first is to update your application's
com ponents.xm l so that the generic PersistenceProvider takes precedence over the Hibernate
version. Simply add the following to the file:
<component name="org.jboss.seam.persistence.persistenceProvider"
class="org.jboss.seam.persistence.PersistenceProvider"
scope="stateless">
</component>
T o take advantage of any of your JPA provider's non-standard features, you must write your own
implementation of the PersistenceProvider. (You can use HibernatePersistenceProvider
as a starting point.) T ell Seam to use this PersistenceProvider like so:
<component name="org.jboss.seam.persistence.persistenceProvider"
class="org.your.package.YourPersistenceProvider">
</component>
Now, update persistence.xm l with the correct provider class, and any properties required by your
provider. Remember to package any required JAR files with your application.
27.3. Configuring Seam in Java EE 6
If you're running in a Java EE environment, this is all the configuration required to start using Seam!
27.3.1. Packaging
Once packaged into an EAR, your archive will be structured similarly to the following:
310
Chapter 27. Configuring Seam and packaging Seam applications
my-application.ear/
jboss-seam.jar
lib/
jboss-el.jar
META-INF/
MANIFEST.MF
application.xml
jboss-deployment-structure.xml
my-application.war/
META-INF/
MANIFEST.MF
WEB-INF/
web.xml
components.xml
faces-config.xml
lib/
jboss-seam-ui.jar
login.jsp
register.jsp
...
my-application.jar/
META-INF/
MANIFEST.MF
persistence.xml
seam.properties
org/
jboss/
myapplication/
User.class
Login.class
LoginBean.class
Register.class
RegisterBean.class
...
You should declare jboss-seam .jar as an ejb module in MET A-INF/application.xm l; jbossel.jar should be placed in the EAR's lib directory (putting it in the EAR classpath.
If you want to use Drools, you must include the needed jars in the EAR's lib directory.
If you want to use the Seam tag library (most Seam applications do), you must include jboss-seam ui.jar in the WEB-INF/lib directory of the WAR. If you want to use the PDF or email tag libraries, you
need to put jboss-seam -pdf.jar or jboss-seam -m ail.jar in WEB-INF/lib.
If you want to use the Seam debug page (only works for applications using facelets), you must include
jboss-seam -debug.jar in the WEB-INF/lib directory of the WAR.
Seam ships with several example applications that are deployable in any Java EE container that
supports EJB 3.1.
faces-config.xm l is not required in JSF 2, but if you want to set up something non-default you need
to place it in WAR/WEB-INF.
27.4. Configuring Seam without EJB
Seam is useful even if you're not yet ready to take the plunge into EJB 3.1. In this case you would use
Hibernate 4 instead of EJB 3.1 persistence, and plain JavaBeans instead of session beans. You'll miss
out on some of the nice features of session beans but it will be very easy to migrate to EJB 3.1 when
you're ready and, in the meantime, you'll be able to take advantage of Seam's unique declarative state
management architecture.
311
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Seam JavaBean components do not provide declarative transaction demarcation like session beans do.
You could manage your transactions manually using the JT A UserT ransaction or declaratively using
Seam's @ T ransactional annotation. But most applications will just use Seam managed transactions
when using Hibernate with JavaBeans.
T he Seam distribution includes a version of the booking example application that uses Hibernate and
JavaBeans instead of EJB, and another version that uses JPA and JavaBeans. T hese example
applications are ready to deploy into any Java EE application server.
27.4.1. Boostrapping Hibernate in Seam
Seam will bootstrap a Hibernate SessionFactory from your hibernate.cfg.xm l file if you install a
built-in component:
<persistence:hibernate-session-factory name="hibernateSessionFactory"/>
You will also need to configure a managed session if you want a Seam managed Hibernate Session to
be available via injection.
<persistence:managed-hibernate-session name="hibernateSession" sessionfactory="#{hibernateSessionFactory}"/>
27.4.2. Boostrapping JPA in Seam
Seam will bootstrap a JPA EntityManagerFactory from your persistence.xm l file if you install
this built-in component:
<persistence:entity-manager-factory name="entityManagerFactory"/>
You will also need to configure a managed persistence context if you want a Seam managed JPA
EntityManager to be available via injection.
<persistence:managed-persistence-context name="entityManager" entity-managerfactory="#{entityManagerFactory}"/>
27.4.3. Packaging
We can package our application as a WAR, in the following structure:
312
Chapter 27. Configuring Seam and packaging Seam applications
my-application.war/
META-INF/
MANIFEST.MF
jboss-deployment-structure.xml
WEB-INF/
web.xml
components.xml
faces-config.xml
lib/
jboss-seam.jar
jboss-seam-ui.jar
jboss-el.jar
hibernate-core.jar
hibernate-annotations.jar
hibernate-validator.jar
...
my-application.jar/
META-INF/
MANIFEST.MF
seam.properties
hibernate.cfg.xml
org/
jboss/
myapplication/
User.class
Login.class
Register.class
...
login.jsp
register.jsp
...
If we want to deploy Hibernate in a non-EE environment like T omcat or T estNG, we need to do a little bit
more work.
27.5. Configuring Seam in Java SE
T o use Seam outside an EE environment, you must tell Seam how to manage transactions, since JT A
will not be available. If you use JPA, you can tell Seam to use JPA resource-local transactions — that is,
EntityT ransaction — like so:
<transaction:entity-transaction entity-manager="#{entityManager}"/>
If you use Hibernate, you can tell Seam to use the Hibernate transaction API with the following:
<transaction:hibernate-transaction session="#{session}"/>
You must also define a datasource.
27.6. Deployment in Red Hat JBoss Enterprise Application
Platform 6
JBoss Enterprise Application Platform 6 is the default deployment target for all examples in Seam 2.3
distribution.
Seam 2.3 requires to have setup special deployment metada file jboss-deploym entstructure.xm l for correct initialization. Minimal content for EAR is:
313
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Example 27.1. jboss-deployment-structure.xml
<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"/>
</dependencies>
</deployment>
</jboss-deployment-structure>
For further details about classloading see your application server documentation.
314
Chapter 27. Configuring Seam and packaging Seam applications
Deployment of multiple modules in one EAR
T here is a significant enhancement for speed up the application deployment. T his unfortunatelly
can cause some issues while you have multiple war/ejb modules in your application.
T his situation requires to use and set up new Java EE 6 configuration parameter - Module
initialization order - in application.xm l - initialize-in-order to true. T his causes that
initialization will happen in defined order like it is in application.xm l. Example of
application.xm l:
<?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"version="6"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/application_6.xsd">
<application-name>test-app</application-name>
<initialize-in-order>true</initialize-in-order>
<module>
<ejb>jboss-seam.jar</ejb>
</module>
<module>
<web>
<web-uri>test-web1.war</web-uri>
<context-root>test</context-root>
</web>
<web>
<web-uri>test-web2.war</web-uri>
<context-root>test2</context-root>
</web>
</module>
</application>
If you are using m aven-ear-plugin for generation of your application, you can use this plugin
configuration:
315
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<plugin>
<artifactId>maven-ear-plugin</artifactId>
<!-- from version 2.6 the plugin supports Java EE 6 descriptor -->
<version>2.7</version>
<configuration>
<version>6</version>
<generateApplicationXml>true</generateApplicationXml>
<defaultLibBundleDir>lib</defaultLibBundleDir>
<initializeInOrder>true</initializeInOrder>
<modules>
<jarModule>
<groupId>org.jboss.el</groupId>
<artifactId>jboss-el</artifactId>
<includeInApplicationXml>false</includeInApplicationXml>
<bundleDir>lib</bundleDir>
</jarModule>
<ejbModule>
<groupId>org.jboss.seam</groupId>
<artifactId>jboss-seam</artifactId>
<bundleFileName>jboss-seam.jar</bundleFileName>
</ejbModule>
<ejbModule>
<groupId>some.user.module</groupId>
<artifactId>hello-ejbs</artifactId>
<bundleFileName>hello-ejbs.jar</bundleFileName>
</ejbModule>
<webModule>
<groupId>some.user.module</groupId>
<artifactId>hello-web1</artifactId>
<contextRoot>/hello1</contextRoot>
<bundleFileName>hello-web1.war</bundleFileName>
</webModule>
<webModule>
<groupId>some.user.module</groupId>
<artifactId>hello-web2</artifactId>
<contextRoot>/hello2</contextRoot>
<bundleFileName>hello-web2.war</bundleFileName>
</webModule>
</modules>
</configuration>
</plugin>
27.7. Configuring SFSB and Session Timeouts in Red Hat JBoss
Enterprise Application Platform 6
It is very important that the timeout for Stateful Session Beans is set higher than the timeout for HT T P
Sessions, otherwise SFSB's may time out before the user's HT T P session has ended. JBoss Enterprise
Application Platform 6 has a default session bean timeout of 30 minutes, which is configured in
standalone/configuration/standalone.xm l (replace standalone.xml with your standalonefull.xml if you use full profile).
T he default SFSB timeout can be adjusted by modifying the value of default-access-tim eout in the
EJB subsystem subsystem xm lns="urn:jboss:dom ain:ejb3:1.2":
316
Chapter 27. Configuring Seam and packaging Seam applications
<subsystem xmlns="urn:jboss:domain:ejb3:1.2">
<session-bean>
<stateless>
<bean-instance-pool-ref pool-name="slsb-strict-max-pool"/>
</stateless>
<stateful default-access-timeout="5000" cache-ref="simple"/>
<singleton default-access-timeout="5000"/>
</session-bean>
...
</subsystem>
T he default HT T P session timeout can't be modified in EAP 6.
T o override default value for your own application, simply include session-tim eout entry in your
application's own web.xm l:
<session-config>
<session-timeout>30</session-timeout>
</session-config>
27.8. Running Seam in a Portlet
JBoss Portlet Bridge integration is a Technology Preview
T echnology 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 T echnology Preview features generally available, we will
provide commercially reasonable efforts to resolve any reported issues that customers
experience when using these features.
You can use the JBoss Portlet Bridge to run your Seam application in a portlet. T he bridge supports JSF
within a portlet, and includes extensions for Seam and RichFaces. See http://labs.jboss.com/
portletbridge for more information.
27.9. Deploying custom resources
On start up, Seam scans all JARs containing /seam .properties, /MET A-INF/com ponents.xm l or
/MET A-INF/seam .properties for resources. For example, all classes annotated with @ Nam e are
registered on start as Seam components.
You can also use Seam to handle custom resources — that is, Seam can handle specific annotations.
First, provide a list of annotation types to handle in the /MET A-INF/seam -deploym ent.properties
files, like so:
# A colon-separated list of annotation types to handle
org.jboss.seam.deployment.annotationTypes=com.acme.Foo:com.acme.Bar
T hen, collect all classes annotated with @ Foo on application start up:
317
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
@Name("fooStartup")
@Scope(APPLICATION)
@Startup
public class FooStartup
{
@In("#{deploymentStrategy.annotatedClasses['com.acme.Foo']}")
private Set<Class<Object>> fooClasses;
@In("#{hotDeploymentStrategy.annotatedClasses['com.acme.Foo']}")
private Set<Class<Object>> hotFooClasses;
@Create
public void create()
{
for (Class clazz: fooClasses)
{
handleClass(clazz);
}
for (Class clazz: hotFooClasses)
{
handleClass(clazz);
}
}
public void handleClass(Class clazz)
{
// ...
}
}
You can also set Seam to handle any resource. For example, if you want to process files with the
.foo.xm l extension, you can write a custom deployment handler:
public class FooDeploymentHandler implements DeploymentHandler
{
private static DeploymentMetadata FOO_METADATA = new DeploymentMetadata()
{
public String getFileNameSuffix()
{
return ".foo.xml";
}
};
public String getName()
{
return "fooDeploymentHandler";
}
public DeploymentMetadata getMetadata()
{
return FOO_METADATA;
}
}
T his provides us with a list of all files with the .foo.xm l suffix.
Next, register the deployment handler with Seam in /MET A-INF/seam -deploym ent.properties:
# For standard deployment
# org.jboss.seam.deployment.deploymentHandlers=
#
com.acme.FooDeploymentHandler
# For hot deployment
# org.jboss.seam.deployment.hotDeploymentHandlers=
#
com.acme.FooDeploymentHandler
You can register multiple deployment handlers with a comma-separated list.
318
Chapter 27. Configuring Seam and packaging Seam applications
Seam uses deployment handlers internally to install components and namespaces, so the handle() is
called too early in Seam bootstrap to be useful. You can access the deployment handler easily during
the start up of an application-scoped component:
@Name("fooStartup")
@Scope(APPLICATION)
@Startup
public class FooStartup
{
@In("#{deploymentStrategy.deploymentHandlers['fooDeploymentHandler']}")
private FooDeploymentHandler myDeploymentHandler;
@In("#{hotDeploymentStrategy.deploymentHandlers['fooDeploymentHandler']}")
private FooDeploymentHandler myHotDeploymentHandler;
@Create public void create()
{
for (FileDescriptor fd: myDeploymentHandler.getResources())
{
handleFooXml(fd);
}
for (FileDescriptor f: myHotDeploymentHandler.getResources())
{
handleFooXml(fd);
}
}
public void handleFooXml(FileDescriptor fd)
{
// ...
}
}
319
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Chapter 28. Seam annotations
Seam uses annotations to achieve a declarative style of programming. Most annotations are defined by
the Enterprise JavaBean 3.0 (EJB3) specification, and the annotations used in data validation are
defined by the Hibernate Validator package. However, Seam also defines its own set of annotations,
which are described in this chapter.
All of these annotations are defined in the org.jboss.seam .annotations package.
28.1. Annotations for component definition
T his group of annotations is used to define a Seam component. T hese annotations appear on the
component class.
@ Nam e
@Name("componentName")
Defines the Seam component name for a class. T his annotation is required for all Seam
components.
@ Scope
@Scope(ScopeType.CONVERSATION)
Defines the default context of the component. T he possible values are defined by the
ScopeT ype enumeration: EVENT , PAGE, CONVERSAT ION, SESSION, APPLICAT ION, or
ST AT ELESS.
When no scope is explicitly specified, the default varies with the component type. For stateless
session beans, the default is ST AT ELESS. For entity beans and stateful session beans, the
default is CONVERSAT ION. For JavaBeans, the default is EVENT .
@ Role
@Role(name="roleName", scope=ScopeType.SESSION)
Allows a Seam component to be bound to multiple context variables. T he @ Nam e and @ Scope
annotations define a default role. Each @ Role annotation defines an additional role.
nam e — the context variable name.
scope — the context variable scope. When no scope is explicitly specified, the default
depends upon the component type, as above.
@ Roles
@Roles({ @Role(name="user", scope=ScopeType.CONVERSATION),
@Role(name="currentUser", scope=ScopeType.SESSION) })
Allows you to specify multiple additional roles.
@ BypassInterceptors
@BypassInterceptors
Disables all Seam interceptors on a particular component or component method.
320
Chapter 28. Seam annotations
@ JndiNam e
@JndiName("my/jndi/name")
Specifies the JNDI name that Seam will use to look up the EJB component. If no JNDI name is
explicitly specified, Seam will use the JNDI pattern specified by
org.jboss.seam .core.init.jndiPattern.
@ Conversational
@Conversational
Specifies that a conversation scope component is conversational, meaning that no method of
the component may be called unless a long-running conversation is active.
@ PerNestedConversation
@PerNestedConversation
Limits the scope of a conversation-scoped component to the parent conversation in which it
was instantiated. T he component instance will not be visible to nested child conversations,
which will operate within their own instances.
Warning
T his is not a recommended application feature. It implies that a component will be visible
only for a specific part of a request cycle.
@ Startup
@Scope(APPLICATION)
@Startup(depends="org.jboss.seam.security.ruleBasedPermissionResolver")
Specifies that an application-scoped component will start immediately at initialization time. T his
is used for built-in components that bootstrap critical infrastructure, such as JNDI, datasources,
etc.
@Scope(SESSION) @Startup
Specifies that a session-scoped component will start immediately at session creation time.
depends — specifies that the named components must be started first, if they are installed.
@ Install
@Install(false)
Specifies that a component should not be installed by default. (If you do not specify this
annotation, the component will be installed.)
@Install(dependencies="org.jboss.seam.security.ruleBasedPermissionResolver")
Specifies that a component should only be installed if the components listed as dependencies
are also installed.
321
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
@Install(genericDependencies=ManagedQueueSender.class)
Specifies that a component should only be installed if a component that is implemented by a
certain class is installed. T his is useful when a required dependency does not have a single
well-known name.
@Install(classDependencies="org.hibernate.Session")
Specifies that a component should only be installed if the named class is included on the
classpath.
@Install(precedence=BUILT_IN)
Specifies the precedence of the component. If multiple components with the same name exist,
the one with the higher precedence will be installed. T he defined precedence values are (in
ascending order):
BUILT _IN — precedence of all built-in Seam components.
FRAMEWORK — precedence to use for components of frameworks which extend Seam.
APPLICAT ION — precedence of application components (the default precedence).
DEPLOYMENT — precedence to use for components which override application components
in a particular deployment.
MOCK — precedence for mock objects used in testing.
@ Synchronized
@Synchronized(timeout=1000)
Specifies that a component is accessed concurrently by multiple clients, and that Seam should
serialize requests. If a request is not able to obtain its lock on the component in the given
timeout period, an exception will be raised.
@ ReadOnly
@ReadOnly
Specifies that a JavaBean component or component method does not require state replication
at the end of the invocation.
@ AutoCreate
@AutoCreate
Specifies that a component will be automatically created, even if the client does not specify
create=true.
28.2. Annotations for bijection
T he next two annotations control bijection. T hese attributes occur on component instance variables or
property accessor methods.
@ In
@In
322
Chapter 28. Seam annotations
Specifies that a component attribute is to be injected from a context variable at the beginning of
each component invocation. If the context variable is null, an exception will be thrown.
@In(required=false)
Specifies that a component attribute is to be injected from a context variable at the beginning of
each component invocation. T he context variable may be null.
@In(create=true)
Specifies that a component attribute is to be injected from a context variable at the beginning of
each component invocation. If the context variable is null, an instance of the component is
instantiated by Seam.
@In(value="contextVariableName")
Specifies the name of the context variable explicitly, instead of using the annotated instance
variable name.
@In(value="#{customer.addresses['shipping']}")
Specifies that a component attribute is to be injected by evaluating a JSF EL expression at the
beginning of each component invocation.
value — specifies the name of the context variable. Defaults to the name of the component
attribute. Alternatively, specifies a JSF EL expression, surrounded by #{...}.
create — specifies that Seam should instantiate the component with the same name as
the context variable, if the context variable is undefined (null) in all contexts. Defaults to
false.
required — specifies that Seam should throw an exception if the context variable is
undefined in all contexts.
@ Out
@Out
Specifies that a component attribute that is a Seam component is to be outjected to its context
variable at the end of the invocation. If the attribute is null, an exception is thrown.
@Out(required=false)
Specifies that a component attribute that is a Seam component is to be outjected to its context
variable at the end of the invocation. T he attribute can be null.
@Out(scope=ScopeType.SESSION)
Specifies that a component attribute that is not a Seam component type is to be outjected to a
specific scope at the end of the invocation.
Alternatively, if no scope is explicitly specified, the scope of the component with the @ Out
attribute issued (or the EVENT scope if the component is stateless).
@Out(value="contextVariableName")
Specifies the name of the context variable explicitly, instead of using the annotated instance
variable name.
value — specifies the name of the context variable. Default to the name of the component
323
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
attribute.
required — specifies that Seam should throw an exception if the component attribute is
null during outjection.
T hese annotations commonly occur together, as in the following example:
@In(create=true)
@Out private User currentUser;
T he next annotation supports the manager component pattern, where a Seam component manages the
life cycle of an instance of some other class that is to be injected. It appears on a component getter
method.
@ Unwrap
@Unwrap
Specifies that the object returned by the annotated getter method will be injected instead of the
component.
T he next annotation supports the factory component pattern, in which a Seam component is responsible
for initializing the value of a context variable. T his is especially useful for initializing any state required to
render a response to a non-Faces request. It appears on a component method.
@ Factory
@Factory("processInstance")
public void createProcessInstance() { ... }
Specifies that the component method be used to initialize the value of the named context
variable, when the context variable has no value. T his style is used with methods that return
void.
@Factory("processInstance", scope=CONVERSATION)
public ProcessInstance createProcessInstance() { ... }
Specifies that the value returned by the method should be used to initialize the value of the
named context variable, if the context variable has no value. T his style is used with methods
that return a value. If no scope is explicitly specified, the scope of the component with the
@ Factory method is used (unless the component is stateless, in which case the EVENT
context is used).
value — specifies the name of the context variable. If the method is a getter method, this
defaults to the JavaBeans property name.
scope — specifies the scope to which Seam should bind the returned value. Only
meaningful for factory methods that return a value.
autoCreate — specifies that this factory method should be automatically called whenever
the variable is asked for, even if @ In does not specify create=true.
T he following annotation lets you inject a Log:
@ Logger
@Logger("categoryName")
Specifies that a component field is to be injected with an instance of
324
Chapter 28. Seam annotations
org.jboss.seam .log.Log. For entity beans, the field must be declared as static.
value — specifies the name of the log category. Defaults to the name of the component
class.
T he final annotation lets you inject a request parameter value:
@ RequestParam eter
@RequestParameter("parameterName")
Specifies that a component attribute is to be injected with the value of a request parameter.
Basic type conversions are performed automatically.
value — specifies the name of the request parameter. Defaults to the name of the
component attribute.
28.3. Annotations for component life cycle methods
T hese annotations allow a component to react to its own life cycle events. T hey occur on methods of the
component. Only one of these annotations may be used in any one component class.
@ Create
@Create
Specifies that the method should be called when an instance of the component is instantiated
by Seam. Create methods are only supported for JavaBeans and stateful session beans.
@ Destroy
@Destroy
Specifies that the method should be called when the context ends and its context variables are
destroyed. Destroy methods are only supported for JavaBeans and stateful session beans.
Destroy methods should be used only for cleanup. Seam catches, logs and swallows any
exception that propagates out of a destroy method.
@ Observer
@Observer("somethingChanged")
Specifies that the method should be called when a component-driven event of the specified type
occurs.
@Observer(value="somethingChanged",create=false)
Specifies that the method should be called when an event of the specified type occurs, but that
an instance should not be created if it does not already exist. If an instance does not exist and
create is set to false, the event will not be observed. T he default value is true.
28.4. Annotations for context demarcation
325
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
T hese annotations provide declarative conversation demarcation. T hey appear on Seam component
methods, usually action listener methods.
Every web request is associated with a conversation context. Most of these conversations end when the
request is complete. T o span a conversation across multiple requests, you must "promote" the
conversation to a long-running conversation by calling a method marked with @ Begin.
@ Begin
@Begin
Specifies that a long-running conversation begins when this method returns a non-null outcome
without exception.
@Begin(join=true)
Specifies that, if a long-running conversation is already in progress, the conversation context is
propagated.
@Begin(nested=true)
Specifies that, if a long-running conversation is already in progress, a new nested conversation
context should begin. T he nested conversation will end when the next @ End is encountered,
and the outer conversation will resume. Multiple nested conversations can exist concurrently in
the same outer conversation.
@Begin(flushMode=FlushModeType.MANUAL)
Specifies the flush mode of any Seam-managed persistence contexts.
flushMode=FlushModeT ype.MANUAL supports the use of atomic conversations, where all
write operations are queued in the conversation context until an explicit call to flush() (which
usually occurs at the end of the conversation) is made.
join — determines the behavior when a long-running conversation is already in progress.
If true, the context is propagated. If false, an exception is thrown. Defaults to false. T his
setting is ignored when nested=true is specified.
nested — specifies that a nested conversation should be started if a long-running
conversation is already in progress.
flushMode — sets the flush mode of any Seam-managed Hibernate sessions or JPA
persistence contexts that are created during this conversation.
@ End
@End
Specifies that a long-running conversation ends when this method returns a non-null outcome
without exception.
beforeRedirect — by default, the conversation will not actually be destroyed until after
any redirect has occurred. Setting beforeRedirect=true specifies that the conversation
should be destroyed at the end of the current request, and that the redirect will be
processed in a new temporary conversation context.
root — by default, ending a nested conversation simply pops the conversation stack and
resumes the outer conversation. Setting root=true specifies that the root conversation
should be destroyed, which destroys the entire conversation stack. If the conversation is not
nested, the current conversation is destroyed.
326
Chapter 28. Seam annotations
28.5. Annotations for use with Seam JavaBean components in a
J2EE environment
Seam provides an annotation that lets you force a rollback of the JT A transaction for certain action
listener outcomes.
@ T ransactional
@Transactional
Specifies that a JavaBean component should have similar transactional behavior to the default
behavior of a session bean component. T hat is, method invocations should take place in a
transaction, and if no transaction exists when the method is called, a transaction will be started
just for that method. T his annotation can be applied at either class or method level.
Note
T his annotation should not be used on EJB3 components — use
@ T ransactionAttribute instead.
@ ApplicationException
@ApplicationException
Applied to an exception to denote that it is an application exception and should be reported to
the client directly — that is, unwrapped. Operates identically to
javax.ejb.ApplicationException when used in a pre-Java EE 5 environment.
Note
T his annotation should not be used on EJB3 components — use
@ javax.ejb.ApplicationException instead.
rollback — by default false, if true this exception sets the transaction to rollback only.
end — by default false, if true, this exception ends the current long-running
conversation.
@ Interceptors
@Interceptors({DVDInterceptor, CDInterceptor})
Declares an ordered list of interceptors for a class or method. Operates identically to
javax.interceptors.Interceptors when used in a pre-Java EE 5 environment. Note
that this may only be used as a meta-annotation.
Note
T his annotation should not be used on EJB3 components — use
@ javax.interceptor.Interceptors instead.
T hese annotations are used primarily for JavaBean Seam components. If you use EJB3 components,
327
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
you should use the standard Java EE 5 annotations.
28.6. Annotations for exceptions
T hese annotations let you specify how Seam handles any exceptions propagating from a Seam
component.
@ Redirect
@Redirect(viewId="error.xhtml")
Specifies that the annotated exception causes a browser redirect to a specified view ID.
viewId — specifies the JSF view ID to redirect to. You can use EL here.
m essage — a message to be displayed. Defaults to the exception message.
end — specifies that the long-running conversation should end. Defaults to false.
@ HttpError
@HttpError(errorCode=404)
Specifies that the annotated exception causes a HT T P error to be sent.
errorCode — the HT T P error code. Defaults to 500.
m essage — a message to be sent with the HT T P error. Defaults to the exception message.
end — specifies that the long-running conversation should end. Defaults to false.
28.7. Annotations for Seam Remoting
Seam Remoting requires that the local interface of a session bean be annotated with the following
annotation:
@ WebRem ote
@WebRemote(exclude="path.to.exclude")
Indicates that the annotated method may be called from client-side JavaScript. T he exclude
property is optional, and allows objects to be excluded from the result's object graph. (See the
Chapter 23, Remoting chapter for more details.)
28.8. Annotations for Seam interceptors
T he following annotations appear on Seam interceptor classes.
Please refer to the documentation for the EJB3 specification for information about the annotations
required to define EJB interceptors.
@ Interceptor
@Interceptor(stateless=true)
Specifies that this interceptor is stateless and Seam may optimize replication.
@Interceptor(type=CLIENT)
328
Chapter 28. Seam annotations
Specifies that this interceptor is a "client-side" interceptor, called prior to the EJB container.
@Interceptor(around={SomeInterceptor.class, OtherInterceptor.class})
Specifies that this interceptor is positioned higher in the stack than the given interceptors.
@Interceptor(within={SomeInterceptor.class, OtherInterceptor.class})
Specifies that this interceptor is positioned deeper in the stack than the given interceptors.
28.9. Annotations for asynchronicity
T he following annotations are used to declare an asynchronous method, as in the following example:
@Asynchronous public void scheduleAlert(Alert alert,
@Expiration Date date) {
...
}
@Asynchronous public Timer scheduleAlerts(Alert alert,
@Expiration Date date,
@IntervalDuration long interval) {
...
}
@ Asynchronous
@Asynchronous
Specifies that the method call is processed asynchronously.
@ Duration
@Duration
Specifies the parameter of the asynchronous call that relates to the duration before the call is
processed (or first processed, for recurring calls).
@ Expiration
@Expiration
Specifies the parameter of the asynchronous call that relates to the date and time at which the
call is processed (or first processed, for recurring calls).
@ IntervalDuration
@IntervalDuration
Specifies that an asynchronous method call recurs. T he associated parameter defines the
duration of the interval between recurrences.
28.10. Annotations for use with JSF
329
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
T he following annotations make it easier to work with JSF.
@ Converter
Allows a Seam component to act as a JSF converter. T he annotated class must be a Seam
component, and must implement javax.faces.convert.Converter.
id — the JSF converter ID. Defaults to the component name.
forClass — if specified, registers this component as the default converter for a type.
@ Validator
Allows a Seam component to act as a JSF validator. T he annotated class must be a Seam
component, and must implement javax.faces.validator.Validator.
id — the JSF validator ID. Defaults to the component name.
28.10.1. Annotations for use with dataTable
T he following annotations make it easy to implement clickable lists backed by a stateful session bean.
T hey appear on attributes.
@ DataModel
@DataModel("variableName")
Outjects a property of type List, Map, Set or Object[] as a JSF DataModel into the scope
of the owning component (or the EVENT scope, if the owning component is ST AT ELESS). In the
case of Map, each row of the DataModel is a Map.Entry.
value — name of the conversation context variable. Default to the attribute name.
scope — if scope=ScopeT ype.PAGE is explicitly specified, the DataModel will be kept
in the PAGE context.
@ DataModelSelection
@DataModelSelection
Injects the selected value from the JSF DataModel. (T his is the element of the underlying
collection, or the map value.) If only one @ DataModel attribute is defined for a component, the
selected value from that DataModel will be injected. Otherwise, the component name of each
@ DataModel must be specified in the value attribute for each @ DataModelSelection.
If PAGE scope is specified on the associated @ DataModel, then the associated DataModel will
be injected in addition to the DataModel Selection. In this case, if the property annotated with
@ DataModel is a getter method, then a setter method for the property must also be part of the
Business API of the containing Seam Component.
value — name of the conversation context variable. Not needed if there is exactly one
@ DataModel in the component.
@ DataModelSelectionIndex
@DataModelSelectionIndex
Exposes the selection index of the JSF DataModel as an attribute of the component. (T his is
the row number of the underlying collection, or the map key.) If only one @ DataModel attribute
is defined for a component, the selected value from that DataModel will be injected. Otherwise,
330
Chapter 28. Seam annotations
the component name of each @ DataModel must be specified in the value attribute for each
@ DataModelSelectionIndex.
value — name of the conversation context variable. T his is not required if there is exactly
one @ DataModel in the component.
28.11. Meta-annotations for databinding
T hese meta-annotations make it possible to implement similar functionality to @ DataModel and
@ DataModelSelection for other datastructures apart from lists.
@ DataBinderClass
@DataBinderClass(DataModelBinder.class)
Specifies that an annotation is a databinding annotation.
@ DataSelectorClass
@DataSelectorClass(DataModelSelector.class)
Specifies that an annotation is a dataselection annotation.
28.12. Annotations for packaging
T his annotation provides a mechanism for declaring information about a set of components that are
packaged together. It can be applied to any Java package.
@ Nam espace
@Namespace(value="http://jboss.org/schema/seam/example/seampay")
Specifies that components in the current package are associated with the given namespace.
T he declared namespace can be used as an XML namespace in a com ponents.xm l file to
simplify application configuration.
@Namespace(value="http://jboss.org/schema/seam/core",
prefix="org.jboss.seam.core")
Specifies a namespace to associate with a given package. Additionally, it specifies a component
name prefix to be applied to component names specified in the XML file. For example, an XML
element named init that is associated with this namespace would be understood to actually
refer to a component named org.jboss.seam .core.init.
28.13. Annotations for integrating with the Servlet container
T hese annotations allow you to integrate your Seam components with the Servlet container.
@ Filter
When used to annotate a Seam component implementing javax.servlet.Filter,
designates that component as a servlet filter to be executed by Seam's master filter.
@Filter(around={"seamComponent", "otherSeamComponent"})
331
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Specifies that this filter is positioned higher in the stack than the given filters.
@Filter(within={"seamComponent", "otherSeamComponent"})
Specifies that this filter is positioned deeper in the stack than the given filters.
332
Chapter 29. Built-in Seam components
Chapter 29. Built-in Seam components
T his chapter describes Seam's built-in components, and their configuration properties. T he built-in
components are created automatically, even if they are not listed in your com ponents.xm l file.
However, if you need to override default properties or specify more than one component of a certain
type, you can do so in com ponents.xm l.
You can replace any of the built-in components with your own implementation by using @ Nam e to name
your own class after the appropriate built-in component.
29.1. Context injection components
T he first set of built-in components support the injection of various contextual objects. For example, the
following component instance variable would have the Seam session context object injected:
@In private Context sessionContext;
org.jboss.seam .core.contexts
Component that provides access to Seam Context objects such as
org.jboss.seam .core.contexts.sessionContext['user'].
org.jboss.seam .faces.facesContext
Manager component for the FacesContext context object. (T his is not a true Seam context.)
All of these components are always installed.
29.2. JSF-related components
T he following set of components are provided to supplement JSF.
org.jboss.seam .faces.dateConverter
Provides a default JSF converter for properties of type java.util.Date.
T his converter is automatically registered with JSF, so developers need not specify a
DateT imeConverter on an input field or page parameter. By default, it assumes the type to be a
date (as opposed to a time or date plus time), and uses the short input style adjusted to the
user's Locale. For Locale.US, the input pattern is m m /dd/yy. However, to comply with Y2K,
the year is changed from two digits to four — m m /dd/yyyy.
You can override the input pattern globally by reconfiguring your component. Consult the
JavaServer Faces documentation for this class to see examples.
org.jboss.seam .faces.facesMessages
Allows Faces success messages to propagate across a browser redirect.
add(FacesMessage facesMessage) — adds a Faces message, which will be displayed
during the next render response phase that occurs in the current conversation.
add(String m essageT em plate) — adds a Faces message, rendered from the given
message template, which may contain EL expressions.
add(Severity severity, String m essageT em plate) — adds a Faces message,
rendered from the given message template, which may contain EL expressions.
addFrom ResourceBundle(String key) — adds a Faces message, rendered from a
message template defined in the Seam resource bundle which may contain EL expressions.
addFrom ResourceBundle(Severity severity, String key) — adds a Faces
message, rendered from a message template defined in the Seam resource bundle, which
333
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
may contain EL expressions.
clear() — clears all messages.
org.jboss.seam .faces.redirect
A convenient API for performing redirects with parameters. T his is particularly useful for
bookmarkable search results screens.
redirect.viewId — the JSF view ID to redirect to.
redirect.conversationPropagationEnabled — determines whether the
conversation will propagate across the redirect.
redirect.param eters — a map of request parameter name to value, to be passed in
the redirect request.
execute() — performs the redirect immediately.
captureCurrentRequest() — stores the view ID and request parameters of the current
GET request (in the conversation context) for later use by calling execute().
org.jboss.seam .faces.httpError
A convenient API for sending HT T P errors.
org.jboss.seam .ui.renderStam pStore
A component which maintains a collection of render stamps. A render stamp indicates whether
a rendered form has been submitted. T his is particularly useful in conjunction with JSF's clientside state saving method, because the form's status (posted or unposted) is controlled by the
server rather than the client.
Client-side state saving is often used to unbind this check from the session. T o do so, you will
need an implementation that can store render stamps within the application (valid while the
application runs), or the database (valid across server restarts).
m axSize — T he maximum number of stamps to keep in the store. T he default is 100.
T he JSF components are installed when the class javax.faces.context.FacesContext is
available on the classpath.
29.3. Utility components
T he following components provide various functions that are useful across a broad range of
applications.
org.jboss.seam .core.events
An API for raising events that can be observed via @ Observer methods, or method bindings in
com ponents.xm l.
raiseEvent(String type) — raises an event of a particular type and distributes it to all
observers.
raiseAsynchronousEvent(String type) — raises an event to be processed
asynchronously by the EJB3 timer service.
raiseT im edEvent(String type, ....) — schedules an event to be processed
asynchronously by the EJB3 timer service.
addListener(String type, String m ethodBinding) — adds an observer for a
particular event type.
org.jboss.seam .core.interpolator
334
Chapter 29. Built-in Seam components
An API for interpolating the values of JSF EL expressions in Strings.
interpolate(String tem plate) — scans the template for JSF EL expressions of the
form #{...} and replaces them with their evaluated values.
org.jboss.seam .core.expressions
An API for creating value and method bindings.
createValueBinding(String expression) — creates a value binding object.
createMethodBinding(String expression) — creates a method binding object.
All of these components are always installed.
29.4. Components for internationalization and themes
T hese components make it easy to build internationalized user interfaces using Seam.
org.jboss.seam .core.locale
T he Seam locale.
org.jboss.seam .international.tim ezone
T he Seam timezone. T he timezone is session-scoped.
org.jboss.seam .core.resourceBundle
T he Seam resource bundle. T he resource bundle is stateless. T he Seam resource bundle
performs a depth-first search for keys in a list of Java resource bundles.
org.jboss.seam .core.resourceLoader
T he resource loader provides access to application resources and resource bundles.
resourceLoader.bundleNam es — the names of the Java resource bundles to search
when the Seam resource bundle is used. Default to m essages.
org.jboss.seam .international.localeSelector
Supports selection of the locale either at configuration time, or by the user at runtime.
select() — selects the specified locale.
localeSelector.locale — the actual java.util.Locale.
localeSelector.localeString — the string representation of the locale.
localeSelector.language — the language for the specified locale.
localeSelector.country — the country for the specified locale.
localeSelector.variant — the variant for the specified locale.
localeSelector.supportedLocales — a list of SelectItem s representing the
supported locales listed in jsf-config.xm l.
localeSelector.cookieEnabled — specifies that the locale selection should be
persisted via a cookie.
org.jboss.seam .international.tim ezoneSelector
Supports selection of the timezone either at configuration time, or by the user at runtime.
select() — selects the specified locale.
335
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
tim ezoneSelector.tim ezone — the actual java.util.T im eZone.
tim ezoneSelector.tim eZoneId — the string representation of the timezone.
tim ezoneSelector.cookieEnabled — specifies that the timezone selection should
be persisted via a cookie.
org.jboss.seam .international.m essages
A map containing internationalized messages rendered from message templates defined in the
Seam resource bundle.
org.jboss.seam .them e.them eSelector
Supports selection of the theme either at configuration time, or by the user at runtime.
select() — select the specified theme.
them e.availableT hem es — the list of defined themes.
them eSelector.them e — the selected theme.
them eSelector.them es — a list of SelectItem s representing the defined themes.
them eSelector.cookieEnabled — specifies that the theme selection should be
persisted via a cookie.
org.jboss.seam .them e.them e
A map containing theme entries.
All of these components are always installed.
29.5. Components for controlling conversations
T he following components allow you to control conversations through either the application or the user
interface.
org.jboss.seam .core.conversation
An API for controlling the current Seam conversation's attributes from within the application.
getId() — returns the current conversation ID.
isNested() — specifies whether the current conversation is a nested conversation.
isLongRunning() — specifies whether the current conversation is a long-running
conversation.
getId() — returns the current conversation ID.
getParentId() — returns the conversation ID of the parent conversation.
getRootId() — returns the conversation ID of the root conversation.
setT im eout(int tim eout) — sets the timeout for the current conversation.
setViewId(String outcom e) — sets the view ID to use when switching back to the
current conversation from the conversation switcher, conversation list, or breadcrumbs.
setDescription(String description) — sets the description of the current
conversation to be displayed in the conversation switcher, conversation list, or
breadcrumbs.
redirect() — redirects to the last well-defined view ID for this conversation. T his is
useful after log in challenges.
leave() — exits the scope of this conversation, without actually ending the conversation.
begin() — begins a long-running conversation (equivalent to @ Begin).
end() — ends a long-running conversation (equivalent to @ End).
pop() — pops the conversation stack, and returns to the parent conversation.
336
Chapter 29. Built-in Seam components
root() — returns to the root conversation of the conversation stack.
changeFlushMode(FlushModeT ype flushMode) — changes the flush mode of the
conversation.
org.jboss.seam .core.conversationList
A manager component for the conversation list.
org.jboss.seam .core.conversationStack
A manager component for the conversation stack (breadcrumbs).
org.jboss.seam .faces.switcher
T he conversation switcher.
All of these components are always installed.
29.6. Security-related components
T hese components relate to web-tier security.
org.jboss.seam .web.userPrincipal
A manager component for the current user Principal.
org.jboss.seam .web.isUserInRole
Allows JSF pages to choose to render a control, depending upon the roles available to the
current principal, for example: <h:com m andButton value="edit"
rendered="#{isUserInRole['adm in']}"/>.
29.7. JMS-related components
T hese components are for use with managed T opicPublishers and QueueSenders (see below).
org.jboss.seam .jm s.queueSession
A manager component for a JMS QueueSession.
org.jboss.seam .jm s.topicSession
A manager component for a JMS T opicSession.
29.8. Mail-related components
T hese components are for use with Seam's Email support.
org.jboss.seam .m ail.m ailSession
A manager component for a JavaMail Session. T he session can be either looked up in the
JNDI context (by setting the sessionJndiNam e property), or created from the configuration
options. In this case, the host is mandatory.
org.jboss.seam .m ail.m ailSession.host — the hostname of the SMT P server to
use.
org.jboss.seam .m ail.m ailSession.port — the port of the SMT P server to use.
337
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
org.jboss.seam .m ail.m ailSession.usernam e — the username to use to connect
to the SMT P server.
org.jboss.seam .m ail.m ailSession.password — the password to use to connect
to the SMT P server.
org.jboss.seam .m ail.m ailSession.debug — enables JavaMail debugging (very
verbose).
org.jboss.seam .m ail.m ailSession.ssl — enables SSL connection to SMT P (will
default to port 465).
org.jboss.seam .m ail.m ailSession.tls — enables T LS support in the mail
session. Defaults to true.
org.jboss.seam .m ail.m ailSession.sessionJndiNam e — name under which a
javax.mail.Session is bound to JNDI. If this is supplied, all other properties will be ignored.
29.9. Infrastructural components
T hese components provide critical platform infrastructure. You can install a component that is not
installed by default by setting install="true" on the component in com ponents.xm l.
org.jboss.seam .core.init
T his component contains initialization settings for Seam. Always installed.
org.jboss.seam .core.init.jndiPattern — the JNDI pattern used for looking up
session beans.
org.jboss.seam .core.init.debug — enables Seam debug mode. During production,
this should be set to false; you may see errors if the system is placed under any load
while debug is enabled.
org.jboss.seam .core.init.clientSideConversations — when true, saves
conversation context variables in the client rather than the HttpSession.
org.jboss.seam .core.m anager
An internal component for Seam page and conversation context management. Always installed.
org.jboss.seam .core.m anager.conversationT im eout — the conversation
context timeout in milliseconds.
org.jboss.seam .core.m anager.concurrentRequestT im eout — the maximum
wait time for a thread attempting to gain a lock on the long-running conversation context.
org.jboss.seam .core.m anager.conversationIdParam eter — the request
parameter used to propagate the conversation ID. T he default is conversationId.
org.jboss.seam .core.m anager.conversationIsLongRunningParam eter —
the request parameter used to propagate that the conversation is long-running. T he default
is conversationIsLongRunning.
org.jboss.seam .core.m anager.defaultFlushMode — sets the default flush mode
on any Seam-managed Persistence Context. T his defaults to AUT O.
org.jboss.seam .navigation.pages
An internal component for Seam workspace management. Always installed.
org.jboss.seam .navigation.pages.noConversationViewId — specifies the
view ID to redirect to, globally, when a conversation entry is not found on the server side.
org.jboss.seam .navigation.pages.loginViewId — specifies the view ID to
redirect to, globally, when an unauthenticated user attempts to access a protected view.
org.jboss.seam .navigation.pages.httpPort — specifies the port to use, globally,
338
Chapter 29. Built-in Seam components
when the HT T P scheme is requested.
org.jboss.seam .navigation.pages.httpsPort — specifies the port to use,
globally, when the HT T PS scheme is requested.
org.jboss.seam .navigation.pages.resources — specifies a list of resources to
search for pages.xm l style resources. T he default is WEB-INF/pages.xm l.
org.jboss.seam .core.conversationEntries
An internal session-scoped component that records active long-running conversations between
requests.
org.jboss.seam .faces.facesPage
An internal page-scoped component that records the conversation context associated with a
page.
org.jboss.seam .persistence.persistenceContexts
An internal component that records the persistence contexts used in the current conversation.
org.jboss.seam .jm s.queueConnection
Manages a JMS QueueConnection. T his is installed whenever managed QueueSender is
installed.
org.jboss.seam .jm s.queueConnection.queueConnectionFactoryJndiNam e
— specifies the JNDI name of a JMS QueueConnectionFactory. T he default is
UIL2ConnectionFactory.
org.jboss.seam .jm s.topicConnection
Manages a JMS T opicConnection. T his is installed whenever managed T opicPublisher
is installed.
org.jboss.seam .jm s.topicConnection.topicConnectionFactoryJndiNam e
— specifies the JNDI name of a JMS T opicConnectionFactory. T he default is
UIL2ConnectionFactory.
org.jboss.seam .persistence.persistenceProvider
An abstraction layer for non-standardized features of the JPA provider.
org.jboss.seam .core.validators
Caches instances of Hibernate Validator ClassValidator.
org.jboss.seam .faces.validation
Lets the application determine whether validation succeeded.
org.jboss.seam .debug.introspector
Provides support for the Seam Debug Page.
org.jboss.seam .debug.contexts
Provides support for the Seam Debug Page.
org.jboss.seam .exception.exceptions
An internal component for exception handling.
339
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
org.jboss.seam .transaction.transaction
An API for controlling transactions and abstracting the underlying transaction management
implementation behind a JT A-compatible interface.
org.jboss.seam .faces.safeActions
Determines that an action expression in an incoming URL is safe by checking that the action
expression exists in the view.
29.10. Miscellaneous components
Additional, uncategorized components.
org.jboss.seam .async.dispatcher
Dispatches stateless session beans for asynchronous methods.
org.jboss.seam .core.im age
Used for image manipulation and interrogation.
org.jboss.seam .core.uiCom ponent
Manages a map of UIComponents keyed by component ID.
29.11. Special components
Certain Seam component classes can be installed multiple times under names specified in the Seam
configuration. For example, the following lines in com ponents.xm l install and configure two Seam
components:
<component name="bookingDatabase"
class="org.jboss.seam.persistence.ManagedPersistenceContext">
<property name="persistenceUnitJndiName">
java:/comp/emf/bookingPersistence
</property>
</component>
<component name="userDatabase"
class="org.jboss.seam.persistence.ManagedPersistenceContext">
<property name="persistenceUnitJndiName">
java:/comp/emf/userPersistence
</property>
</component>
T he Seam component names are bookingDatabase and userDatabase.
<entityManager> , org.jboss.seam .persistence.ManagedPersistenceContext
A manager component for a conversation-scoped, managed EntityManager with an
extended persistence context.
<entityManager>.entityManagerFactory — a value binding expression that evaluates to an
instance of EntityManagerFactory.
<entityManager>.persistenceUnitJndiName — the JNDI name of the entity manager factory.
By default, this is java:/<m anagedPersistenceContext> .
34 0
Chapter 29. Built-in Seam components
<entityManagerFactory> , org.jboss.seam .persistence.EntityManagerFactory
Manages a JPA EntityManagerFactory. T his is most useful when using JPA outside of an
environment with EJB3 support.
entityManagerFactory.persistenceUnitNam e — the name of the persistence unit.
See the API JavaDoc for further configuration properties.
<session> , org.jboss.seam .persistence.ManagedSession
A manager component for a conversation-scoped, managed Hibernate Session.
<session>.sessionFactory — a value binding expression that evaluates to an instance of
SessionFactory.
<session>.sessionFactoryJndiName — the JNDI name of the session factory. By default, this
is java:/<m anagedSession>.
<sessionFactory> , org.jboss.seam .persistence.HibernateSessionFactory
Manages a Hibernate SessionFactory.
<sessionFactory>.cfgResourceNam e — specifies the path to the configuration file.
By default, this is hibernate.cfg.xm l.
See the API JavaDoc for further configuration properties.
<managedQueueSender> , org.jboss.seam .jm s.ManagedQueueSender
A manager component for an event scoped managed JMS QueueSender.
<managedQueueSender>.queueJndiName — the JNDI name of the JMS queue.
<managedTopicPublisher> , org.jboss.seam .jm s.ManagedT opicPublisher
A manager component for an event-scoped, managed JMS T opicPublisher.
<managedTopicPublisher>.topicJndiName — the JNDI name of the JMS topic.
<managedWorkingMemory> , org.jboss.seam .drools.ManagedWorkingMem ory
A manager component for a conversation-scoped, managed Drools WorkingMem ory.
<managedWorkingMemory>.ruleBase — a value expression that evaluates to an instance
of RuleBase.
<ruleBase> , org.jboss.seam .drools.RuleBase
A manager component for an application-scoped Drools RuleBase. Note that this does not
support dynamic installation of new rules, so it is not appropriate for use in production.
<ruleBase>.ruleFiles — a list of files containing Drools rules.
<ruleBase>.dslFile — a Drools DSL definition.
34 1
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Chapter 30. Seam JSF controls
Seam includes a number of JavaServer Faces (JSF) controls to complement built-in controls, and
controls from other third-party libraries. We recommend JBoss RichFaces and Apache MyFaces T rinidad
tag libraries for use with Seam. We do not recommend the use of the T omahawk tag library.
30.1. Tags
T o use these tags, define the s namespace in your page as follows (Facelets only):
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.org/schema/seam/taglib">
T he user interface example demonstrates the use of a number of these tags.
30.1.1. Navigation Controls
30.1.1.1. <s:button>
Description
A button that supports invoking an action with control over conversation propagation. This button does
not submit the form.
Attributes
value — the button label.
action — a method binding that specifies the action listener.
view — specifies the JSF view ID to link to.
fragm ent — specifies the fragment identifier to link to.
disabled — specifies whether the link is disabled.
propagation — determines the conversation propagation style: begin, join, nest, none or
end.
Usage
<s:button id="cancel" value="Cancel" action="#{hotelBooking.cancel}"/>
You can specify both view and action on <s:link
/>
. In this case, the action will be called once the redirect to the specified view has occurred.
T he use of action listeners (including the default JSF action listener) is not supported with <s:button
/>
.
30.1.1.2. <s:conversationId>
Description
Adds the conversation ID to a JSF link or button, for example:
<h:com m andLink />
, <s:button />
.
Attributes
None.
34 2
Chapter 30. Seam JSF controls
30.1.1.3. <s:taskId>
Description
Adds the task ID to an output link (or similar JSF control) when the task is available via #{task}.
Attributes
None.
30.1.1.4 . <s:link>
Description
A link that supports invoking an action with control over conversation propagation. This does not submit
the form.
T he use of action listeners (including the default JSF action listener) is not supported with <s:link />
.
Attributes
value — specifies the link label.
action — a method binding that specifies the action listener.
view — specifies the JSF view ID to link to.
fragm ent — specifies the fragment identifier to link to.
disabled — specifies whether the link is disabled.
propagation — determines the conversation propagation style: begin, join, nest, none or
end.
Usage
<s:link id="register" view="/register.xhtml" value="Register New User"/>
You can specify both view and action on <s:link
/>
. In this case, the action will be called once the redirect to the specified view has occurred.
30.1.1.5. <s:conversationPropagation>
Description
Customizes the conversation propagation for a command link or button (or similar JSF control). Facelets
only.
Attributes
type — determines the conversation propagation style: begin, join, nest, none or end.
Usage
<h:commandButton value="Apply" action="#{personHome.update}">
<s:conversationPropagation type="join" />
</h:commandButton>
30.1.1.6. <s:defaultAction>
Description
Specifies the default action to run when the form is submitted using the enter key.
34 3
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Currently you this can only be nested inside buttons, such as <h:com m andButton
/>
, <a:com m andButton />
or <tr:com m andButton />
).
You must specify an ID on the action source, and only one default action can be specified per form.
Attributes
None.
Usage
<h:commandButton id="foo" value="Foo" action="#{manager.foo}">
<s:defaultAction />
</h:commandButton>
30.1.2. Converters and Validators
30.1.2.1. <s:convertDateT im e>
Description
Perform date or time conversions in the Seam timezone.
Attributes
None.
Usage
<h:outputText value="#{item.orderDate}">
<s:convertDateTime type="both" dateStyle="full"/>
</h:outputText>
30.1.2.2. <s:convertEntity>
Description
Assigns an entity converter to the current component. T his is useful for radio button and dropdown
controls.
T he converter works with any managed entity - either simple or composite. If the converter cannot find
the items declared in the JSF controls upon form submission, a validation error will occur.
Attributes
None.
Configuration
You must use Seam managed transactions (see Section 9.2, “Seam managed transactions”) with
<s:convertEntity
/>
.
Your Managed Persistence Context must be named entityManager — if it is not, you can change its
named in com ponents.xm l:
34 4
Chapter 30. Seam JSF controls
<components xmlns="http://jboss.org/schema/seam/components"
xmlns:ui="http://jboss.org/schema/seam/ui">
<ui:jpa-entity-loader entity-manager="#{em}" />
If you are using a Managed Hibernate Session, you must also set this in com ponents.xm l:
<components xmlns="http://jboss.org/schema/seam/components"
xmlns:ui="http://jboss.org/schema/seam/ui">
<ui:hibernate-entity-loader />
Your Managed Hibernate Session must be named session — if it is not, you can change its named in
com ponents.xm l:
<components xmlns="http://jboss.org/schema/seam/components"
xmlns:ui="http://jboss.org/schema/seam/ui">
<ui:hibernate-entity-loader session="#{hibernateSession}" />
T o use multiple entity managers with the entity converter, create a copy of the entity converter for each
entity manager in com ponents.xm l. T he entity converter delegates to the entity loader to perform
persistence operations like so:
<components xmlns="http://jboss.org/schema/seam/components"
xmlns:ui="http://jboss.org/schema/seam/ui">
<ui:entity-converter name="standardEntityConverter"
entity-loader="#{standardEntityLoader}" />
<ui:jpa-entity-loader name="standardEntityLoader"
entity-manager="#{standardEntityManager}" />
<ui:entity-converter name="restrictedEntityConverter"
entity-loader="#{restrictedEntityLoader}" />
<ui:jpa-entity-loader name="restrictedEntityLoader"
entity-manager="#{restrictedEntityManager}" />
<h:selectOneMenu value="#{person.continent}">
<s:selectItems value="#{continents.resultList}"
var="continent" label="#{continent.name}" />
<f:converter converterId="standardEntityConverter" />
</h:selectOneMenu>
Usage
<h:selectOneMenu value="#{person.continent}" required="true">
<s:selectItems value="#{continents.resultList}" var="continent"
label="#{continent.name}" noSelectionLabel="Please Select..."/>
<s:convertEntity />
</h:selectOneMenu>
30.1.2.3. <s:convertEnum >
Description
Assigns an enum converter to the current component. T his is primarily useful for radio button and
dropdown controls.
Attributes
None.
34 5
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Usage
<h:selectOneMenu value="#{person.honorific}">
<s:selectItems value="#{honorifics}" var="honorific"
label="#{honorific.label}" noSelectionLabel="Please select" />
<s:convertEnum />
</h:selectOneMenu>
30.1.2.4 . <s:convertAtom icBoolean>
Description
javax.faces.convert.Converter for java.util.concurrent.atom ic.Atom icBoolean.
Attributes
None.
Usage
<h:outputText value="#{item.valid}">
<s:convertAtomicBoolean />
</h:outputText>
30.1.2.5. <s:convertAtom icInteger>
Description
javax.faces.convert.Converter for java.util.concurrent.atom ic.Atom icInteger.
Attributes
None.
Usage
<h:outputText value="#{item.id}">
<s:convertAtomicInteger />
</h:outputText>
30.1.2.6. <s:convertAtom icLong>
Description
javax.faces.convert.Converter for java.util.concurrent.atom ic.Atom icLong.
Attributes
None.
Usage
<h:outputText value="#{item.id}">
<s:convertAtomicLong />
</h:outputText>
30.1.2.7. <s:validateEquality>
Description
Validates that an input control's parent's value is equal, or not equal, to the referenced control's value.
34 6
Chapter 30. Seam JSF controls
Attributes
for — T he ID of a control to validate against.
m essage — Message to show on failure.
required — False will disable a check that a value at all is inputted in fields.
m essageId — Message ID to show on failure.
operator — T he operator to use when comparing values. Valid operators are:
equal — validates that value.equals(forValue).
not_equal — validates that !value.equals(forValue)
greater — validates that ((Comparable)value).compareT o(forValue) > 0
greater_or_equal — validates that ((Comparable)value).compareT o(forValue) >= 0
less — validates that ((Comparable)value).compareT o(forValue) < 0
less_or_equal — validates that ((Comparable)value).compareT o(forValue) <= 0
Usage
<h:inputText id="name" value="#{bean.name}"/>
<h:inputText id="nameVerification" >
<s:validateEquality for="name" />
</h:inputText>
30.1.2.8. <s:validate>
Description
A non-visual control that validates a JSF input field against the bound property with the Hibernate
Validator.
Attributes
None.
Usage
<h:inputText id="userName" required="true" value="#{customer.userName}">
<s:validate />
</h:inputText>
<h:message for="userName" styleClass="error" />
30.1.2.9. <s:validateAll>
Description
A non-visual control that validates all child JSF input fields against their bound properties with the
Hibernate Validator.
Attributes
None.
Usage
34 7
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<s:validateAll>
<div class="entry">
<h:outputLabel for="username">Username:</h:outputLabel>
<h:inputText id="username" value="#{user.username}" required="true"/>
<h:message for="username" styleClass="error" />
</div>
<div class="entry">
<h:outputLabel for="password">Password:</h:outputLabel>
<h:inputSecret id="password" value="#{user.password}"
required="true"/>
<h:message for="password" styleClass="error" />
</div>
<div class="entry">
<h:outputLabel for="verify">Verify Password:</h:outputLabel>
<h:inputSecret id="verify" value="#{register.verify}"
required="true"/>
<h:message for="verify" styleClass="error" />
</div>
</s:validateAll>
30.1.3. Formatting
30.1.3.1. <s:decorate>
Description
"Decorates" a JSF input field when validation fails or when required="true" is set.
Attributes
tem plate — the Facelets template used to decorate the component.
enclose — if true, the template used to decorate the input field is enclosed by the element
specified with the "element" attribute. (By default, this is a div element.)
elem ent — the element enclosing the template that decorates the input field. By default, the
template is enclosed with a div element.
#{invalid} and #{required} are available inside s:decorate. #{required} evaluates to true
if the input component being decorated is set to required. #{invalid} evaluates to true if a
validation error occurs.
Usage
<s:decorate template="edit.xhtml">
<ui:define name="label">Country:</ui:define>
<h:inputText value="#{location.country}" required="true"/>
</s:decorate>
34 8
Chapter 30. Seam JSF controls
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:s="http://jboss.org/schema/seam/taglib">
<div>
<s:label styleClass="#{invalid?&#39;error&#39;:&#39;&#39;}">
<ui:insert name="label"/>
<s:span styleClass="required" rendered="#{required}">*</s:span>
</s:label>
<span class="#{invalid?&#39;error&#39;:&#39;&#39;}">
<s:validateAll>
<ui:insert/>
</s:validateAll>
</span>
<s:message styleClass="error"/>
</div>
</ui:composition>
30.1.3.2. <s:div>
Description
Renders a HT ML <div>
.
Attributes
None.
Usage
<s:div rendered="#{selectedMember == null}">
Sorry, but this member does not exist.
</s:div>
30.1.3.3. <s:span>
Description
Renders a HT ML <span>
.
Attributes
title — T itle for a span.
Usage
<s:span styleClass="required" rendered="#{required}" title="Small tooltip">
*
</s:span>
30.1.3.4 . <s:fragm ent>
Description
A non-rendering component useful for enabling/disabling rendering of it's children.
Attributes
None.
34 9
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Usage
<s:fragment rendered="#{auction.highBidder ne null}">
Current bid:
</s:fragment>
30.1.3.5. <s:label>
Description
"Decorates" a JSF input field with the label. T he label is placed inside the HT ML <label>
tag, and is associated with the nearest JSF input component. It is often used with <s:decorate>
.
Attributes
style — T he control's style.
styleClass — T he control's style class.
Usage
<s:label styleClass="label"> Country: </s:label>
<h:inputText value="#{location.country}" required="true"/>
30.1.3.6. <s:m essage>
Description
"Decorates" a JSF input field with the validation error message.
Attributes
None.
Usage
<f:facet name="afterInvalidField">
<s:span>
Error:
<s:message/>
</s:span>
</f:facet>
30.1.4. Seam Text
30.1.4 .1. <s:validateForm attedT ext>
Description
Checks that the submitted value is valid Seam T ext.
Attributes
None.
30.1.4 .2. <s:form attedT ext>
Description
Outputs Seam Text, a rich text markup useful for blogs, wikis and other applications that might use rich
text. See the Seam T ext chapter for full usage.
350
Chapter 30. Seam JSF controls
Attributes
value — an EL expression specifying the rich text markup to render.
Usage
<s:formattedText value="#{blog.text}"/>
Example
30.1.5. Form support
30.1.5.1. <s:token>
Description
Produces a random token to insert into a hidden form field in order to secure JSF form posts against
Cross-Site Request Forgery (XSRF) attacks. T he browser must have cookies enabled to submit forms
that include this component.
Attributes
requireSession — indicates whether the session ID should be included in the form signature to
bind the token to the session. T he default value is false, but this should only be used if Facelets is
in "build before restore" mode. ("Build before restore" is the default mode in JSF 2.0.)
enableCookieNotice — indicates that a JavaScript check should be inserted into the page to
verify that cookies are enabled in the browser. If cookies are not enabled, present a notice to the
user that form posts will not work. T he default value is false.
allowMultiplePosts — indicates whether the same form is allowed to submit multiple times with
the same signature (where the view has not changed). T his is often required when the form is
performing AJAX calls without rerendering itself or the UIT oken component. It is better to rerender the
UIT oken component upon any AJAX call where the UIT oken component would be processed. T he
default value is false.
Usage
351
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<h:form>
<s:token enableCookieNotice="true" requireSession="false"/>
...
</h:form>
30.1.5.2. <s:enum Item >
Description
Creates a SelectItem from an enum value.
Attributes
enum Value — the string representation of the enum value.
label — the label to be used when rendering the SelectItem .
Usage
<h:selectOneRadio id="radioList"
layout="lineDirection"
value="#{newPayment.paymentFrequency}">
<s:convertEnum />
<s:enumItem enumValue="ONCE"
label="Only Once" />
<s:enumItem enumValue="EVERY_MINUTE" label="Every Minute" />
<s:enumItem enumValue="HOURLY"
label="Every Hour" />
<s:enumItem enumValue="DAILY"
label="Every Day" />
<s:enumItem enumValue="WEEKLY"
label="Every Week" />
</h:selectOneRadio>
30.1.5.3. <s:selectItem s>
Description
Creates a List<SelectItem >
from a List, Set, DataModel or Array.
Attributes
value — an EL expression specifying the data that backs the ListSelectItem >
var — defines the name of the local variable that holds the current object during iteration.
label — the label to be used when rendering the SelectItem . Can reference the var variable.
item Value — specifies the value to return to the server if this option is selected. T his is an optional
attribute. If included, var is the default object used. Can reference the var variable.
disabled — if this is set to true, the SelectItem will be rendered disabled. Can reference the
var variable.
noSelectionLabel — specifies the (optional) label to place at the top of list. If
required="true" is also specified then selecting this value will cause a validation error.
hideNoSelectionLabel — if true, the noSelectionLabel will be hidden when a value is
selected.
Usage
<h:selectOneMenu value="#{person.age}" converter="ageConverter">
<s:selectItems value="#{ages}" var="age" label="#{age}" />
</h:selectOneMenu>
30.1.5.4 . <s:fileUpload>
Description
352
Chapter 30. Seam JSF controls
Renders a file upload control. T his control must be used within a form with an encoding type of
m ultipart/form -data:
<h:form enctype="multipart/form-data">
For multipart requests, the Seam Multipart servlet filter must also be configured in web.xm l:
<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>/*</url-pattern>
</filter-mapping>
Configuration
T he following configuration options for multipart requests can be configured in com ponents.xm l:
createT em pFiles — if this option is set to true, uploaded files are streamed to a temporary file
rather than being held in memory.
m axRequestSize — the maximum size of a file upload request, in bytes.
Here's an example:
<component class="org.jboss.seam.web.MultipartFilter">
<property name="createTempFiles">true</property>
<property name="maxRequestSize">1000000</property>
</component>
Attributes
data — specifies the value binding that receives the binary file data. T he receiving field must be
declared as either a byte[] or InputStream .
contentT ype — an optional attribute specifying the value binding that receives the file's content
type.
fileNam e — an optional attribute specifying the value binding that receives the filename.
fileSize — an optional attribute specifying the value binding that receives the file size.
accept — a comma-separated list of acceptable content types, for example,
"im ages/png,im ages/jpg", "im ages/* ". T he types listed may not be supported by the
browser.
style — T he control's style.
styleClass — T he control's style class.
Usage
<s:fileUpload id="picture"
data="#{register.picture}" accept="image/png"
contentType="#{register.pictureContentType}" />
30.1.6. Other
30.1.6.1. <s:cache>
Description
Caches the rendered page fragment using JBoss Cache. Note that <s:cache>
actually uses the instance of JBoss Cache managed by the built-in pojoCache component.
353
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Attributes
key — the key to cache rendered content, often a value expression. For example, if we were caching
a page fragment that displays a document, we might use key="Docum ent-#{docum ent.id}".
enabled — a value expression that determines whether the cache should be used.
region — specifies the JBoss Cache node to use. Different nodes can have different expiry
policies.
Usage
<s:cache key="entry-#{blogEntry.id}" region="pageFragments">
<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>
</s:cache>
30.1.6.2. <s:resource>
Description
A tag that acts as a file download provider. It must be alone in the JSF page. T o use this control, you
must configure web.xm l as follows:
Configuration
<servlet>
<servlet-name>Document Store Servlet</servlet-name>
<servlet-class>
org.jboss.seam.document.DocumentStoreServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Document Store Servlet</servlet-name>
<url-pattern>/seam/docstore/*</url-pattern>
</servlet-mapping>
Attributes
data — specifies data that should be downloaded. May be a java.util.File, an InputStream or a byte
array.
fileNam e — the filename of the file to be served.
contentT ype — the content type of the file to be downloaded.
disposition — the disposition to use. T he default disposition is inline.
Usage
T he tag is used as follows:
354
Chapter 30. Seam JSF controls
<s:resource xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.org/schema/seam/taglib"
data="#{resources.data}" contentType="#{resources.contentType}"
fileName="#{resources.fileName}" />
Here, the bean named resources is some backing bean that, given some request parameters, serves
a specific file — see s:download.
30.1.6.3. <s:download>
Description
Builds a REST ful link to a <s:resource>
. Nested f:param build up the url.
src — Resource file serving files.
Attributes
<s:download src="/resources.xhtml">
<f:param name="fileId" value="#{someBean.downloadableFileId}"/>
</s:download>
T his produces a link of a similar form to the following: http://localhost/resources.seam ?
fileId=1
30.1.6.4 . <s:graphicIm age>
Description
An extended <h:graphicIm age>
that allows the image to be created in a Seam Component. It is possible to transform the image further.
All <h:graphicIm age>
attributes are supported, in addition to:
Attributes
value — specifies the image to display. Can be a path String (loaded from the classpath), a
byte[], a java.io.File, a java.io.InputStream or a java.net.URL. Currently supported
image formats are im age/bm p, im age/png, im age/jpeg and im age/gif.
fileNam e — specifies the filename of the image. T his name should be unique. If left unspecified, a
unique filename will be generated for the image.
Transformations
T o transform the image, nest a tag specifying which transformation to apply. Seam currently supports
the following transformation tags:
<s:transform Im ageSize>
width — specifies the new width of the image.
height — specifies the new height of the image.
m aintainRatio — if true, and one of either width or height is specified, the image
will be resized to maintain the height:width aspect ratio.
factor — scales the image by the specified factor.
<s:transform Im ageBlur>
355
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
radius — performs a convolution blur with the specified radius.
<s:transform Im ageT ype>
contentT ype — alters the image type to either im age/jpeg or im age/png.
You can also create your own image transformation. Create a UICom ponent that implements
org.jboss.seam .ui.graphicIm age.Im ageT ransform . Inside the applyT ransform ()method,
use im age.getBufferedIm age() to get the original image and im age.setBufferedIm age() to
set your transformed image. T ransforms are applied in the order specified in the view.
Usage
<s:graphicImage rendered="#{auction.image ne null}"
value="#{auction.image.data}">
<s:transformImageSize width="200" maintainRatio="true"/>
</s:graphicImage>
30.1.6.5. <s:rem ote>
Description
Generates the Javascript stubs required to use Seam Remoting.
Attributes
include — a comma-separated list of the component names (or fully qualified class names) for
which to generate Seam Remoting Javascript stubs. See Chapter 23, Remoting for more details.
Usage
<s:remote include="customerAction,accountAction,com.acme.MyBean"/>
30.2. Annotations
Seam also provides annotations to let you use Seam components as JSF converters and validators:
@ Converter
@Name("itemConverter")
@BypassInterceptors
@Converter
public class ItemConverter implements Converter {
@Transactional
public Object getAsObject(FacesContext context, UIComponent cmp,
String value) {
EntityManager entityManager =
(EntityManager) Component.getInstance("entityManager");
entityManager.joinTransaction(); // Do the conversion
}
public String getAsString(FacesContext context, UIComponent cmp,
Object value) {
// Do the conversion
}
}
<h:inputText value="#{shop.item}" converter="itemConverter" />
Registers the Seam component as a JSF converter. Here, the converter accesses the JPA
EntityManager inside a JT A transaction when converting the value back to its object
356
Chapter 30. Seam JSF controls
representation.
@ Validator
@Name("itemValidator")
@BypassInterceptors
@org.jboss.seam.annotations.faces.Validator
public class ItemValidator implements javax.faces.validator.Validator {
public void validate(FacesContext context, UIComponent cmp,
Object value) throws ValidatorException {
ItemController ItemController =
(ItemController) Component.getInstance("itemController");
boolean valid = itemController.validate(value);
if (!valid) {
throw ValidatorException("Invalid value " + value);
}
}
}
<h:inputText value="#{shop.item}" validator="itemValidator" />
Registers the Seam component as a JSF validator. Here, the validator injects another Seam
component; the injected component is used to validate the value.
357
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Chapter 31. JBoss EL
Seam uses JBoss EL to provide an extension to the standard Unified Expression Language (EL). T his
provides several enhancements to the expressiveness and power of EL expressions.
31.1. Parameterized Expressions
Standard EL 2.1 does not allow you to use a method with user defined parameters — of course, JSF
listener methods (e.g. a valueChangeListener) take parameters provided by JSF. Standard EL 2.2,
which is in Java EE 6, allows it now. So you don't have to use JBoss EL enhancements.
You can still use JBoss EL instead of standard EL 2.2 from Java EE 6 by setting up
com .sun.faces.expressionFactory in web.xm l:
<context-param>
<param-name>com.sun.faces.expressionFactory</param-name>
<param-value>org.jboss.el.ExpressionFactoryImpl</param-value>
</context-param>
JBoss EL and EL 2.2 removed this restriction. For example:
<h:commandButton action="#{hotelBooking.bookHotel(hotel)}" value="Book Hotel"/>
@Name("hotelBooking")
public class HotelBooking
{
public String bookHotel(Hotel hotel)
{
// Book the hotel
}
}
31.1.1. Usage
As in method calls from Java, parameters are surrounded by parentheses, and separated by commas:
<h:commandButton action="#{hotelBooking.bookHotel(hotel, user)}"
value="Book Hotel"/>
Here, the parameters hotel and user will be evaluated as value expressions and passed to the
bookHotel() method of the component.
Any value expression can be used as a parameter:
<h:commandButton action="#{hotelBooking.bookHotel(hotel.id,
user.username)}"
value="Book Hotel"/>
When the page is rendered, the parameter names —hotel.id and user.usernam e —are stored,
and evaluated as value expressions when the page is submitted. Objects cannot be passed as
parameters.
Parameters must be available both when the page is rendered and when it is submitted. If the arguments
cannot be resolved at page submission time, the action method will be called with null arguments.
You can also pass literal strings using single quotes:
<h:commandLink action="#{printer.println('Hello world!')}"
value="Hello"/>
Unified EL also supports value expressions, which are used to bind a field to a backing bean. Value
358
Chapter 31. JBoss EL
expressions use JavaBean naming conventions and expect a getter/setter pair. JSF often expects a
value expression where only retrieval (get) is required (for example, in the rendered attribute), but
many objects do not have appropriately named property accessors, or do not require parameters.
JBoss EL removes this restriction by allowing values to be retrieved using the method syntax. For
example:
<h:outputText value="#{person.name}"
rendered="#{person.name.length() > 5}" />
You can access the size of a collection in a similar manner:
#{searchResults.size()}
In general, any expression of the form #{obj.property} would be identical to the expression
#{obj.getProperty()}.
Parameters are also allowed. T he following example calls the productsByColorMethod with a literal
string argument:
#{controller.productsByColor('blue')}
31.1.2. Limitations and Hints
JBoss EL does have several limitations:
Incompatibility with JSP 2.1 —JBoss EL cannot currently be used with JSP 2.1, because the compiler
rejects expressions that include parameters.
Use inside iterative components —Components like <c:forEach />
and <ui:repeat />
iterate over a list or array, exposing each item in the list to nested components. T his is effective if
you are selecting a row with a <h:com m andButton />
or <h:com m andLink />
like so:
@Factory("items")
public List<Item> getItems() {
return entityManager.createQuery("select ...").getResultList();
}
<h:dataTable value="#{items}" var="item">
<h:column>
<h:commandLink value="Select #{item.name}"
action="#{itemSelector.select(item})" />
</h:column>
</h:dataTable>
However, if you want to use <s:link />
or <s:button />
you must expose the items as a DataModel, and use a <dataT able />
(or equivalent from a component set like <rich:dataT able />
). Neither <s:link />
or <s:button />
submit the form, so they do not produce a bookmarkable link. An additional parameter is required to
recreate the item when the action method is called. T his parameter can only be added when a data
table backed by a DataModel is used.
Calling a MethodExpression from Java code —Normally, when a MethodExpression is created,
the parameter types are passed in by JSF. However, in a method binding, JSF assumes that there
are no parameters to pass. With this extension, there is no way to know the parameter types prior to
expression evaluation. T his has two minor consequences:
359
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
When you invoke a MethodExpression in Java code, parameters you pass may be ignored.
Parameters defined in the expression will take precedence.
Ordinarily, it is safe to call m ethodExpression.getMethodInfo().getParam T ypes() at
any time. For an expression with parameters, you must first invoke the MethodExpression
before calling getParam T ypes().
Both of these cases are exceedingly rare and only apply when you want to invoke the
MethodExpression by hand in Java code.
31.2. Projection
JBoss EL supports a limited projection syntax. A projection expression maps a sub-expression across a
multi-valued (list, set, etc...) expression. For instance, the expression:
#{company.departments}
might return a list of departments. If you only need a list of department names, you must iterate over the
list to retrieve the values. JBoss EL allows this with a projection expression:
#{company.departments.{d|d.name}}
T he sub-expression is enclosed in braces. In this example, the expression d.nam e is evaluated for
each department, using d as an alias to the department object. T he result of this expression will be a list
of String values.
Any valid expression can be used in an expression, so —assuming you would use department names of
all lengths in a company —it would also be valid to write the following:
#{company.departments.{d|d.size()}}
Projections can be nested. T he following expression returns the last names of every employee in every
department:
#{company.departments.{d|d.employees.{emp|emp.lastName}}}
Nested projections can be slightly tricky, however. T he following expression appears to return a list of all
employees in all departments:
#{company.departments.{d|d.employees}}
However, it actually returns a list containing a list of the employees for each individual department. T o
combine the values, it is necessary to use a slightly longer expression:
#{company.departments.{d|d.employees.{e|e}}}
T his syntax cannot be parsed by either Facelets or JSP, so it cannot be used in XHT ML or JSP files.
Future versions of JBoss EL may accommodate the projection syntax more easily.
360
Chapter 32. Performance Tuning
Chapter 32. Performance Tuning
T his chapter contains tips for getting the best performance from your Seam application.
32.1. Bypassing Interceptors
For repetitive value bindings such as those found in JavaServer Faces (JSF) dataT ables, or in iterative
controls such as ui:repeat, the full interceptor stack is invoked upon each invocation of the
referenced Seam component. T his can substantially decrease performance, particularly if the component
is accessed many times. You can improve performance by disabling the interceptor stack for the invoked
Seam component —annotate the component class with @ BypassInterceptors.
Warning
Before you disable the interceptors, note that any component marked with
@ BypassInterceptors cannot use features such as bijection, annotated security restrictions,
or synchronization. However, you can usually compensate for the loss of these features —for
example, instead of injecting a component with @ In, you can use Com ponent.getInstance()
instead.
T he following code listing demonstrates a Seam component with its interceptors disabled:
@Name("foo")
@Scope(EVENT)
@BypassInterceptors
public class Foo {
public String getRowActions() {
// Role-based security check performed inline instead of using
// @Restrict or other security annotation
Identity.instance().checkRole("user");
// Inline code to lookup component instead of using @In
Bar bar = (Bar) Component.getInstance("bar");
String actions;
// some code here that does something
return actions;
}
}
361
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Chapter 33. Testing Seam applications
Most Seam applications will require at least two kinds of automated tests: unit tests, which test a
particular Seam component in isolation, and scripted integration tests, which exercise all Java layers of
the application (that is, everything except the view pages).
Both test types are easily written.
33.1. Unit testing Seam components
All Seam components are POJOs (Plain Old Java Objects), which simplifies unit testing. Since Seam also
emphasizes the use of bijection for component interaction and contextual object access, testing Seam
components outside their normal runtime environments is very easy.
T he following Seam Component which creates a statement of account for a customer:
@Stateless
@Scope(EVENT)
@Name("statementOfAccount")
public class StatementOfAccount
{
@In(create=true) EntityManager entityManager
private double statementTotal;
@In
private Customer customer;
@Create
public void create()
{
List<Invoice> invoices = entityManager
.createQuery("select invoice from Invoice invoice where " +
"invoice.customer = :customer")
.setParameter("customer", customer)
.getResultList();
statementTotal = calculateTotal(invoices);
}
public double calculateTotal(List<Invoice> invoices)
{
double total = 0.0;
for (Invoice invoice: invoices)
{
double += invoice.getTotal();
}
return total;
}
// getter and setter for statementTotal
}
We can test the calculateT otal method, which tests the component's business logic, as follows:
public class StatementOfAccountTest
{
@Test
public testCalculateTotal
{
List<Invoice> invoices =
generateTestInvoices(); // A test data generator
double statementTotal =
new StatementOfAccount().calculateTotal(invoices);
assert statementTotal = 123.45;
}
}
362
Chapter 33. Testing Seam applications
Note that we are not testing data retrieval or persistence, or any of the functions provided by Seam.
Here, we are testing only the logic of our POJOs. Seam components do not usually depend directly upon
container infrastructure, so most unit tests are just as easy.
If you do want to test the entire application, read the section following.
33.2. Integration testing Seam components
Integration testing is more complicated. We cannot eliminate the container infrastructure, but neither do
we want to deploy our application to an application server to run automated tests. T herefore, our testing
environment must replicate enough container infrastructure that we can exercise the entire application,
without impacting performance too heavily.
Arquillian makes it possible to run integration tests inside a real container, even without Seam T est.
363
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Example 33.1. RegisterT est.java
@RunWith(Arquillian)
public class RegisterTest
{
@Deployment
@OverProtocol("Servlet 3.0")
public static Archive<?> createDeployment()
{
EnterpriseArchive er = ShrinkWrap.create(ZipImporter.class).importFrom(new
File("../registration-ear/target/seamregistration.ear")).as(EnterpriseArchive.class);
return er;
}
@Before
public void before()
{
Lifecycle.beginCall();
}
@After
public void after(
{
Lifecycle.endCall();
}
protected void setValue(String valueExpression, Object value)
{
Expressions.instance().createValueExpression(valueExpression).setValue(value);
}
@Test
public void testRegisterComponent() throws Exception
{
setValue("#{user.username}", "1ovthafew");
setValue("#{user.name}", "Gavin King");
setValue("#{user.password}", "secret");
Register register = (Register)Component.getInstance("register");
Assert.assertEquals("success", register.register());
}
...
}
T he JUnit @ RunWith annotation must be present to run our tests with Arquillian.
Since we want to run our test in a real container, we need to specify an archive that gets
deployed.
@ OverProtocol is an Arquillian annotation to specify the protocol used for running the tests.
T he "Servlet 3.0" protocol is the recommended protocol for running Seam tests.
ShrinkWrap can be used to create the deployment archive. In this example, the whole EAR is
imported, but we could also use the ShrinkWrap API to create a WAR or an EAR from scratch
and put in just the artifacts that we need for the test.
Lifecycle.beginCall() is needed to setup Seam contexts.
33.2.1. Configuration
T he Arquillian configuration depends on the specific container used. See Arquillian documentation for
more information.
Assuming you are using Maven as your build tool and want to run your tests on Red Hat JBoss
Enterprise Application Platform, you will need to put these dependencies into your pom .xm l:
364
Chapter 33. Testing Seam applications
<dependency>
<groupId>org.jboss.arquillian.junit</groupId>
<artifactId>arquillian-junit-container</artifactId>
<version>${version.arquillian}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-arquillian-container-managed</artifactId>
<version>${version.jboss.as7}</version>
<scope>test</scope>
</dependency>
T he Arquillian JBoss Enterprise Application Platform Managed Container will automatically start the
application server, provided the JBOSS_HOME environment property points to the JBoss Enterprise
Application Platform 6 installation.
33.2.2. Using JUnitSeamTest with Arquillian
It is also possible to use the simulated JSF environment provided by Seam T est along with Arquillian.
T his is useful especially if you are migrating from previous Seam releases and want to keep your
existing testsuite mostly unchanged.
Note
SeamT est was primarily designated for T estNG integration tests. Currently, there are some
glitches so we recommend using JUnitSeamT est which is the JUnit variant of SeamT est.
T he following changes must be done to run a JUnitSeamT est with Arquillian:
Create the @ Deploym ent method, which constructs the test archive using ShrinkWrap. ShrinkWrap
Resolver can be used to resolve any required dependencies.
Convert the test to JUnit. A JUnitSeam T est class can now be used instead of the original
Seam T est.
Replace the Seam Listener with org.jboss.seam .m ock.MockSeam Listener in web.xml.
365
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Example 33.2. RegisterT est.java
@RunWith(Arquillian)
public class RegisterTest extends JUnitSeamTest
{
@Deployment
@OverProtocol("Servlet 3.0")
public static Archive<?> createDeployment()
{
return Deployments.registrationDeployment();
}
@Test
public void testRegisterComponent() throws Exception
{
new ComponentTest()
{
protected void testComponents() throws Exception
{
setValue("#{user.username}", "1ovthafew");
setValue("#{user.name}", "Gavin King");
setValue("#{user.password}", "secret");
assert invokeMethod("#{register.register}").equals("success");
assert getValue("#{user.username}").equals("1ovthafew");
assert getValue("#{user.name}").equals("Gavin King");
assert getValue("#{user.password}").equals("secret");
}
}.run();
}
...
}
366
Chapter 33. Testing Seam applications
Example 33.3. Deployments.java
public class Deployments
{
public static WebArchive registrationDeployment()
{
File[] libs = Maven.resolver().loadPomFromFile("pom.xml")
.importCompileAndRuntimeDependencies()
// resolve jboss-seam, because it is provided-scoped in the pom, but we
need it bundled in the WAR
.resolve("org.jboss.seam:jboss-seam")
.withTransitivity().asFile();
return ShrinkWrap.create(WebArchive.class, "seam-registration.war")
// all main classes required for testing
.addPackage(RegisterAction.class.getPackage())
// classpath resources
.addAsWebInfResource("META-INF/ejb-jar.xml", "ejb-jar.xml")
.addAsResource("persistence.xml", "META-INF/persistence.xml")
.addAsResource("seam.properties", "seam.properties")
// resources manually copied from EAR and WAR modules
.addAsWebInfResource("components.xml", "components.xml")
.addAsWebInfResource("jboss-deployment-structure.xml", "jboss-deploymentstructure.xml")
// the modified web.xml
.addAsWebInfResource("mock-web.xml", "web.xml")
// web resources
.addAsWebResource("index.html")
.addAsWebResource("register.xhtml")
.addAsWebResource("registered.xhtml")
// libraries resolved using ShrinkWrap Resolver
.addAsLibraries(libs);
}
}
Example 33.4 . mock-web.xml
<?xml version="1.0" ?>
<web-app version="3.0" 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">
<listener>
<listener-class>org.jboss.seam.mock.MockSeamListener</listener-class>
</listener>
</web-app>
33.2.2.1. Using mocks in integration tests
You may need to replace Seam components requiring resources that are unavailable in the integration
test environment. For example, suppose that you use the following Seam component as a facade to
some payment processing system:
367
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
@Name("paymentProcessor")
public class PaymentProcessor
{
public boolean processPayment(Payment payment) { .... }
}
For integration tests, we can make a mock component like so:
@Name("paymentProcessor")
@Install(precedence=MOCK)
public class MockPaymentProcessor extends PaymentProcessor
{
public boolean processPayment(Payment payment)
{
return true;
}
}
T he MOCK precedence is higher than the default precedence of application components, so Seam will
install the mock implementation whenever it is in the classpath. When deployed into production, the mock
implementation is absent, so the real component will be installed.
33.2.3. Integration testing Seam application user interactions
It is more difficult to emulate user interactions, and to place assertions appropriately. Some test
frameworks let us test the whole application by reproducing user interactions with the web browser.
T hese are useful, but not appropriate during development.
Seam T est lets you write scripted tests in a simulated JSF environment. A scripted test reproduces the
interaction between the view and the Seam components, so you play the role of the JSF implementation
during testing. You can test everything but the view with this approach.
Consider a JSP view for the component we unit tested above:
<html>
<head>
<title>Register New User</title>
</head>
<body>
<f:view>
<h:form>
<table border="0">
<tr>
<td>Username</td>
<td><h:inputText value="#{user.username}"/></td>
</tr>
<tr>
<td>Real Name</td>
<td><h:inputText value="#{user.name}"/></td>
</tr>
<tr>
<td>Password</td>
<td><h:inputSecret value="#{user.password}"/></td>
</tr>
</table>
<h:messages/>
<h:commandButton type="submit" value="Register"
action="#{register.register}"/>
</h:form>
</f:view>
</body>
</html>
We want to test the registration functionality of our application (that is, what happens when a user clicks
368
Chapter 33. Testing Seam applications
the Register button). We will reproduce the JSF request lifecycle in an automated JUnit test:
@RunWith(Arquillian.class)
public class RegisterTest extends JUnitSeamTest
{
@Deployment(name="RegisterTest")
@OverProtocol("Servlet 3.0")
public static Archive<?> createDeployment()
{
return Deployments.registrationDeployment();
}
@Test
public void testLogin() throws Exception
{
new FacesRequest("/register.xhtml")
{
@Override
protected void processValidations() throws Exception
{
validateValue("#{user.username}", "1ovthafew");
validateValue("#{user.name}", "Gavin King");
validateValue("#{user.password}", "secret");
assert !isValidationFailure();
}
@Override
protected void updateModelValues() throws Exception
{
setValue("#{user.username}", "1ovthafew");
setValue("#{user.name}", "Gavin King");
setValue("#{user.password}", "secret");
}
@Override
protected void invokeApplication()
{
assert invokeMethod("#{register.register}").equals("/registered.xhtml");
setOutcome("/registered.xhtml");
}
@Override
protected void afterRequest()
{
assert isInvokeApplicationComplete();
assert !isRenderResponseBegun();
}
}
}.run();
...
}
You have extended JUnitSeam T est, which provides a Seam environment for our components, and
written our test script as an anonymous class that extends JUnitSeam T est.FacesRequest, which
provides an emulated JSF request lifecycle. (T here is also a JUnitSeam T est.NonFacesRequest for
testing GET requests.) We've written our code in methods which are named for the various JSF phases,
to emulate the calls that JSF would make to our components. T hen we've thrown in various assertions.
You will find plenty of integration tests for the Seam example applications which demonstrate more
complex cases.
33.2.3.1. Configuration
If you used seam-gen to create your project you are ready to start writing tests. Otherwise you'll need to
setup the testing environment in your favorite build tool (e.g. ant, maven, eclipse).
If you use ant or a custom build tool which uses locally available jars - you can get all jars by running
ant -f get-arquillian-libs.xm l -Dtest.lib.dir=lib/test. T his just downloads all
Arquillian jars for managed JBoss Enterprise Application Platform container and copies those into a
369
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
directory defined by the test.lib.dir property, which is lib/test in this case.
And, of course you need to put your built project and tests onto the classpath, along with the jar of your
test framework. Don't forget to put all the correct configuration files for JPA and Seam onto the classpath
as well. Seam asks Arquillian to deploy any resource (jar or directory) which has a seam .properties
file in it's root. T herefore, if you don't assemble a directory structure that resembles a deployable archive
containing your built project, you must put a seam .properties in each resource.
33.2.3.2. Using SeamT est with another test framework
Seam provides JUnit support out of the box, but you can also use another test framework, if you want.
You will need to provide an implementation of AbstractSeam T est which does the following:
Calls super.begin() before every test method.
Calls super.end() after every test method.
Calls super.setupClass() to setup integration test environment. T his should be called before any
test methods are called.
Calls super.cleanupClass() to clean up the integration test environment.
Calls super.startSeam () to start Seam at the start of integration testing.
Calls super.stopSeam () to cleanly shut down Seam at the end of integration testing.
33.2.3.3. Integration T esting with Mock Data
If you want to insert or clean data in your database before each test you can use Seam's integration with
DBUnit. T o do this, extend DBJUnitSeam T est rather than JUnitSeam T est.
You have to provide a dataset for DBUnit.
Note
DBUnit supports two formats for dataset files, flat and XML. Seam's DBJUnitSeam T est
assumes the flat format is used, so make sure that your dataset is in this format.
<dataset>
<ARTIST id="1" dtype="Band" name="Pink Floyd" />
<DISC id="1" name="Dark Side of the Moon" artist_id="1" />
</dataset>
In your test class, configure your dataset by overriding prepareDBUnitOperations().
protected void prepareDBUnitOperations()
{
setDatabase("HSQL");
setDatasourceJndiName("java:/jboss/myDatasource");
beforeTestOperations.add(
new DataSetOperation("my/datasets/BaseData.xml"));
}
DataSetOperation defaults to DatabaseOperation.CLEAN_INSERT if no other operation is
specified as a constructor argument. T he above example cleans all tables defined BaseData.xm l, then
inserts all rows declared in BaseData.xm l before each @ T est method is invoked.
If you require extra cleanup after a test method executes, add operations to afterT estOperations
list.
You need to tell DBUnit which datasource you are using. T his is accomplished by calling
setDatasourceJndiNam e.
DBJUnitSeamT est has support for MySQL and HSQL - you need to tell it which database is being used,
370
Chapter 33. Testing Seam applications
otherwise it defaults to HSQL.
It also allows you to insert binary data into the test data set (n.b. this is untested on Windows). You need
to tell it where to locate these resources on your classpath:
setBinaryUrl("images/");
You do not have to configure any of these parameters except the datasourceJndiNam e if you use
HSQL and have no binary imports. You have to call setDatabaseJndiNam e() before your test runs. If
you are not using HSQL or MySQL, you need to override some methods. See the Javadoc of
DBJUnitSeam T est for more details.
371
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
Chapter 34. Dependencies
34.1. JDK Dependencies
Seam does not work with JDK 1.4 and requires JDK 5 or above as it uses annotations and other JDK
5.0 features. Seam has been thoroughly tested using Oracle's JDKs and OpenJDKs. However there are
no known issues specific to Seam with other JDKs.
34.1.1. Oracle's JDK 6 Considerations
Earlier versions of Oracle's JDK 6 contained an incompatible version of JAXB and required overriding it
using the "endorsed" directory. Oracle's JDK6 Update 4 release upgraded to JAXB 2.1 and removed this
requirement. When building, testing, or executing be sure to use this version or higher.
34.2. Project Dependencies
T his section both lists the compile-time and runtime dependencies for Seam. Where the type is listed as
ear, the library should be included in the /lib directory of your application's ear file. Where the type is
listed as war, the library should be placed in the /WEB-INF/lib directory of your application's war file.
T he scope of the dependency is either all, runtime or provided (by Red Hat JBoss Enterprise Application
Platform 6).
Up to date version information and complete dependency information is not included in the docs, but is
provided in the /dependency-report.txt which is generated from the Maven POMs stored in
/build. You can generate this file by running ant dependencyReport.
34.2.1. Core
T able 34 .1.
Name
Scope
T ype
Notes
all
ear
T he core Seam library, always
required.
jboss-seam -debug.jar
runtime
war
Include during development when
enabling Seam's debug feature
jboss-seam -ioc.jar
runtime
war
Required when using Seam with
Spring
jboss-seam -pdf.jar
runtime
war
Required when using Seam's PDF
features
jboss-seam -excel.jar
runtime
war
Required when using Seam's
Microsoft® Excel® features
jboss-seam rem oting.jar
runtime
war
Required when using Seam
Remoting
jboss-seam -ui.jar
runtime
war
Required to use the Seam JSF
controls
jsf-api.jar
provided
JSF API
jsf-im pl.jar
provided
JSF Reference Implementation
urlrewrite.jar
runtime
war
URL Rewrite library
quartz.jar
runtime
ear
Required when you wish to use
Quartz with Seam's asynchronous
features
jboss-seam .jar
34.2.2. RichFaces
372
Chapter 34. D ependencies
T able 34 .2. RichFaces dependencies
Name
Scope
T ype
Notes
richfaces-coreapi.jar
all
ear
Required to use RichFaces.
Provides Core API classes that
you may wish to use from your
application e.g. to create a tree
richfaces-coreim pl.jar
runtime
war
Required to use RichFaces Core
implementations.
richfacescom ponents-ui.jar
runtime
war
Required to use RichFaces.
Provides all the Components UI
components.
richfacescom ponents-api.jar
runtime
war
Required to use RichFaces.
Provides all the API for UI
components.
34.2.3. Seam PDF
T able 34 .3. Seam PDF Dependencies
Name
T ype
Scope
Notes
itext.jar
runtime
war
PDF Library
jfreechart.jar
runtime
war
Charting library
jcom m on.jar
runtime
war
Required by JFreeChart
jboss-seam -pdf.jar
runtime
war
Seam PDF core library
34.2.4. Seam Microsoft®Excel®
T able 34 .4 . Seam Microsoft® Excel® Dependencies
Name
T ype
Scope
Notes
jxl.jar
runtime
war
JExcelAPI library
jboss-seam -excel.jar
runtime
war
Seam Microsoft® Excel® core
library
34.2.5. Drools
T he Drools libraries can be found in the lib directory in Seam.
T able 34 .5. Drools Dependencies
Name
Scope
T ype
Notes
antlr-runtim e.jar
runtime
ear
ANT LR Runtime Library
ecj.jar
runtime
ear
Eclipse Compiler for Java
knowledge-api.jar
runtime
ear
drools-com piler.jar
runtime
ear
drools-core.jar
runtime
ear
droolsdecisiontables.jar
runtime
ear
drools-tem plates.jar
runtime
ear
m vel2.jar
runtime
ear
Drools compiler
373
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
34.2.6. GWT
T hese libraries are required if you with to use the Google Web T oolkit (GWT ) with your Seam
application.
T able 34 .6. GWT dependencies
Name
gwt-servlet.jar
Scope
T ype
runtime
Notes
war
T he GWT Servlet libs
34.2.7. Spring
T hese libraries are required if you with to use the Spring Framework with your Seam application.
T able 34 .7. Spring Framework dependencies
Name
spring.jar
Scope
T ype
runtime
Notes
ear
T he Spring Framework library
34.2.8. Groovy
T hese libraries are required if you with to use Groovy with your Seam application.
T able 34 .8. Groovy dependencies
Name
groovy-all.jar
Scope
T ype
runtime
Notes
ear
T he Groovy libs
34.3. Dependency Management using Maven
T his section describes some uses of Seam from user or application point of view.
All the Seam artifacts are available in Maven:
<dependency>
<groupId>org.jboss.seam</groupId>
<artifactId>jboss-seam</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.seam</groupId>
<artifactId>jboss-seam-ui</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.seam</groupId>
<artifactId>jboss-seam-pdf</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.seam</groupId>
<artifactId>jboss-seam-mail</artifactId>
</dependency>
374
Chapter 34. D ependencies
<dependency>
<groupId>org.jboss.seam</groupId>
<artifactId>jboss-seam-debug</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.seam</groupId>
<artifactId>jboss-seam-remoting</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.seam</groupId>
<artifactId>jboss-seam-ioc</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.seam</groupId>
<artifactId>jboss-seam-excel</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.seam</groupId>
<artifactId>jboss-seam-resteasy</artifactId>
</dependency>
T his sample POM will give you Seam, JPA (provided by Hibernate), Hibernate Validator and Hibernate
Search:
375
Red Hat JBoss Web Framework Kit 2.3 Seam Reference Guide
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.jboss.seam.example/groupId>
<artifactId>my-project</artifactId>
<version>1.0</version>
<name>My Seam Project</name>
<packaging>jar</packaging>
<repositories>
<repository>
<id>repository.jboss.org</id>
<name>JBoss Public Repository</name>
<url>http://repository.jboss.org/nexus/content/groups/public</url>
</repository>
</repositories>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jboss.seam</groupId>
<artifactId>bom</artifactId>
<version>2.3.1.Final-redhat-2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-search</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.seam</groupId>
<artifactId>jboss-seam</artifactId>
</dependency>
</dependencies>
</project>
376
Revision History
Revision History
Revision 2.3.0-101.4 00
Rebuild with publican 4.0.0
2013-10-31
Rüdiger Landmann
Revision 2.3.0-101
Wed 16 Oct 2013
Petr Penicka
Added a security warning about using string concatenation to construct log messages.
Revision 2.3.0-100
T ue 09 Jul 2013
Sneha Agarwal
Incorporated changes for the release of JBoss Web Framework Kit 2.3.0.
Revision 2.2.0-100
T hu 28 Mar 2013
Sneha Agarwal
Incorporated changes for the release of JBoss Web Framework Kit 2.2.0.
Revision 2.1.0-100
T ue 23 Oct 2012
Updated with Seam 2.3 content from the community.
Isaac Rooskov
377
Was this manual useful for you? yes no
Thank you for your participation!

* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project

Download PDF

advertisement