[jboss-cvs] jboss-seam/doc/reference/en/modules ...
Norman Richards
norman.richards at jboss.com
Sat May 26 22:55:14 EDT 2007
User: nrichards
Date: 07/05/26 22:55:14
Modified: doc/reference/en/modules tutorial.xml
Log:
updates for 1.3
Revision Changes Path
1.83 +1809 -2208jboss-seam/doc/reference/en/modules/tutorial.xml
(In the diff below, changes in quantity of whitespace are not shown.)
Index: tutorial.xml
===================================================================
RCS file: /cvsroot/jboss/jboss-seam/doc/reference/en/modules/tutorial.xml,v
retrieving revision 1.82
retrieving revision 1.83
diff -u -b -r1.82 -r1.83
--- tutorial.xml 22 Mar 2007 01:30:26 -0000 1.82
+++ tutorial.xml 27 May 2007 02:55:14 -0000 1.83
@@ -4,38 +4,29 @@
<section id="try-examples">
<title>Try the examples</title>
- <para>
- In this tutorial, we'll assume that you have downloaded JBoss AS 4.0.5 and installed the
- EJB 3.0 profile (using the JBoss AS installer). You should also have a copy of Seam
- downloaded and extracted to a work directory.
- </para>
+ <para> In this tutorial, we'll assume that you have downloaded JBoss AS 4.2.0. You should also have a copy of
+ Seam downloaded and extracted to a work directory. </para>
- <para>
- The directory structure of each example in Seam follows this pattern:
- </para>
+ <para> The directory structure of each example in Seam follows this pattern: </para>
<itemizedlist>
<listitem>
- <para>
- Web pages, images and stylesheets may be found in
+ <para> Web pages, images and stylesheets may be found in
<filename>examples/<replaceable>registration</replaceable>/view</filename>
</para>
</listitem>
<listitem>
- <para>
- Resources such as deployment descriptors and data import scripts may be found in
+ <para> Resources such as deployment descriptors and data import scripts may be found in
<filename>examples/<replaceable>registration</replaceable>/resources</filename>
</para>
</listitem>
<listitem>
- <para>
- Java source code may be found in
+ <para> Java source code may be found in
<filename>examples/<replaceable>registration</replaceable>/src</filename>
</para>
</listitem>
<listitem>
- <para>
- The Ant build script is
+ <para> The Ant build script is
<filename>examples/<replaceable>registration</replaceable>/build.xml</filename>
</para>
</listitem>
@@ -44,68 +35,49 @@
<section>
<title>Running the examples on JBoss AS</title>
- <para>
- First, make sure you have Ant correctly installed, with <literal>$ANT_HOME</literal>
- and <literal>$JAVA_HOME</literal> set correctly. Next, make sure you set the
- location of your JBoss AS 4.0.5 installation in the <literal>build.properties</literal>
- file in the root folder of your Seam installation. If you haven't already done so, start
- JBoss AS now by typing <literal>bin/run.sh</literal> or <literal>bin/run.bat</literal>
- in the root directory of your JBoss installation.
- </para>
-
- <para>
- Now, build and deploy the example by typing <literal>ant deploy</literal> in the
- <filename>examples/<replaceable>registration</replaceable></filename> directory.
- </para>
-
- <para>
- Try it out by accessing
- <ulink url="http://localhost:8080/seam-registration/"><literal>http://localhost:8080/seam-registration/</literal></ulink>
- with your web browser.
- </para>
+ <para> First, make sure you have Ant correctly installed, with <literal>$ANT_HOME</literal> and
+ <literal>$JAVA_HOME</literal> set correctly. Next, make sure you set the location of your JBoss AS
+ 4.2.0 installation in the <literal>build.properties</literal> file in the root folder of your Seam
+ installation. If you haven't already done so, start JBoss AS now by typing <literal>bin/run.sh</literal>
+ or <literal>bin/run.bat</literal> in the root directory of your JBoss installation. </para>
+
+ <para> Now, build and deploy the example by typing <literal>ant deploy</literal> in the
+ <filename>examples/<replaceable>registration</replaceable></filename> directory. </para>
+
+ <para> Try it out by accessing <ulink url="http://localhost:8080/seam-registration/">
+ <literal>http://localhost:8080/seam-registration/</literal>
+ </ulink> with your web browser. </para>
</section>
<section>
<title>Running the examples on Tomcat</title>
- <para>
- First, make sure you have Ant correctly installed, with <literal>$ANT_HOME</literal>
- and <literal>$JAVA_HOME</literal> set correctly. Next, make sure you set the
- location of your Tomcat 5.5 installation in the <literal>build.properties</literal>
- file in the root folder of your Seam installation.
- </para>
-
- <para>
- Now, build and deploy the example by typing <literal>ant deploy.tomcat</literal> in the
- <filename>examples/<replaceable>registration</replaceable></filename> directory.
- </para>
-
- <para>
- Finally, start Tomcat.
- </para>
+ <para> First, make sure you have Ant correctly installed, with <literal>$ANT_HOME</literal> and
+ <literal>$JAVA_HOME</literal> set correctly. Next, make sure you set the location of your Tomcat 6.0
+ installation in the <literal>build.properties</literal> file in the root folder of your Seam
+ installation. </para>
+
+ <para> Now, build and deploy the example by typing <literal>ant deploy.tomcat</literal> in the
+ <filename>examples/<replaceable>registration</replaceable></filename> directory. </para>
+
+ <para> Finally, start Tomcat. </para>
+
+ <para> Try it out by accessing <ulink url="http://localhost:8080/jboss-seam-registration/">
+ <literal>http://localhost:8080/jboss-seam-registration/</literal>
+ </ulink> with your web browser. </para>
- <para>
- Try it out by accessing
- <ulink url="http://localhost:8080/jboss-seam-registration/"><literal>http://localhost:8080/jboss-seam-registration/</literal></ulink>
- with your web browser.
- </para>
-
- <para>
- When you deploy the example to Tomcat, any EJB3 components will run inside the JBoss
- Embeddable EJB3 container, a complete standalone EJB3 container environment.
- </para>
+ <para> When you deploy the example to Tomcat, any EJB3 components will run inside the JBoss Embeddable EJB3
+ container, a complete standalone EJB3 container environment. </para>
</section>
<section>
<title>Running the example tests</title>
- <para>
- Most of the examples come with a suite of TestNG integration tests. The easiest way
- to run the tests is to run <literal>ant testexample</literal> inside the
- <filename>examples/<replaceable>registration</replaceable></filename> directory.
- It is also possible to run the tests inside your IDE using the TestNG plugin.
- </para>
+ <para> Most of the examples come with a suite of TestNG integration tests. The easiest way to run the tests
+ is to run <literal>ant testexample</literal> inside the
+ <filename>examples/<replaceable>registration</replaceable></filename> directory. It is also possible
+ to run the tests inside your IDE using the TestNG plugin. </para>
</section>
</section>
@@ -113,21 +85,15 @@
<section id="registration-example">
<title>Your first Seam application: the registration example</title>
- <para>
- The registration example is a fairly trivial application that lets a new user store
- his username, real name and password in the database. The example isn't intended to
- show off all of the cool functionality of Seam. However, it demonstrates the use of
- an EJB3 session bean as a JSF action listener, and basic configuration of Seam.
- </para>
+ <para> The registration example is a fairly trivial application that lets a new user store his username, real
+ name and password in the database. The example isn't intended to show off all of the cool functionality of
+ Seam. However, it demonstrates the use of an EJB3 session bean as a JSF action listener, and basic
+ configuration of Seam. </para>
- <para>
- We'll go slowly, since we realize you might not yet be familiar with EJB 3.0.
- </para>
+ <para> We'll go slowly, since we realize you might not yet be familiar with EJB 3.0. </para>
- <para>
- The start page displays a very basic form with three input fields. Try filling them
- in and then submitting the form. This will save a user object in the database.
- </para>
+ <para> The start page displays a very basic form with three input fields. Try filling them in and then
+ submitting the form. This will save a user object in the database. </para>
<mediaobject>
<imageobject role="fo">
@@ -141,10 +107,7 @@
<section>
<title>Understanding the code</title>
- <para>
- The example is implemented with two JSP pages, one entity bean and one stateless
- session bean.
- </para>
+ <para> The example is implemented with two JSP pages, one entity bean and one stateless session bean. </para>
<mediaobject>
<imageobject role="fo">
@@ -155,18 +118,14 @@
</imageobject>
</mediaobject>
- <para>
- Let's take a look at the code, starting from the "bottom".
- </para>
+ <para> Let's take a look at the code, starting from the "bottom". </para>
<section>
<title>The entity bean: <literal>User.java</literal></title>
- <para>
- We need an EJB entity bean for user data. This class defines <emphasis>persistence</emphasis>
- and <emphasis>validation</emphasis> declaratively, via annotations. It also needs some extra
- annotations that define the class as a Seam component.
- </para>
+ <para> We need an EJB entity bean for user data. This class defines <emphasis>persistence</emphasis> and
+ <emphasis>validation</emphasis> declaratively, via annotations. It also needs some extra
+ annotations that define the class as a Seam component. </para>
<example>
<programlistingco>
@@ -237,85 +196,65 @@
}]]></programlisting>
<calloutlist>
<callout arearefs="registration-entity-annotation">
- <para>
- The EJB3 standard <literal>@Entity</literal> annotation indicates that the
- <literal>User</literal> class is an entity bean.
- </para>
+ <para> The EJB3 standard <literal>@Entity</literal> annotation indicates that the
+ <literal>User</literal> class is an entity bean. </para>
</callout>
<callout arearefs="registration-name-annotation">
- <para>
- A Seam component needs a <emphasis>component name</emphasis> specified by the
- <link linkend="name-annotation"><literal>@Name</literal></link> annotation. This
- name must be unique within the Seam application. When JSF asks Seam to resolve
- a context variable with a name that is the same as a Seam component name, and
- the context variable is currently undefined (null), Seam will instantiate that
- component, and bind the new instance to the context variable. In this case,
- Seam will instantiate a <literal>User</literal> the first time JSF encounters
- a variable named <literal>user</literal>.
- </para>
+ <para> A Seam component needs a <emphasis>component name</emphasis> specified by the
+ <link linkend="name-annotation">
+ <literal>@Name</literal>
+ </link> annotation. This name must be unique within the Seam application. When JSF
+ asks Seam to resolve a context variable with a name that is the same as a Seam
+ component name, and the context variable is currently undefined (null), Seam will
+ instantiate that component, and bind the new instance to the context variable. In
+ this case, Seam will instantiate a <literal>User</literal> the first time JSF
+ encounters a variable named <literal>user</literal>. </para>
</callout>
<callout arearefs="registration-scope-annotation">
- <para>
- Whenever Seam instantiates a component, it binds the new instance to a context
+ <para> Whenever Seam instantiates a component, it binds the new instance to a context
variable in the component's <emphasis>default context</emphasis>. The default
- context is specified using the
- <link linkend="scope-annotation"><literal>@Scope</literal></link> annotation.
- The <literal>User</literal> bean is a session scoped component.
+ context is specified using the <link linkend="scope-annotation">
+ <literal>@Scope</literal>
+ </link> annotation. The <literal>User</literal> bean is a session scoped component.
</para>
</callout>
<callout arearefs="registration-table-annotation">
- <para>
- The EJB standard <literal>@Table</literal> annotation indicates that
- the <literal>User</literal> class is mapped to the <literal>users</literal>
- table.
+ <para> The EJB standard <literal>@Table</literal> annotation indicates that the
+ <literal>User</literal> class is mapped to the <literal>users</literal> table.
</para>
</callout>
<callout arearefs="registration-attributes">
<para>
- <literal>name</literal>, <literal>password</literal> and
- <literal>username</literal> are the persistent attributes of the
- entity bean. All of our persistent attributes define accessor
- methods. These are needed when this component is used by JSF
- in the render response and update model values phases.
- </para>
+ <literal>name</literal>, <literal>password</literal> and <literal>username</literal>
+ are the persistent attributes of the entity bean. All of our persistent attributes
+ define accessor methods. These are needed when this component is used by JSF in the
+ render response and update model values phases. </para>
</callout>
<callout arearefs="registration-empty-constructor">
- <para>
- An empty constructor is both required by both the EJB specification
- and by Seam.
+ <para> An empty constructor is both required by both the EJB specification and by Seam.
</para>
</callout>
<callout arearefs="registration-notnull">
- <para>
- The <literal>@NotNull</literal> and <literal>@Length</literal>
- annotations are part of the Hibernate Validator framework. Seam
- integrates Hibernate Validator and lets you use it for data validation
- (even if you are not using Hibernate for persistence).
- </para>
+ <para> The <literal>@NotNull</literal> and <literal>@Length</literal> annotations are
+ part of the Hibernate Validator framework. Seam integrates Hibernate Validator and
+ lets you use it for data validation (even if you are not using Hibernate for
+ persistence). </para>
</callout>
<callout arearefs="registration-id-annotation">
- <para>
- The EJB standard <literal>@Id</literal> annotation indicates the
- primary key attribute of the entity bean.
- </para>
+ <para> The EJB standard <literal>@Id</literal> annotation indicates the primary key
+ attribute of the entity bean. </para>
</callout>
</calloutlist>
</programlistingco>
- <para>
- The most important things to notice in this example are the <literal>@Name</literal>
- and <literal>@Scope</literal> annotations. These annotations establish that this class
- is a Seam component.
- </para>
- <para>
- We'll see below that the properties of our <literal>User</literal> class are bound
- to directly to JSF components and are populated by JSF during the update model values
- phase. We don't need any tedious glue code to copy data back and forth between the
- JSP pages and the entity bean domain model.
- </para>
- <para>
- However, entity beans shouldn't do transaction management or database access. So we
- can't use this component as a JSF action listener. For that we need a session bean.
- </para>
+ <para> The most important things to notice in this example are the <literal>@Name</literal> and
+ <literal>@Scope</literal> annotations. These annotations establish that this class is a Seam
+ component. </para>
+ <para> We'll see below that the properties of our <literal>User</literal> class are bound to
+ directly to JSF components and are populated by JSF during the update model values phase. We
+ don't need any tedious glue code to copy data back and forth between the JSP pages and the
+ entity bean domain model. </para>
+ <para> However, entity beans shouldn't do transaction management or database access. So we can't use
+ this component as a JSF action listener. For that we need a session bean. </para>
</example>
</section>
@@ -323,19 +262,13 @@
<section>
<title>The stateless session bean class: <literal>RegisterAction.java</literal></title>
- <para>
- Most Seam application use session beans as JSF action listeners (you can use JavaBeans
- instead if you like).
- </para>
- <para>
- We have exactly one JSF action in our application, and one session bean method attached
- to it. In this case, we'll use a stateless session bean, since all the state associated
- with our action is held by the <literal>User</literal> bean.
- </para>
+ <para> Most Seam application use session beans as JSF action listeners (you can use JavaBeans instead if
+ you like). </para>
+ <para> We have exactly one JSF action in our application, and one session bean method attached to it. In
+ this case, we'll use a stateless session bean, since all the state associated with our action is
+ held by the <literal>User</literal> bean. </para>
- <para>
- This is the only really interesting code in the example!
- </para>
+ <para> This is the only really interesting code in the example! </para>
<example>
<programlistingco>
@@ -374,7 +307,7 @@
{
em.persist(user);
log.info("Registered new user #{user.username}");
- return "/registered.jsp";
+ return "/registered.jspx";
}
else
{
@@ -387,54 +320,42 @@
<calloutlist>
<callout arearefs="registration-stateless-annotation">
- <para>
- The EJB standard <literal>@Stateless</literal> annotation marks this class
- as stateless session bean.
- </para>
+ <para> The EJB standard <literal>@Stateless</literal> annotation marks this class as
+ stateless session bean. </para>
</callout>
<callout arearefs="registration-in-annotation">
- <para>
- The <link linkend="in-annotation"><literal>@In</literal></link> annotation
- marks an attribute of the bean as injected by Seam. In this case, the
- attribute is injected from a context variable named <literal>user</literal>
- (the instance variable name).
- </para>
+ <para> The <link linkend="in-annotation">
+ <literal>@In</literal>
+ </link> annotation marks an attribute of the bean as injected by Seam. In this case,
+ the attribute is injected from a context variable named <literal>user</literal> (the
+ instance variable name). </para>
</callout>
<callout arearefs="registration-persistencecontext-annotation">
- <para>
- The EJB standard <literal>@PersistenceContext</literal> annotation is used to
- inject the EJB3 entity manager.
- </para>
+ <para> The EJB standard <literal>@PersistenceContext</literal> annotation is used to
+ inject the EJB3 entity manager. </para>
</callout>
<callout arearefs="registration-logger-annotation">
- <para>
- The Seam <literal>@Logger</literal> annotation is used to inject the component's
- <literal>Log</literal> instance.
- </para>
+ <para> The Seam <literal>@Logger</literal> annotation is used to inject the component's
+ <literal>Log</literal> instance. </para>
</callout>
<callout arearefs="registration-action-listener">
- <para>
- The action listener method uses the standard EJB3 <literal>EntityManager</literal>
- API to interact with the database, and returns the JSF outcome. Note that, since
- this is a sesson bean, a transaction is automatically begun when the
- <literal>register()</literal> method is called, and committed when it completes.
- </para>
+ <para> The action listener method uses the standard EJB3
+ <literal>EntityManager</literal> API to interact with the database, and returns the
+ JSF outcome. Note that, since this is a sesson bean, a transaction is automatically
+ begun when the <literal>register()</literal> method is called, and committed when it
+ completes. </para>
</callout>
<callout arearefs="registration-query">
- <para>
- Notice that Seam lets you use a JSF EL expression inside EJB-QL. Under the covers,
- this results in an ordinary JPA <literal>setParameter()</literal> call on the
- standard JPA <literal>Query</literal> object. Nice, huh?
- </para>
+ <para> Notice that Seam lets you use a JSF EL expression inside EJB-QL. Under the
+ covers, this results in an ordinary JPA <literal>setParameter()</literal> call on
+ the standard JPA <literal>Query</literal> object. Nice, huh? </para>
</callout>
<callout arearefs="registration-log">
- <para>
- The <literal>Log</literal> API lets us easily display templated log messages.
+ <para> The <literal>Log</literal> API lets us easily display templated log messages.
</para>
</callout>
<callout arearefs="registration-outcome">
- <para>
- JSF action listener methods return a string-valued outcome that determines what
+ <para> JSF action listener methods return a string-valued outcome that determines what
page will be displayed next. A null outcome (or a void action listener method)
redisplays the previous page. In plain JSF, it is normal to always use a JSF
<emphasis>navigation rule</emphasis> to determine the JSF view id from the
@@ -446,8 +367,7 @@
</para>
</callout>
<callout arearefs="registration-builtin">
- <para>
- Seam provides a number of <emphasis>built-in components</emphasis> to help solve
+ <para> Seam provides a number of <emphasis>built-in components</emphasis> to help solve
common problems. The <literal>FacesMessages</literal> component makes it easy to
display templated error or success messages. Built-in Seam components may be
obtained by injection, or by calling an <literal>instance()</literal> method.
@@ -456,29 +376,22 @@
</calloutlist>
</programlistingco>
- <para>
- Note that we did not explicitly specify a <literal>@Scope</literal> this time. Each Seam
- component type has a default scope if not explicitly specified. For stateless session beans,
- the default scope is the stateless context. Actually, <emphasis>all</emphasis> stateless
- session beans belong in the stateless context.
- </para>
-
- <para>
- Our session bean action listener performs the business and persistence logic for our
- mini-application. In more complex applications, we might need to layer the code and
- refactor persistence logic into a dedicated data access component. That's perfectly
- trivial to do. But notice that Seam does not force you into any particular strategy
- for application layering.
- </para>
-
- <para>
- Furthermore, notice that our session bean has simultaneous access to context associated
- with the web request (the form values in the <literal>User</literal> object, for example),
- and state held in transactional resources (the <literal>EntityManager</literal> object).
- This is a break from traditional J2EE architectures. Again, if you are more comfortable
- with the traditional J2EE layering, you can certainly implement that in a Seam application.
- But for many applications, it's simply not very useful.
- </para>
+ <para> Note that we did not explicitly specify a <literal>@Scope</literal> this time. Each Seam
+ component type has a default scope if not explicitly specified. For stateless session beans, the
+ default scope is the stateless context. Actually, <emphasis>all</emphasis> stateless session
+ beans belong in the stateless context. </para>
+
+ <para> Our session bean action listener performs the business and persistence logic for our
+ mini-application. In more complex applications, we might need to layer the code and refactor
+ persistence logic into a dedicated data access component. That's perfectly trivial to do. But
+ notice that Seam does not force you into any particular strategy for application layering. </para>
+
+ <para> Furthermore, notice that our session bean has simultaneous access to context associated with
+ the web request (the form values in the <literal>User</literal> object, for example), and state
+ held in transactional resources (the <literal>EntityManager</literal> object). This is a break
+ from traditional J2EE architectures. Again, if you are more comfortable with the traditional
+ J2EE layering, you can certainly implement that in a Seam application. But for many
+ applications, it's simply not very useful. </para>
</example>
@@ -497,33 +410,25 @@
}]]></programlisting>
</example>
- <para>
- That's the end of the Java code. Now onto the deployment descriptors.
- </para>
+ <para> That's the end of the Java code. Now onto the deployment descriptors. </para>
</section>
<section>
<title>The Seam component deployment descriptor: <literal>components.xml</literal></title>
- <para>
- If you've used many Java frameworks before, you'll be used to having to declare
- all your component classes in some kind of XML file that gradually grows more and
- more unmanageable as your project matures. You'll be relieved to know that Seam
- does not require that application components be accompanied by XML. Most Seam
- applications require a very small amount of XML that does not grow very much as
- the project gets bigger.
- </para>
-
- <para>
- Nevertheless, it is often useful to be able to provide for <emphasis>some</emphasis>
- external configuration of <emphasis>some</emphasis> components (particularly the
- components built in to Seam). You have a couple of options here, but the most flexible
- option is to provide this configuration in a file called <literal>components.xml</literal>,
- located in the <literal>WEB-INF</literal> directory. We'll use the
- <literal>components.xml</literal> file to tell Seam how to find our EJB components
- in JNDI:
- </para>
+ <para> If you've used many Java frameworks before, you'll be used to having to declare all your
+ component classes in some kind of XML file that gradually grows more and more unmanageable as your
+ project matures. You'll be relieved to know that Seam does not require that application components
+ be accompanied by XML. Most Seam applications require a very small amount of XML that does not grow
+ very much as the project gets bigger. </para>
+
+ <para> Nevertheless, it is often useful to be able to provide for <emphasis>some</emphasis> external
+ configuration of <emphasis>some</emphasis> components (particularly the components built in to
+ Seam). You have a couple of options here, but the most flexible option is to provide this
+ configuration in a file called <literal>components.xml</literal>, located in the
+ <literal>WEB-INF</literal> directory. We'll use the <literal>components.xml</literal> file to tell
+ Seam how to find our EJB components in JNDI: </para>
<example>
<programlisting><![CDATA[<components xmlns="http://jboss.com/products/seam/components"
@@ -532,20 +437,16 @@
</components>]]></programlisting>
</example>
- <para>
- This code configures a property named <literal>jndiPattern</literal> of a built-in
- Seam component named <literal>org.jboss.seam.core.init</literal>.
- </para>
+ <para> This code configures a property named <literal>jndiPattern</literal> of a built-in Seam component
+ named <literal>org.jboss.seam.core.init</literal>. </para>
</section>
<section>
<title>The web deployment description: <literal>web.xml</literal></title>
- <para>
- The presentation layer for our mini-application will be deployed in a WAR.
- So we'll need a web deployment descriptor.
- </para>
+ <para> The presentation layer for our mini-application will be deployed in a WAR. So we'll need a web
+ deployment descriptor. </para>
<example>
<programlisting><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
@@ -561,12 +462,10 @@
<listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
</listener>
- <!-- MyFaces -->
+ <!-- JSF -->
<listener>
- <listener-class>
- org.apache.myfaces.webapp.StartupServletContextListener
- </listener-class>
+ <listener-class>com.sun.faces.config.ConfigureListener</listener-class>
</listener>
<context-param>
@@ -574,6 +473,11 @@
<param-value>client</param-value>
</context-param>
+ <context-param>
+ <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
+ <param-value>.jspx</param-value>
+ </context-param>
+
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
@@ -586,23 +490,23 @@
<url-pattern>*.seam</url-pattern>
</servlet-mapping>
+ <session-config>
+ <session-timeout>10</session-timeout>
+ </session-config>
+
</web-app>]]></programlisting>
</example>
- <para>
- This <literal>web.xml</literal> file configures Seam and MyFaces. The configuration you
- see here is pretty much identical in all Seam applications.
- </para>
+ <para> This <literal>web.xml</literal> file configures Seam and JSF. The configuration you see here is
+ pretty much identical in all Seam applications. </para>
</section>
<section>
<title>The JSF configration: <literal>faces-config.xml</literal></title>
- <para>
- All Seam applications use JSF views as the presentation layer. So we'll need
- <literal>faces-config.xml</literal>.
- </para>
+ <para> All Seam applications use JSF views as the presentation layer. So we'll need
+ <literal>faces-config.xml</literal>. </para>
<example id="registration-faces-config-xml">
<programlisting><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
@@ -620,40 +524,30 @@
</faces-config>]]></programlisting>
</example>
- <para>
- The <literal>faces-config.xml</literal> file integrates Seam into JSF. Note that we don't
- need any JSF managed bean declarations! The managed beans are the Seam components. In Seam
- applications, the <literal>faces-config.xml</literal> is used much less often than in plain
- JSF.
- </para>
-
- <para>
- In fact, once you have all the basic descriptors set up, the <emphasis>only</emphasis>
- XML you need to write as you add new functionality to a Seam application is the navigation
- rules, and possibly jBPM process definitions. Seam takes the view that
- <emphasis>process flow</emphasis> and <emphasis>configuration data</emphasis> are the only
- things that truly belong in XML.
- </para>
+ <para> The <literal>faces-config.xml</literal> file integrates Seam into JSF. Note that we don't need
+ any JSF managed bean declarations! The managed beans are the Seam components. In Seam applications,
+ the <literal>faces-config.xml</literal> is used much less often than in plain JSF. </para>
+
+ <para> In fact, once you have all the basic descriptors set up, the <emphasis>only</emphasis> XML you
+ need to write as you add new functionality to a Seam application is the navigation rules, and
+ possibly jBPM process definitions. Seam takes the view that <emphasis>process flow</emphasis> and
+ <emphasis>configuration data</emphasis> are the only things that truly belong in XML. </para>
- <para>
- In this simple example, we don't even need a navigation rule, since we decided to embed
- the view id in our action code.
- </para>
+ <para> In this simple example, we don't even need a navigation rule, since we decided to embed the view
+ id in our action code. </para>
</section>
<section>
<title>The EJB deployment descriptor: <literal>ejb-jar.xml</literal></title>
- <para>
- The <literal>ejb-jar.xml</literal> file integrates Seam with EJB3, by
- attaching the <literal>SeamInterceptor</literal> to all session beans
- in the archive.
- </para>
+ <para> The <literal>ejb-jar.xml</literal> file integrates Seam with EJB3, by attaching the
+ <literal>SeamInterceptor</literal> to all session beans in the archive. </para>
<programlisting><![CDATA[<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"
+ 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>
@@ -676,16 +570,15 @@
<section>
<title>The EJB persistence deployment descriptor: <literal>persistence.xml</literal></title>
- <para>
- The <literal>persistence.xml</literal> file tells the EJB persistence provider where
- to find the datasource, and contains some vendor-specific settings. In this case,
- enables automatic schema export at startup time.
- </para>
+ <para> The <literal>persistence.xml</literal> file tells the EJB persistence provider where to find the
+ datasource, and contains some vendor-specific settings. In this case, enables automatic schema
+ export at startup time. </para>
<programlisting><![CDATA[<?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_1_0.xsd"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
+ http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="userDatabase">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
@@ -701,12 +594,10 @@
<section>
<title>The view: <literal>register.jsp</literal> and <literal>registered.jsp</literal></title>
- <para>
- The view pages for a Seam application could be implemented using any technology that
- supports JSF. In this example we use JSP, since it is familiar to most developers
- and since we have minimal requirements here anyway. (But if you take our advice, you'll
- use Facelets for your own applications.)
- </para>
+ <para> The view pages for a Seam application could be implemented using any technology that supports
+ JSF. In this example we use JSP, since it is familiar to most developers and since we have minimal
+ requirements here anyway. (But if you take our advice, you'll use Facelets for your own
+ applications.) </para>
<example id="registration-simpleform">
<programlisting><![CDATA[<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
@@ -743,11 +634,9 @@
</html>]]></programlisting>
</example>
- <para>
- The only thing here that is specific to Seam is the <literal><s:validateAll></literal>
- tag. This JSF component tells JSF to validate all the contained input fields against the
- Hibernate Validator annotations specified on the entity bean.
- </para>
+ <para> The only thing here that is specific to Seam is the
+ <literal><s:validateAll></literal> tag. This JSF component tells JSF to validate all
+ the contained input fields against the Hibernate Validator annotations specified on the entity bean. </para>
<example id="registration-simplepage">
<programlisting><![CDATA[<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
@@ -766,26 +655,22 @@
</example>
- <para>
- This is a boring old JSP pages using standard JSF components. There is
- nothing specific to Seam here.
- </para>
+ <para> This is a boring old JSP pages using standard JSF components. There is nothing specific to Seam
+ here. </para>
</section>
<section>
<title>The EAR deployment descriptor: <literal>application.xml</literal></title>
- <para>
- Finally, since our application is deployed as an EAR, we need a deployment descriptor
- there, too.
- </para>
+ <para> Finally, since our application is deployed as an EAR, we need a deployment descriptor there, too. </para>
<example id="registration-application-xml">
<programlisting><![CDATA[<?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_5.xsd"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
+ http://java.sun.com/xml/ns/javaee/application_5.xsd"
version="5">
<display-name>Seam Registration</display-name>
@@ -803,70 +688,45 @@
<java>jboss-seam.jar</java>
</module>
<module>
- <java>el-api.jar</java>
- </module>
- <module>
- <java>el-ri.jar</java>
+ <java>jboss-el.jar</java>
</module>
</application>]]></programlisting>
</example>
- <para>
- This deployment descriptor links modules in the enterprise archive and binds
- the web application to the context root <literal>/seam-registration</literal>.
- </para>
+ <para> This deployment descriptor links modules in the enterprise archive and binds the web application
+ to the context root <literal>/seam-registration</literal>. </para>
</section>
- <para>
- We've now seen <emphasis>all</emphasis> the files in the entire application!
- </para>
+ <para> We've now seen <emphasis>all</emphasis> the files in the entire application! </para>
</section>
<section>
<title>How it works</title>
- <para>
- When the form is submitted, JSF asks Seam to resolve the variable named
- <literal>user</literal>. Since there is no value already bound to that
- name (in any Seam context), Seam instantiates the <literal>user</literal>
- component, and returns the resulting <literal>User</literal> entity
- bean instance to JSF after storing it in the Seam session context.
- </para>
- <para>
- The form input values are now validated against the Hibernate Validator
- constraints specified on the <literal>User</literal> entity. If the
- constraints are violated, JSF redisplays the page. Otherwise, JSF binds
- the form input values to properties of the <literal>User</literal> entity
- bean.
- </para>
- <para>
- Next, JSF asks Seam to resolve the variable named <literal>register</literal>.
- Seam finds the <literal>RegisterAction</literal> stateless session bean
- in the stateless context and returns it. JSF invokes the <literal>register()</literal>
- action listener method.
- </para>
- <para>
- Seam intercepts the method call and injects the <literal>User</literal> entity
- from the Seam session context, before continuing the invocation.
- </para>
- <para>
- The <literal>register()</literal> method checks if a user with the entered
- username already exists. If so, an error message is queued with the
- <literal>FacesMessages</literal> component, and a null outcome is returned,
- causing a page redisplay. The <literal>FacesMessages</literal> component
- interpolates the JSF expression embedded in the message string and adds
- a JSF <literal>FacesMessage</literal> to the view.
- </para>
- <para>
- If no user with that username exists, the <literal>"/registered.jsp"</literal>
- outcome triggers a browser redirect to the <literal>registered.jsp</literal> page.
- When JSF comes to render the page, it asks Seam to resolve the variable named
- <literal>user</literal> and uses property values of the returned <literal>User</literal>
- entity from Seam's session scope.
- </para>
+ <para> When the form is submitted, JSF asks Seam to resolve the variable named <literal>user</literal>.
+ Since there is no value already bound to that name (in any Seam context), Seam instantiates the
+ <literal>user</literal> component, and returns the resulting <literal>User</literal> entity bean
+ instance to JSF after storing it in the Seam session context. </para>
+ <para> The form input values are now validated against the Hibernate Validator constraints specified on the
+ <literal>User</literal> entity. If the constraints are violated, JSF redisplays the page. Otherwise,
+ JSF binds the form input values to properties of the <literal>User</literal> entity bean. </para>
+ <para> Next, JSF asks Seam to resolve the variable named <literal>register</literal>. Seam finds the
+ <literal>RegisterAction</literal> stateless session bean in the stateless context and returns it.
+ JSF invokes the <literal>register()</literal> action listener method. </para>
+ <para> Seam intercepts the method call and injects the <literal>User</literal> entity from the Seam session
+ context, before continuing the invocation. </para>
+ <para> The <literal>register()</literal> method checks if a user with the entered username already exists.
+ If so, an error message is queued with the <literal>FacesMessages</literal> component, and a null
+ outcome is returned, causing a page redisplay. The <literal>FacesMessages</literal> component
+ interpolates the JSF expression embedded in the message string and adds a JSF
+ <literal>FacesMessage</literal> to the view. </para>
+ <para> If no user with that username exists, the <literal>"/registered.jspx"</literal> outcome triggers a
+ browser redirect to the <literal>registered.jsp</literal> page. When JSF comes to render the page, it
+ asks Seam to resolve the variable named <literal>user</literal> and uses property values of the returned
+ <literal>User</literal> entity from Seam's session scope. </para>
</section>
@@ -875,13 +735,10 @@
<section id="messages">
<title>Clickable lists in Seam: the messages example</title>
- <para>
- Clickable lists of database search results are such an important part of any
- online application that Seam provides special functionality on top of JSF to
- make it easier to query data using EJB-QL or HQL and display it as a clickable
- list using a JSF <literal><h:dataTable></literal>. The messages example
- demonstrates this functionality.
- </para>
+ <para> Clickable lists of database search results are such an important part of any online application that Seam
+ provides special functionality on top of JSF to make it easier to query data using EJB-QL or HQL and display
+ it as a clickable list using a JSF <literal><h:dataTable></literal>. The messages example
+ demonstrates this functionality. </para>
<mediaobject>
<imageobject role="fo">
@@ -894,18 +751,14 @@
<section>
<title>Understanding the code</title>
- <para>
- The message list example has one entity bean, <literal>Message</literal>,
- one session bean, <literal>MessageListBean</literal> and one JSP.
- </para>
+ <para> The message list example has one entity bean, <literal>Message</literal>, one session bean,
+ <literal>MessageListBean</literal> and one JSP. </para>
<section>
<title>The entity bean: <literal>Message.java</literal></title>
- <para>
- The <literal>Message</literal> entity defines the title, text, date and time
- of a message, and a flag indicating whether the message has been read:
- </para>
+ <para> The <literal>Message</literal> entity defines the title, text, date and time of a message, and a
+ flag indicating whether the message has been read: </para>
<example>
<programlisting><![CDATA[@Entity
@@ -968,30 +821,19 @@
<section>
<title>The stateful session bean: <literal>MessageManagerBean.java</literal></title>
- <para>
- Just like in the previous example, we have a session bean,
- <literal>MessageManagerBean</literal>, which defines the action
- listener methods for the two buttons on our form. One of the
- buttons selects a message from the list, and displays that
- message. The other button deletes a message. So far, this
- is not so different to the previous example.
- </para>
-
- <para>
- But <literal>MessageManagerBean</literal> is also responsible
- for fetching the list of messages the first time we navigate
- to the message list page. There are various ways the user
- could navigate to the page, and not all of them are preceded
- by a JSF action—the user might have bookmarked the page,
- for example. So the job of fetching the message list takes
- place in a Seam <emphasis>factory method</emphasis>, instead
- of in an action listener method.
- </para>
+ <para> Just like in the previous example, we have a session bean, <literal>MessageManagerBean</literal>,
+ which defines the action listener methods for the two buttons on our form. One of the buttons
+ selects a message from the list, and displays that message. The other button deletes a message. So
+ far, this is not so different to the previous example. </para>
+
+ <para> But <literal>MessageManagerBean</literal> is also responsible for fetching the list of messages
+ the first time we navigate to the message list page. There are various ways the user could navigate
+ to the page, and not all of them are preceded by a JSF action—the user might have
+ bookmarked the page, for example. So the job of fetching the message list takes place in a Seam
+ <emphasis>factory method</emphasis>, instead of in an action listener method. </para>
- <para>
- We want to cache the list of messages in memory between server
- requests, so we will make this a stateful session bean.
- </para>
+ <para> We want to cache the list of messages in memory between server requests, so we will make this a
+ stateful session bean. </para>
<example>
<programlistingco>
@@ -1001,9 +843,9 @@
<area id="messages-out" coords="11"/>
<area id="messages-persistencecontext" coords="14"/>
<area id="messages-factory" coords="17"/>
- <area id="messages-select" coords="23"/>
- <area id="messages-delete" coords="28"/>
- <area id="messages-remove" coords="35"/>
+ <area id="messages-select" coords="24"/>
+ <area id="messages-delete" coords="29"/>
+ <area id="messages-remove" coords="36"/>
</areaspec>
<programlisting><![CDATA[@Stateful
@Scope(SESSION)
@@ -1024,7 +866,8 @@
@Factory("messageList")
public void findMessages()
{
- messageList = em.createQuery("from Message msg order by msg.datetime desc").getResultList();
+ messageList = em.createQuery("from Message msg order by msg.datetime desc")
+ .getResultList();
}
public void select()
@@ -1045,91 +888,66 @@
}]]></programlisting>
<calloutlist>
<callout arearefs="messages-datamodel">
- <para>
- The <literal>@DataModel</literal> annotation exposes an attibute of
- type <literal>java.util.List</literal> to the JSF page as an instance
- of <literal>javax.faces.model.DataModel</literal>. This allows us to
- use the list in a JSF <literal><h:dataTable></literal> with
- clickable links for each row. In this case, the <literal>DataModel</literal>
- is made available in a session context variable named
- <literal>messageList</literal>.
- </para>
+ <para> The <literal>@DataModel</literal> annotation exposes an attibute of type
+ <literal>java.util.List</literal> to the JSF page as an instance of
+ <literal>javax.faces.model.DataModel</literal>. This allows us to use the list
+ in a JSF <literal><h:dataTable></literal> with clickable links for
+ each row. In this case, the <literal>DataModel</literal> is made available in a
+ session context variable named <literal>messageList</literal>. </para>
</callout>
<callout arearefs="messages-datamodelselection">
- <para>
- The <literal>@DataModelSelection</literal> annotation tells Seam to
- inject the <literal>List</literal> element that corresponded to the
- clicked link.
- </para>
+ <para> The <literal>@DataModelSelection</literal> annotation tells Seam to inject the
+ <literal>List</literal> element that corresponded to the clicked link. </para>
</callout>
<callout arearefs="messages-out">
- <para>
- The <literal>@Out</literal> annotation then exposes the selected value
- directly to the page. So ever time a row of the clickable list is
- selected, the <literal>Message</literal> is injected to the attribute
- of the stateful bean, and the subsequently <emphasis>outjected</emphasis>
- to the event context variable named <literal>message</literal>.
- </para>
+ <para> The <literal>@Out</literal> annotation then exposes the selected value directly
+ to the page. So ever time a row of the clickable list is selected, the
+ <literal>Message</literal> is injected to the attribute of the stateful bean,
+ and the subsequently <emphasis>outjected</emphasis> to the event context variable
+ named <literal>message</literal>. </para>
</callout>
<callout arearefs="messages-persistencecontext">
- <para>
- This stateful bean has an EJB3 <emphasis>extended persistence context</emphasis>.
- The messages retrieved in the query remain in the managed state as long
- as the bean exists, so any subsequent method calls to the stateful bean
- can update them without needing to make any explicit call to the
- <literal>EntityManager</literal>.
- </para>
+ <para> This stateful bean has an EJB3 <emphasis>extended persistence context</emphasis>.
+ The messages retrieved in the query remain in the managed state as long as the bean
+ exists, so any subsequent method calls to the stateful bean can update them without
+ needing to make any explicit call to the <literal>EntityManager</literal>. </para>
</callout>
<callout arearefs="messages-factory">
- <para>
- The first time we navigate to the JSP page, there will be no value in
- the <literal>messageList</literal> context variable. The <literal>@Factory</literal>
+ <para> The first time we navigate to the JSP page, there will be no value in the
+ <literal>messageList</literal> context variable. The <literal>@Factory</literal>
annotation tells Seam to create an instance of <literal>MessageManagerBean</literal>
- and invoke the <literal>findMessages()</literal> method to initialize the
- value. We call <literal>findMessages()</literal> a <emphasis>factory
- method</emphasis> for <literal>messages</literal>.
- </para>
+ and invoke the <literal>findMessages()</literal> method to initialize the value. We
+ call <literal>findMessages()</literal> a <emphasis>factory method</emphasis> for
+ <literal>messages</literal>. </para>
</callout>
<callout arearefs="messages-select">
- <para>
- The <literal>select()</literal> action listener method marks the
- selected <literal>Message</literal> as read, and updates it in the
- database.
- </para>
+ <para> The <literal>select()</literal> action listener method marks the selected
+ <literal>Message</literal> as read, and updates it in the database. </para>
</callout>
<callout arearefs="messages-delete">
- <para>
- The <literal>delete()</literal> action listener method removes the
- selected <literal>Message</literal> from the database.
- </para>
+ <para> The <literal>delete()</literal> action listener method removes the selected
+ <literal>Message</literal> from the database. </para>
</callout>
<callout arearefs="messages-remove">
- <para>
- All stateful session bean Seam components <emphasis>must</emphasis>
- have a method marked <literal>@Remove @Destroy</literal> to ensure that
- Seam will remove the stateful bean when the Seam context ends, and
- clean up any server-side state.
+ <para> All stateful session bean Seam components <emphasis>must</emphasis> have a method
+ marked <literal>@Remove @Destroy</literal> to ensure that Seam will remove the
+ stateful bean when the Seam context ends, and clean up any server-side state.
</para>
</callout>
</calloutlist>
</programlistingco>
</example>
- <para>
- Note that this is a session-scoped Seam component. It is associated
- with the user login session, and all requests from a login session
- share the same instance of the component. (In Seam applications,
- we usually use session-scoped components sparingly.)
- </para>
+ <para> Note that this is a session-scoped Seam component. It is associated with the user login session,
+ and all requests from a login session share the same instance of the component. (In Seam
+ applications, we usually use session-scoped components sparingly.) </para>
</section>
<section>
<title>The session bean local interface: <literal>MessageManager.java</literal></title>
- <para>
- All session beans have a business interface, of course.
- </para>
+ <para> All session beans have a business interface, of course. </para>
<programlisting><![CDATA[@Local
public interface MessageManager
@@ -1140,26 +958,20 @@
public void destroy();
}]]></programlisting>
- <para>
- From now on, we won't show local interfaces in our code examples.
- </para>
+ <para> From now on, we won't show local interfaces in our code examples. </para>
- <para>
- Let's skip over <literal>components.xml</literal>, <literal>persistence.xml</literal>,
+ <para> Let's skip over <literal>components.xml</literal>, <literal>persistence.xml</literal>,
<literal>web.xml</literal>, <literal>ejb-jar.xml</literal>, <literal>faces-config.xml</literal>
- and <literal>application.xml</literal> since they are much the same as the previous
- example, and go straight to the JSP.
- </para>
+ and <literal>application.xml</literal> since they are much the same as the previous example, and go
+ straight to the JSP. </para>
</section>
<section>
<title>The view: <literal>messages.jsp</literal></title>
- <para>
- The JSP page is a straightforward use of the JSF <literal><h:dataTable></literal>
- component. Again, nothing specific to Seam.
- </para>
+ <para> The JSP page is a straightforward use of the JSF <literal><h:dataTable></literal>
+ component. Again, nothing specific to Seam. </para>
<example>
<programlisting><![CDATA[<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
@@ -1172,8 +984,10 @@
<f:view>
<h:form>
<h2>Message List</h2>
- <h:outputText value="No messages to display" rendered="#{messageList.rowCount==0}"/>
- <h:dataTable var="msg" value="#{messageList}" rendered="#{messageList.rowCount>0}">
+ <h:outputText value="No messages to display"
+ rendered="#{messageList.rowCount==0}"/>
+ <h:dataTable var="msg" value="#{messageList}"
+ rendered="#{messageList.rowCount>0}">
<h:column>
<f:facet name="header">
<h:outputText value="Read"/>
@@ -1213,42 +1027,32 @@
<section>
<title>How it works</title>
- <para>
- The first time we navigate to the <literal>messages.jsp</literal> page, whether
- by a JSF postback (faces request) or a direct browser GET request (non-faces
- request), the page will try to resolve the <literal>messageList</literal> context
- variable. Since this context variable is not initialized, Seam will call the
- factory method <literal>findMessages()</literal>, which performs a query against
- the database and results in a <literal>DataModel</literal> being outjected.
- This <literal>DataModel</literal> provides the row data needed for rendering
- the <literal><h:dataTable></literal>.
- </para>
-
- <para>
- When the user clicks the <literal><h:commandLink></literal>, JSF calls the
- <literal>select()</literal> action listener. Seam intercepts this call and injects
- the selected row data into the <literal>message</literal> attribute of the
- <literal>messageManager</literal> component. The action listener fires, marking
- the selected <literal>Message</literal> as read. At the end of the call, Seam
- outjects the selected <literal>Message</literal> to the context variable named
- <literal>message</literal>. Next, the EJB container commits the transaction, and
- the change to the <literal>Message</literal> is flushed to the database. Finally,
- the page is re-rendered, redisplaying the message list, and displaying the selected
- message below it.
- </para>
-
- <para>
- If the user clicks the <literal><h:commandButton></literal>, JSF calls the
- <literal>delete()</literal> action listener. Seam intercepts this call and injects
- the selected row data into the <literal>message</literal> attribute of the
- <literal>messageList</literal> component. The action listener fires, removing
- the selected <literal>Message</literal> from the list, and also calling
- <literal>remove()</literal> on the <literal>EntityManager</literal>. At the end
- of the call, Seam refreshes the <literal>messageList</literal> context variable and
- clears the context variable named <literal>message</literal>. The EJB container
- commits the transaction, and deletes the <literal>Message</literal> from the
- database. Finally, the page is re-rendered, redisplaying the message list.
- </para>
+ <para> The first time we navigate to the <literal>messages.jsp</literal> page, whether by a JSF postback
+ (faces request) or a direct browser GET request (non-faces request), the page will try to resolve the
+ <literal>messageList</literal> context variable. Since this context variable is not initialized,
+ Seam will call the factory method <literal>findMessages()</literal>, which performs a query against the
+ database and results in a <literal>DataModel</literal> being outjected. This
+ <literal>DataModel</literal> provides the row data needed for rendering the
+ <literal><h:dataTable></literal>. </para>
+
+ <para> When the user clicks the <literal><h:commandLink></literal>, JSF calls the
+ <literal>select()</literal> action listener. Seam intercepts this call and injects the selected row
+ data into the <literal>message</literal> attribute of the <literal>messageManager</literal> component.
+ The action listener fires, marking the selected <literal>Message</literal> as read. At the end of the
+ call, Seam outjects the selected <literal>Message</literal> to the context variable named
+ <literal>message</literal>. Next, the EJB container commits the transaction, and the change to the
+ <literal>Message</literal> is flushed to the database. Finally, the page is re-rendered,
+ redisplaying the message list, and displaying the selected message below it. </para>
+
+ <para> If the user clicks the <literal><h:commandButton></literal>, JSF calls the
+ <literal>delete()</literal> action listener. Seam intercepts this call and injects the selected row
+ data into the <literal>message</literal> attribute of the <literal>messageList</literal> component. The
+ action listener fires, removing the selected <literal>Message</literal> from the list, and also calling
+ <literal>remove()</literal> on the <literal>EntityManager</literal>. At the end of the call, Seam
+ refreshes the <literal>messageList</literal> context variable and clears the context variable named
+ <literal>message</literal>. The EJB container commits the transaction, and deletes the
+ <literal>Message</literal> from the database. Finally, the page is re-rendered, redisplaying the
+ message list. </para>
</section>
@@ -1257,12 +1061,9 @@
<section id="todo">
<title>Seam and jBPM: the todo list example</title>
- <para>
- jBPM provides sophisticated functionality for workflow and task management.
- To get a small taste of how jBPM integrates with Seam, we'll show you a
- simple "todo list" application. Since managing lists of tasks is such core
- functionality for jBPM, there is hardly any Java code at all in this example.
- </para>
+ <para> jBPM provides sophisticated functionality for workflow and task management. To get a small taste of how
+ jBPM integrates with Seam, we'll show you a simple "todo list" application. Since managing lists of tasks is
+ such core functionality for jBPM, there is hardly any Java code at all in this example. </para>
<mediaobject>
<imageobject role="fo">
@@ -1275,12 +1076,9 @@
<section>
<title>Understanding the code</title>
- <para>
- The center of this example is the jBPM process definition. There are also
- two JSPs and two trivial JavaBeans (There was no reason to use session beans,
- since they do not access the database, or have any other transactional
- behavior). Let's start with the process definition:
- </para>
+ <para> The center of this example is the jBPM process definition. There are also two JSPs and two trivial
+ JavaBeans (There was no reason to use session beans, since they do not access the database, or have any
+ other transactional behavior). Let's start with the process definition: </para>
<example>
<programlistingco>
@@ -1309,53 +1107,38 @@
</process-definition>]]></programlisting>
<calloutlist>
<callout arearefs="todo-startstate">
- <para>
- The <literal><start-state></literal> node represents the
- logical start of the process. When the process starts, it
- immediately transitions to the <literal>todo</literal> node.
- </para>
+ <para> The <literal><start-state></literal> node represents the logical start
+ of the process. When the process starts, it immediately transitions to the
+ <literal>todo</literal> node. </para>
</callout>
<callout arearefs="todo-tasknode">
- <para>
- The <literal><task-node></literal> node represents a
- <emphasis>wait state</emphasis>, where business process execution
- pauses, waiting for one or more tasks to be performed.
- </para>
+ <para> The <literal><task-node></literal> node represents a <emphasis>wait
+ state</emphasis>, where business process execution pauses, waiting for one or more
+ tasks to be performed. </para>
</callout>
<callout arearefs="todo-task">
- <para>
- The <literal><task></literal> element defines a task
- to be performed by a user. Since there is only one task
- defined on this node, when it is complete, execution
- resumes, and we transition to the end state. The task gets
- its description from a Seam component named
- <literal>todoList</literal> (one of the JavaBeans).
- </para>
+ <para> The <literal><task></literal> element defines a task to be performed by
+ a user. Since there is only one task defined on this node, when it is complete,
+ execution resumes, and we transition to the end state. The task gets its description
+ from a Seam component named <literal>todoList</literal> (one of the JavaBeans). </para>
</callout>
<callout arearefs="todo-assignment">
- <para>
- Tasks need to be assigned to a user or group of users when
- they are created. In this case, the task is assigned to the
- current user, which we get from a built-in Seam component
- named <literal>actor</literal>. Any Seam component may be
- used to perform task assignment.
- </para>
+ <para> Tasks need to be assigned to a user or group of users when they are created. In this
+ case, the task is assigned to the current user, which we get from a built-in Seam
+ component named <literal>actor</literal>. Any Seam component may be used to perform task
+ assignment. </para>
</callout>
<callout arearefs="todo-endstate">
- <para>
- The <literal><end-state></literal> node defines the logical
- end of the business process. When execution reaches this node,
- the process instance is destroyed.
+ <para> The <literal><end-state></literal> node defines the logical end of the
+ business process. When execution reaches this node, the process instance is destroyed.
</para>
</callout>
</calloutlist>
</programlistingco>
</example>
- <para>
- If we view this process definition using the process definition editor provided
- by JBossIDE, this is what it looks like:
- </para>
+ <para> If we view this process definition using the process definition editor provided by JBossIDE, this is
+ what it looks like: </para>
<mediaobject>
<imageobject role="fo">
@@ -1366,19 +1149,13 @@
</imageobject>
</mediaobject>
- <para>
- This document defines our <emphasis>business process</emphasis> as a graph
- of nodes. This is the most trivial possible business process: there is one
- <emphasis>task</emphasis> to be performed, and when that task is complete,
- the business process ends.
- </para>
-
- <para>
- The first JavaBean handles the login screen <literal>login.jsp</literal>.
- Its job is just to initialize the jBPM actor id using the
- <literal>actor</literal> component. (In a real application, it would also
- need to authenticate the user.)
- </para>
+ <para> This document defines our <emphasis>business process</emphasis> as a graph of nodes. This is the most
+ trivial possible business process: there is one <emphasis>task</emphasis> to be performed, and when that
+ task is complete, the business process ends. </para>
+
+ <para> The first JavaBean handles the login screen <literal>login.jsp</literal>. Its job is just to
+ initialize the jBPM actor id using the <literal>actor</literal> component. (In a real application, it
+ would also need to authenticate the user.) </para>
<example>
<programlisting><![CDATA[@Name("login")
@@ -1405,14 +1182,10 @@
}]]></programlisting>
</example>
- <para>
- Here we see the use of <literal>@In</literal> to inject the built-in
- <literal>Actor</literal> component.
- </para>
+ <para> Here we see the use of <literal>@In</literal> to inject the built-in <literal>Actor</literal>
+ component. </para>
- <para>
- The JSP itself is trivial:
- </para>
+ <para> The JSP itself is trivial: </para>
<example>
<programlisting><![CDATA[<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
@@ -1436,10 +1209,7 @@
</example>
- <para>
- The second JavaBean is responsible for starting business process instances,
- and ending tasks.
- </para>
+ <para> The second JavaBean is responsible for starting business process instances, and ending tasks. </para>
<example>
<programlistingco>
@@ -1471,38 +1241,27 @@
}]]></programlisting>
<calloutlist>
<callout arearefs="todo-description">
- <para>
- The description property accepts user input form the JSP page, and
- exposes it to the process definition, allowing the task description
- to be set.
- </para>
+ <para> The description property accepts user input form the JSP page, and exposes it to the
+ process definition, allowing the task description to be set. </para>
</callout>
<callout arearefs="todo-createprocess-annotation">
- <para>
- The Seam <literal>@CreateProcess</literal> annotation creates a new
- jBPM process instance for the named process definition.
- </para>
+ <para> The Seam <literal>@CreateProcess</literal> annotation creates a new jBPM process
+ instance for the named process definition. </para>
</callout>
<callout arearefs="todo-task-annotations">
- <para>
- The Seam <literal>@StartTask</literal> annotation starts work on
- a task. The <literal>@EndTask</literal> ends the task, and allows
- the business process execution to resume.
- </para>
+ <para> The Seam <literal>@StartTask</literal> annotation starts work on a task. The
+ <literal>@EndTask</literal> ends the task, and allows the business process execution
+ to resume. </para>
</callout>
</calloutlist>
</programlistingco>
</example>
- <para>
- In a more realistic example, <literal>@StartTask</literal> and <literal>@EndTask</literal>
- would not appear on the same method, because there is usually work to be done using the
- application in order to complete the task.
- </para>
+ <para> In a more realistic example, <literal>@StartTask</literal> and <literal>@EndTask</literal> would not
+ appear on the same method, because there is usually work to be done using the application in order to
+ complete the task. </para>
- <para>
- Finally, the meat of the application is in <literal>todo.jsp</literal>:
- </para>
+ <para> Finally, the meat of the application is in <literal>todo.jsp</literal>: </para>
<example>
<programlisting><![CDATA[<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
@@ -1517,8 +1276,10 @@
<f:view>
<h:form id="list">
<div>
- <h:outputText value="There are no todo items." rendered="#{empty taskInstanceList}"/>
- <h:dataTable value="#{taskInstanceList}" var="task" rendered="#{not empty taskInstanceList}">
+ <h:outputText value="There are no todo items."
+ rendered="#{empty taskInstanceList}"/>
+ <h:dataTable value="#{taskInstanceList}" var="task"
+ rendered="#{not empty taskInstanceList}">
<h:column>
<f:facet name="header">
<h:outputText value="Description"/>
@@ -1570,32 +1331,26 @@
</html>]]></programlisting>
</example>
- <para>
- Let's take this one piece at a time.
- </para>
+ <para> Let's take this one piece at a time. </para>
- <para>
- The page renders a list of tasks, which it gets from a built-in Seam component named
- <literal>taskInstanceList</literal>. The list is defined inside a JSF form.
- </para>
+ <para> The page renders a list of tasks, which it gets from a built-in Seam component named
+ <literal>taskInstanceList</literal>. The list is defined inside a JSF form. </para>
<programlisting><![CDATA[<h:form id="list">
<div>
<h:outputText value="There are no todo items." rendered="#{empty taskInstanceList}"/>
- <h:dataTable value="#{taskInstanceList}" var="task" rendered="#{not empty taskInstanceList}">
+ <h:dataTable value="#{taskInstanceList}" var="task"
+ rendered="#{not empty taskInstanceList}">
...
</h:dataTable>
</div>
</h:form>]]></programlisting>
- <para>
- Each element of the list is an instance of the jBPM class <literal>TaskInstance</literal>.
- The following code simply displays the interesting properties of each task in the list. For the
- description, priority and due date, we use input controls, to allow the user to update
- these values.
- </para>
+ <para> Each element of the list is an instance of the jBPM class <literal>TaskInstance</literal>. The
+ following code simply displays the interesting properties of each task in the list. For the description,
+ priority and due date, we use input controls, to allow the user to update these values. </para>
-<programlisting><![CDATA[<h:column>
+ <programlisting><![CDATA[<h:column>
<f:facet name="header">
<h:outputText value="Description"/>
</f:facet>
@@ -1625,33 +1380,23 @@
</h:column>]]></programlisting>
- <para>
- This button ends the task by calling the action method annotated
- <literal>@StartTask @EndTask</literal>. It passes the task id to
- Seam as a request parameter:
- </para>
+ <para> This button ends the task by calling the action method annotated <literal>@StartTask
+ @EndTask</literal>. It passes the task id to Seam as a request parameter: </para>
<programlisting><![CDATA[<h:column>
<s:button value="Done" action="#{todoList.done}" taskInstance="#{task}"/>
</h:column>]]></programlisting>
- <para>
- (Note that this is using a Seam <literal><s:button></literal> JSF control
- from the <literal>seam-ui.jar</literal> package.)
- </para>
+ <para> (Note that this is using a Seam <literal><s:button></literal> JSF control from the
+ <literal>seam-ui.jar</literal> package.) </para>
- <para>
- This button is used to update the properties of the tasks. When the form is
- submitted, Seam and jBPM will make any changes to the tasks persistent.
- There is no need for any action listener method:
- </para>
+ <para> This button is used to update the properties of the tasks. When the form is submitted, Seam and jBPM
+ will make any changes to the tasks persistent. There is no need for any action listener method: </para>
<programlisting><![CDATA[<h:commandButton value="Update Items" action="update"/>]]></programlisting>
- <para>
- A second form on the page is used to create new items, by calling the
- action method annotated <literal>@CreateProcess</literal>.
- </para>
+ <para> A second form on the page is used to create new items, by calling the action method annotated
+ <literal>@CreateProcess</literal>. </para>
<programlisting><![CDATA[<h:form id="new">
<div>
@@ -1660,10 +1405,8 @@
</div>
</h:form>]]></programlisting>
- <para>
- There are several other files needed for the example, but they are just
- standard jBPM and Seam configuration and not very interesting.
- </para>
+ <para> There are several other files needed for the example, but they are just standard jBPM and Seam
+ configuration and not very interesting. </para>
</section>
<section>
@@ -1676,20 +1419,14 @@
<section id="numberguess">
<title>Seam pageflow: the numberguess example</title>
- <para>
- For Seam applications with relatively freeform (ad hoc) navigation, JSF/Seam
- navigation rules are a perfectly good way to define the page flow.
- For applications with a more constrained style of navigation, especially
- for user interfaces which are more stateful, navigation rules make it
- difficult to really understand the flow of the system. To understand
- the flow, you need to piece it together from the view pages, the actions
- and the navigation rules.
- </para>
+ <para> For Seam applications with relatively freeform (ad hoc) navigation, JSF/Seam navigation rules are a
+ perfectly good way to define the page flow. For applications with a more constrained style of navigation,
+ especially for user interfaces which are more stateful, navigation rules make it difficult to really
+ understand the flow of the system. To understand the flow, you need to piece it together from the view
+ pages, the actions and the navigation rules. </para>
- <para>
- Seam allows you to use a jPDL process definition to define pageflow. The
- simple number guessing example shows how this is done.
- </para>
+ <para> Seam allows you to use a jPDL process definition to define pageflow. The simple number guessing example
+ shows how this is done. </para>
<mediaobject>
<imageobject role="fo">
@@ -1702,26 +1439,30 @@
<section>
<title>Understanding the code</title>
- <para>
- The example is implemented using one JavaBean, three JSP pages and
- a jPDL pageflow definition. Let's begin with the pageflow:
- </para>
+ <para> The example is implemented using one JavaBean, three JSP pages and a jPDL pageflow definition. Let's
+ begin with the pageflow: </para>
<example>
<programlistingco>
<areaspec>
- <area id="numberguess-page" coords="7"/>
- <area id="numberguess-transition" coords="8"/>
- <area id="numberguess-action" coords="9"/>
- <area id="numberguess-decision" coords="13"/>
+ <area id="numberguess-page" coords="8"/>
+ <area id="numberguess-transition" coords="10"/>
+ <area id="numberguess-action" coords="11"/>
+ <area id="numberguess-decision" coords="16"/>
</areaspec>
- <programlisting><![CDATA[<pageflow-definition name="numberGuess">
+ <programlisting><![CDATA[<pageflow-definition
+ xmlns="http://jboss.com/products/seam/pageflow"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://jboss.com/products/seam/pageflow
+ http://jboss.com/products/seam/pageflow-1.3.xsd"
+ name="numberGuess">
- <start-page name="displayGuess" view-id="/numberGuess.jsp">
+ <start-page name="displayGuess" view-id="/numberGuess.jspx">
<redirect/>
<transition name="guess" to="evaluateGuess">
- <action expression="#{numberGuess.guess}" />
+ <action expression="#{numberGuess.guess}"/>
</transition>
+ <transition name="giveup" to="giveup"/>
</start-page>
<decision name="evaluateGuess" expression="#{numberGuess.correctGuess}">
@@ -1734,63 +1475,51 @@
<transition name="false" to="displayGuess"/>
</decision>
- <page name="win" view-id="/win.jsp">
+ <page name="giveup" view-id="/giveup.jspx">
<redirect/>
- <end-conversation />
+ <transition name="yes" to="lose"/>
+ <transition name="no" to="displayGuess"/>
</page>
- <page name="lose" view-id="/lose.jsp">
+ <page name="win" view-id="/win.jspx">
<redirect/>
- <end-conversation />
+ <end-conversation/>
+ </page>
+
+ <page name="lose" view-id="/lose.jspx">
+ <redirect/>
+ <end-conversation/>
</page>
</pageflow-definition>]]></programlisting>
<calloutlist>
<callout arearefs="numberguess-page">
- <para>
- The <literal><page></literal> element defines
- a wait state where the system displays a particular
- JSF view and waits for user input. The
- <literal>view-id</literal> is the same JSF view id
- used in plain JSF navigation rules. The
- <literal>redirect</literal> attribute tells Seam to
- use post-then-redirect when navigating to the page.
- (This results in friendly browser URLs.)
- </para>
+ <para> The <literal><page></literal> element defines a wait state where the
+ system displays a particular JSF view and waits for user input. The
+ <literal>view-id</literal> is the same JSF view id used in plain JSF navigation rules.
+ The <literal>redirect</literal> attribute tells Seam to use post-then-redirect when
+ navigating to the page. (This results in friendly browser URLs.) </para>
</callout>
<callout arearefs="numberguess-transition">
- <para>
- The <literal><transition></literal> element
- names a JSF outcome. The transition is triggered
- when a JSF action results in that outcome. Execution
- will then proceed to the next node of the pageflow
- graph, after invocation of any jBPM transition
- actions.
- </para>
+ <para> The <literal><transition></literal> element names a JSF outcome. The
+ transition is triggered when a JSF action results in that outcome. Execution will then
+ proceed to the next node of the pageflow graph, after invocation of any jBPM transition
+ actions. </para>
</callout>
<callout arearefs="numberguess-action">
- <para>
- A transition <literal><action></literal> is just
- like a JSF action, except that it occurs when a jBPM
- transition occurs. The transition action can invoke
- any Seam component.
- </para>
+ <para> A transition <literal><action></literal> is just like a JSF action,
+ except that it occurs when a jBPM transition occurs. The transition action can invoke
+ any Seam component. </para>
</callout>
<callout arearefs="numberguess-decision">
- <para>
- A <literal><decision></literal> node branches
- the pageflow, and determines the next node to execute
- by evaluating a JSF EL expression.
- </para>
+ <para> A <literal><decision></literal> node branches the pageflow, and
+ determines the next node to execute by evaluating a JSF EL expression. </para>
</callout>
</calloutlist>
</programlistingco>
</example>
- <para>
- Here is what the pageflow looks like in the JBossIDE pageflow
- editor:
- </para>
+ <para> Here is what the pageflow looks like in the JBossIDE pageflow editor: </para>
<mediaobject>
<imageobject role="fo">
@@ -1801,90 +1530,129 @@
</imageobject>
</mediaobject>
- <para>
- Now that we have seen the pageflow, it is very, very easy to
- understand the rest of the application!
- </para>
+ <para> Now that we have seen the pageflow, it is very, very easy to understand the rest of the application! </para>
- <para>
- Here is the main page of the application,
- <literal>numberGuess.jsp</literal>:
- </para>
+ <para> Here is the main page of the application, <literal>numberGuess.jspx</literal>: </para>
<example>
- <programlisting><![CDATA[<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
-<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
-<html>
-<head>
-<title>Guess a number...</title>
-</head>
-<body>
-<h1>Guess a number...</h1>
-<f:view>
- <h:form>
- <h:outputText value="Higher!" rendered="#{numberGuess.randomNumber>numberGuess.currentGuess}" />
- <h:outputText value="Lower!" rendered="#{numberGuess.randomNumber<numberGuess.currentGuess}" />
- <br />
- I'm thinking of a number between <h:outputText value="#{numberGuess.smallest}" /> and
- <h:outputText value="#{numberGuess.biggest}" />. You have
- <h:outputText value="#{numberGuess.remainingGuesses}" /> guesses.
- <br />
+ <programlisting><![CDATA[<<?xml version="1.0"?>
+<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:s="http://jboss.com/products/seam/taglib"
+ xmlns="http://www.w3.org/1999/xhtml"
+ version="2.0">
+ <jsp:output doctype-root-element="html"
+ doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
+ doctype-system="http://www.w3c.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>
+ <jsp:directive.page contentType="text/html"/>
+ <html>
+ <head>
+ <title>Guess a number...</title>
+ <link href="niceforms.css" rel="stylesheet" type="text/css" />
+ <script language="javascript" type="text/javascript" src="niceforms.js" />
+ </head>
+ <body>
+ <h1>Guess a number...</h1>
+ <f:view>
+ <h:form styleClass="niceform">
+
+ <div>
+ <h:messages globalOnly="true"/>
+ <h:outputText value="Higher!"
+ rendered="#{numberGuess.randomNumber gt numberGuess.currentGuess}"/>
+ <h:outputText value="Lower!"
+ rendered="#{numberGuess.randomNumber lt numberGuess.currentGuess}"/>
+ </div>
+
+ <div>
+ I'm thinking of a number between
+ <h:outputText value="#{numberGuess.smallest}"/> and
+ <h:outputText value="#{numberGuess.biggest}"/>. You have
+ <h:outputText value="#{numberGuess.remainingGuesses}"/> guesses.
+ </div>
+
+ <div>
Your guess:
- <h:inputText value="#{numberGuess.currentGuess}" id="guess" required="true">
- <f:validateLongRange
- maximum="#{numberGuess.biggest}"
+ <h:inputText value="#{numberGuess.currentGuess}" id="inputGuess"
+ required="true" size="3"
+ rendered="#{(numberGuess.biggest-numberGuess.smallest) gt 20}">
+ <f:validateLongRange maximum="#{numberGuess.biggest}"
minimum="#{numberGuess.smallest}"/>
</h:inputText>
- <h:commandButton type="submit" value="Guess" action="guess" />
- <br/>
- <h:message for="guess" style="color: red"/>
+ <h:selectOneMenu value="#{numberGuess.currentGuess}"
+ id="selectGuessMenu" required="true"
+ rendered="#{(numberGuess.biggest-numberGuess.smallest) le 20 and
+ (numberGuess.biggest-numberGuess.smallest) gt 4}">
+ <s:selectItems value="#{numberGuess.possibilities}" var="i" label="#{i}"/>
+ </h:selectOneMenu>
+ <h:selectOneRadio value="#{numberGuess.currentGuess}" id="selectGuessRadio"
+ required="true"
+ rendered="#{(numberGuess.biggest-numberGuess.smallest) le 4}">
+ <s:selectItems value="#{numberGuess.possibilities}" var="i" label="#{i}"/>
+ </h:selectOneRadio>
+ <h:commandButton value="Guess" action="guess"/>
+ <s:button value="Cheat" view="/confirm.jspx"/>
+ <s:button value="Give up" action="giveup"/>
+ </div>
+
+ <div>
+ <h:message for="inputGuess" style="color: red"/>
+ </div>
+
</h:form>
-</f:view>
-</body>
-</html>]]></programlisting>
+ </f:view>
+ </body>
+ </html>
+</jsp:root>]]></programlisting>
</example>
- <para>
- Notice how the command button names the <literal>guess</literal> transition
- instead of calling an action directly.
- </para>
+ <para> Notice how the command button names the <literal>guess</literal> transition instead of calling an
+ action directly. </para>
- <para>
- The <literal>win.jsp</literal> page is predictable:
- </para>
+ <para> The <literal>win.jspx</literal> page is predictable: </para>
<example>
- <programlisting><![CDATA[<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
-<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
-<html>
-<head>
-<title>You won!</title>
-</head>
-<body>
-<h1>You won!</h1>
-<f:view>
+ <programlisting><![CDATA[<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns="http://www.w3.org/1999/xhtml"
+ version="2.0">
+ <jsp:output doctype-root-element="html"
+ doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
+ doctype-system="http://www.w3c.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>
+ <jsp:directive.page contentType="text/html"/>
+ <html>
+ <head>
+ <title>You won!</title>
+ <link href="niceforms.css" rel="stylesheet" type="text/css" />
+ </head>
+ <body>
+ <h1>You won!</h1>
+ <f:view>
Yes, the answer was <h:outputText value="#{numberGuess.currentGuess}" />.
It took you <h:outputText value="#{numberGuess.guessCount}" /> guesses.
+ <h:outputText value="But you cheated, so it doesn't count!"
+ rendered="#{numberGuess.cheat}"/>
Would you like to <a href="numberGuess.seam">play again</a>?
</f:view>
-</body>
-</html>]]></programlisting>
+ </body>
+ </html>
+</jsp:root>
+]]></programlisting>
</example>
- <para>
- As is <literal>lose.jsp</literal> (which I can't be bothered copy/pasting).
- Finally, the JavaBean Seam component:
- </para>
+ <para> As is <literal>lose.jspx</literal> (which I can't be bothered copy/pasting). Finally, the JavaBean
+ Seam component: </para>
<example>
<programlistingco>
<areaspec>
- <area id="numberguess-create" coords="12"/>
- <area id="numberguess-begin" coords="13"/>
+ <area id="numberguess-create" coords="13"/>
</areaspec>
<programlisting><![CDATA[@Name("numberGuess")
@Scope(ScopeType.CONVERSATION)
-public class NumberGuess {
+public class NumberGuess implements Serializable {
private int randomNumber;
private Integer currentGuess;
@@ -1892,9 +1660,9 @@
private int smallest;
private int guessCount;
private int maxGuesses;
+ private boolean cheated;
@Create
- @Begin(pageflow="numberGuess")
public void begin()
{
randomNumber = new Random().nextInt(100);
@@ -1966,34 +1734,62 @@
public int getRandomNumber() {
return randomNumber;
}
-}]]></programlisting>
+
+ public void cheated()
+ {
+ cheated = true;
+ }
+
+ public boolean isCheat() {
+ return cheated;
+ }
+
+ public List<Integer> getPossibilities()
+ {
+ List<Integer> result = new ArrayList<Integer>();
+ for(int i=smallest; i<=biggest; i++) result.add(i);
+ return result;
+ }
+
+}
+]]></programlisting>
<calloutlist>
<callout arearefs="numberguess-create">
- <para>
- The first time a JSP page asks for a <literal>numberGuess</literal>
- component, Seam will create a new one for it, and the
- <literal>@Create</literal> method will be invoked, allowing
- the component to initialize itself.
- </para>
- </callout>
- <callout arearefs="numberguess-begin">
- <para>
- The <literal>@Begin</literal> annotation starts a Seam
- <emphasis>conversation</emphasis> (much more about that
- later), and specifies the pageflow definition to use for
- the conversation's page flow.
- </para>
+ <para> The first time a JSP page asks for a <literal>numberGuess</literal> component, Seam
+ will create a new one for it, and the <literal>@Create</literal> method will be invoked,
+ allowing the component to initialize itself. </para>
</callout>
</calloutlist>
</programlistingco>
</example>
- <para>
- As you can see, this Seam component is pure business logic! It
- doesn't need to know anything at all about the user interaction
- flow. This makes the component potentially more reuseable.
+
+ <para>The <literal>pages.xml</literal> file starts a Seam
+ <emphasis>conversation</emphasis> (much more about that later), and specifies the
+ pageflow definition to use for the conversation's page flow.
</para>
+
+ <example>
+ <programlisting><![CDATA[
+<!DOCTYPE pages PUBLIC
+ "-//JBoss/Seam Pages Configuration DTD 1.3//EN"
+ "http://jboss.com/products/seam/pages-1.3.dtd">
+
+<pages>
+ <page view-id="/numberGuess.jspx">
+ <begin-conversation join="true" pageflow="numberGuess"/>
+ </page>
+ <page view-id="/confirm.jspx">
+ <begin-conversation nested="true" pageflow="cheat"/>
+ </page>
+</pages>
+]]></programlisting>
+ </example>
+
+ <para> As you can see, this Seam component is pure business logic! It doesn't need to know anything at all
+ about the user interaction flow. This makes the component potentially more reuseable. </para>
+
</section>
<section>
@@ -2009,10 +1805,8 @@
<section>
<title>Introduction</title>
- <para>
- The booking application is a complete hotel room reservation system
- incorporating the following features:
- </para>
+ <para> The booking application is a complete hotel room reservation system incorporating the following
+ features: </para>
<itemizedlist>
<listitem>
@@ -2056,80 +1850,56 @@
</mediaobject>
</screenshot>
- <para>
- The booking application uses JSF, EJB 3.0 and Seam, together with Facelets for the
- view. There is also a port of this application to JSF, Facelets, Seam, JavaBeans
- and Hibernate3.
- </para>
+ <para> The booking application uses JSF, EJB 3.0 and Seam, together with Facelets for the view. There is
+ also a port of this application to JSF, Facelets, Seam, JavaBeans and Hibernate3. </para>
- <para>
- One of the things you'll notice if you play with this application for long enough
- is that it is extremely <emphasis>robust</emphasis>. You can play with back buttons
- and browser refresh and opening multiple windows and entering nonsensical data as
- much as you like and you will find it very difficult to make the application crash.
- You might think that we spent weeks testing and fixing bugs to achive this. Actually,
- this is not the case. Seam was designed to make it very straightforward to build
- robust web applications and a lot of robustness that you are probably used to having to
- code yourself comes naturally and automatically with Seam.
- </para>
- <para>
- As you browse the sourcecode of the example application, and learn how the application
- works, observe how the declarative state management and integrated validation has been
- used to achieve this robustness.
- </para>
+ <para> One of the things you'll notice if you play with this application for long enough is that it is
+ extremely <emphasis>robust</emphasis>. You can play with back buttons and browser refresh and opening
+ multiple windows and entering nonsensical data as much as you like and you will find it very difficult
+ to make the application crash. You might think that we spent weeks testing and fixing bugs to achive
+ this. Actually, this is not the case. Seam was designed to make it very straightforward to build robust
+ web applications and a lot of robustness that you are probably used to having to code yourself comes
+ naturally and automatically with Seam. </para>
+ <para> As you browse the sourcecode of the example application, and learn how the application works, observe
+ how the declarative state management and integrated validation has been used to achieve this robustness. </para>
</section>
<section>
<title>Overview of the booking example</title>
- <para>
- The project structure is identical to the previous one, to install and deploy this
- application, please refer to <xref linkend="try-examples"/>. Once you've successfully
- started the application, you can access it by pointing your browser to
- <ulink url="http://localhost:8080/seam-booking/"><literal>http://localhost:8080/seam-booking/</literal></ulink>
+ <para> The project structure is identical to the previous one, to install and deploy this application,
+ please refer to <xref linkend="try-examples"/>. Once you've successfully started the application, you
+ can access it by pointing your browser to <ulink url="http://localhost:8080/seam-booking/">
+ <literal>http://localhost:8080/seam-booking/</literal>
+ </ulink>
</para>
- <para>
- Just nine classes (plus six session beans local interfaces)
- where used to implement this application.
- Six session bean action listeners contain all the business logic for the listed features.
- </para>
+ <para> Just nine classes (plus six session beans local interfaces) where used to implement this application.
+ Six session bean action listeners contain all the business logic for the listed features. </para>
<itemizedlist>
<listitem>
- <literal>BookingListAction</literal> retrieves existing bookings for the currently
- logged in user.
- </listitem>
+ <literal>BookingListAction</literal> retrieves existing bookings for the currently logged in user. </listitem>
<listitem>
- <literal>ChangePasswordAction</literal> updates the password of the currently logged
- in user.
- </listitem>
+ <literal>ChangePasswordAction</literal> updates the password of the currently logged in user. </listitem>
<listitem>
- <literal>HotelBookingAction</literal> implements the core functionality of the application:
- hotel room searching, selection, booking and booking confirmation. This functionality is
- implemented as a <emphasis>conversation</emphasis>, so this is the most interesting class
- in the application.
- </listitem>
+ <literal>HotelBookingAction</literal> implements the core functionality of the application: hotel
+ room searching, selection, booking and booking confirmation. This functionality is implemented as a
+ <emphasis>conversation</emphasis>, so this is the most interesting class in the application. </listitem>
<listitem>
- <literal>RegisterAction</literal> registers a new system user.
- </listitem>
+ <literal>RegisterAction</literal> registers a new system user. </listitem>
</itemizedlist>
- <para>
- Three entity beans implement the application's persistent domain model.
- </para>
+ <para> Three entity beans implement the application's persistent domain model. </para>
<itemizedlist>
<listitem>
- <literal>Hotel</literal> is an entity bean that represent a hotel
- </listitem>
+ <literal>Hotel</literal> is an entity bean that represent a hotel </listitem>
<listitem>
- <literal>Booking</literal> is an entity bean that represents an existing booking
- </listitem>
+ <literal>Booking</literal> is an entity bean that represents an existing booking </listitem>
<listitem>
- <literal>User</literal> is an entity bean to represents a user who can make
- hotel bookings
+ <literal>User</literal> is an entity bean to represents a user who can make hotel bookings
</listitem>
</itemizedlist>
@@ -2137,99 +1907,74 @@
<section>
<title>Understanding Seam conversations</title>
- <para>
- We encourage you browse the sourcecode at your pleasure. In this tutorial we'll concentrate
- upon one particular piece of functionality: hotel search, selection, booking and confirmation.
- From the point of view of the user, everything from selecting a hotel to confirming a booking
- is one continuous unit of work, a <emphasis>conversation</emphasis>. Searching, however, is
- <emphasis>not</emphasis> part of the conversation. The user can select multiple hotels from
- the same search results page, in different browser tabs.
- </para>
- <para>
- Most web application architectures have no first class construct to represent a conversation.
- This causes enormous problems managing state associated with the conversation. Usually, Java
- web applications use a combination of two techniques: first, some state is thrown into the
- <literal>HttpSession</literal>; second, persistable state is flushed to the database after
- every request, and reconstructed from the database at the beginning of each new request.
- </para>
- <para>
- Since the database is the least scalable tier, this often results in an utterly unacceptable
- lack of scalability. Added latency is also a problem, due to the extra traffic to and from the
- database on every request. To reduce this redundant traffic, Java applications often introduce
- a data (second-level) cache that keeps commonly accessed data between requests. This cache is
- necessarily inefficient, because invalidation is based upon an LRU policy instead of being based
- upon when the user has finished working with the data. Furthermore, because the cache is shared
- between many concurrent transactions, we've introduced a whole raft of problem's associated with
- keeping the cached state consistent with the database.
- </para>
- <para>
- Now consider the state held in the <literal>HttpSession</literal>. By very careful programming,
- we might be able to control the size of the session data. This is a lot more difficult than it sounds,
+ <para> We encourage you browse the sourcecode at your pleasure. In this tutorial we'll concentrate upon one
+ particular piece of functionality: hotel search, selection, booking and confirmation. From the point of
+ view of the user, everything from selecting a hotel to confirming a booking is one continuous unit of
+ work, a <emphasis>conversation</emphasis>. Searching, however, is <emphasis>not</emphasis> part of the
+ conversation. The user can select multiple hotels from the same search results page, in different
+ browser tabs. </para>
+ <para> Most web application architectures have no first class construct to represent a conversation. This
+ causes enormous problems managing state associated with the conversation. Usually, Java web applications
+ use a combination of two techniques: first, some state is thrown into the
+ <literal>HttpSession</literal>; second, persistable state is flushed to the database after every
+ request, and reconstructed from the database at the beginning of each new request. </para>
+ <para> Since the database is the least scalable tier, this often results in an utterly unacceptable lack of
+ scalability. Added latency is also a problem, due to the extra traffic to and from the database on every
+ request. To reduce this redundant traffic, Java applications often introduce a data (second-level) cache
+ that keeps commonly accessed data between requests. This cache is necessarily inefficient, because
+ invalidation is based upon an LRU policy instead of being based upon when the user has finished working
+ with the data. Furthermore, because the cache is shared between many concurrent transactions, we've
+ introduced a whole raft of problem's associated with keeping the cached state consistent with the
+ database. </para>
+ <para> Now consider the state held in the <literal>HttpSession</literal>. By very careful programming, we
+ might be able to control the size of the session data. This is a lot more difficult than it sounds,
since web browsers permit ad hoc non-linear navigation. But suppose we suddenly discover a system
- requirement that says that a user is allowed to have <emphasis>mutiple concurrent conversations</emphasis>,
- halfway through the development of the system (this has happened to me). Developing mechanisms
- to isolate session state associated with different concurrent conversations, and incorporating
- failsafes to ensure that conversation state is destroyed when the user aborts one of the
- conversations by closing a browser window or tab is not for the faint hearted (I've implemented
- this stuff twice so far, once for a client application, once for Seam, but I'm famously
- psychotic).
- </para>
- <para>
- Now there is a better way.
- </para>
- <para>
- Seam introduces the <emphasis>conversation context</emphasis> as a first class construct. You
- can safely keep conversational state in this context, and be assured that it will have a
- well-defined lifecycle. Even better, you won't need to be continually pushing data back and
- forth between the application server and the database, since the conversation context is a
- natural cache of data that the user is currently working with.
- </para>
- <para>
- Usually, the components we keep in the conversation context are stateful session beans. (We
- can also keep entity beans and JavaBeans in the conversation context.) There is an ancient
- canard in the Java community that stateful session beans are a scalability killer. This may
- have been true in 1998 when WebFoobar 1.0 was released. It is no longer true today.
- Application servers like JBoss 4.0 have extremely sophisticated mechanisms for stateful
- session bean state replication. (For example, the JBoss EJB3 container performs fine-grained
- replication, replicating only those bean attribute values which actually changed.) Note
- that all the traditional technical arguments for why stateful beans are inefficient apply
- equally to the <literal>HttpSession</literal>, so the practice of shifting state from business
- tier stateful session bean components to the web session to try and improve performance is
- unbelievably misguided. It is certainly possible to write unscalable applications using
- stateful session beans, by using stateful beans incorrectly, or by using them for the wrong
- thing. But that doesn't mean you should <emphasis>never</emphasis> use them. Anyway, Seam
- guides you toward a safe usage model. Welcome to 2005.
- </para>
- <para>
- OK, I'll stop ranting now, and get back to the tutorial.
- </para>
+ requirement that says that a user is allowed to have <emphasis>mutiple concurrent
+ conversations</emphasis>, halfway through the development of the system (this has happened to me).
+ Developing mechanisms to isolate session state associated with different concurrent conversations, and
+ incorporating failsafes to ensure that conversation state is destroyed when the user aborts one of the
+ conversations by closing a browser window or tab is not for the faint hearted (I've implemented this
+ stuff twice so far, once for a client application, once for Seam, but I'm famously psychotic). </para>
+ <para> Now there is a better way. </para>
+ <para> Seam introduces the <emphasis>conversation context</emphasis> as a first class construct. You can
+ safely keep conversational state in this context, and be assured that it will have a well-defined
+ lifecycle. Even better, you won't need to be continually pushing data back and forth between the
+ application server and the database, since the conversation context is a natural cache of data that the
+ user is currently working with. </para>
+ <para> Usually, the components we keep in the conversation context are stateful session beans. (We can also
+ keep entity beans and JavaBeans in the conversation context.) There is an ancient canard in the Java
+ community that stateful session beans are a scalability killer. This may have been true in 1998 when
+ WebFoobar 1.0 was released. It is no longer true today. Application servers like JBoss AS have extremely
+ sophisticated mechanisms for stateful session bean state replication. (For example, the JBoss EJB3
+ container performs fine-grained replication, replicating only those bean attribute values which actually
+ changed.) Note that all the traditional technical arguments for why stateful beans are inefficient apply
+ equally to the <literal>HttpSession</literal>, so the practice of shifting state from business tier
+ stateful session bean components to the web session to try and improve performance is unbelievably
+ misguided. It is certainly possible to write unscalable applications using stateful session beans, by
+ using stateful beans incorrectly, or by using them for the wrong thing. But that doesn't mean you should
+ <emphasis>never</emphasis> use them. Anyway, Seam guides you toward a safe usage model. Welcome to
+ 2005. </para>
+ <para> OK, I'll stop ranting now, and get back to the tutorial. </para>
+
+ <para> The booking example application shows how stateful components with different scopes can collaborate
+ together to achieve complex behaviors. The main page of the booking application allows the user to
+ search for hotels. The search results are kept in the Seam session scope. When the user navigates to one
+ of these hotels, a conversation begins, and a conversation scoped component calls back to the session
+ scoped component to retrieve the selected hotel. </para>
- <para>
- The booking example application shows how stateful components with different scopes can
- collaborate together to achieve complex behaviors. The main page of the booking application
- allows the user to search for hotels. The search results are kept in the Seam session scope.
- When the user navigates to one of these hotels, a conversation begins, and a conversation
- scoped component calls back to the session scoped component to retrieve the selected
- hotel.
- </para>
+ <para> The booking example also demonstrates the use of Ajax4JSF to implement rich client behavior without
+ the use of handwritten JavaScript. </para>
- <para>
- The booking example also demonstrates the use of Ajax4JSF to implement rich client behavior
- without the use of handwritten JavaScript.
- </para>
-
- <para>
- The search functionality is implemented using a session-scope stateful session bean,
- similar to the one we saw in the message list example above.
- </para>
+ <para> The search functionality is implemented using a session-scope stateful session bean, similar to the
+ one we saw in the message list example above. </para>
<example>
<programlistingco>
<areaspec>
<area id="booking-stateful-annotation" coords="1"/>
<area id="booking-restrict-annotation" coords="4"/>
- <area id="booking-datamodel-annotation" coords="16"/>
- <area id="booking-destroy-annotation" coords="66"/>
+ <area id="booking-datamodel-annotation" coords="15"/>
+ <area id="booking-destroy-annotation" coords="70"/>
</areaspec>
<programlisting><![CDATA[@Stateful
@Name("hotelSearch")
@@ -2248,25 +1993,24 @@
@DataModel
private List<Hotel> hotels;
- public String find()
+ public void find()
{
page = 0;
queryHotels();
- return "main";
}
-
- public String nextPage()
+ public void nextPage()
{
page++;
queryHotels();
- return "main";
}
private void queryHotels()
{
- String searchPattern = searchString==null ? "%" : '%' + searchString.toLowerCase().replace('*', '%') + '%';
- hotels = em.createQuery("select h from Hotel h where lower(h.name) like :search or lower(h.city) like :search or lower(h.zip) like :search or lower(h.address) like :search")
- .setParameter("search", searchPattern)
+ 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();
@@ -2285,6 +2029,13 @@
this.pageSize = pageSize;
}
+ @Factory(value="pattern", scope=ScopeType.EVENT)
+ public String getSearchPattern()
+ {
+ return searchString==null ?
+ "%" : '%' + searchString.toLowerCase().replace('*', '%') + '%';
+ }
+
public String getSearchString()
{
return searchString;
@@ -2297,79 +2048,73 @@
@Destroy @Remove
public void destroy() {}
-
}]]></programlisting>
<calloutlist>
<callout arearefs="booking-stateful-annotation">
- <para>
- The EJB standard <literal>@Stateful</literal> annotation identifies
- this class as a stateful session bean. Stateful session beans are
- scoped to the conversation context by default.
- </para>
+ <para> The EJB standard <literal>@Stateful</literal> annotation identifies this class as a
+ stateful session bean. Stateful session beans are scoped to the conversation context by
+ default. </para>
</callout>
<callout arearefs="booking-restrict-annotation">
- <para>
- The <literal>@Restrict</literal> annotation applies a security restriction to the
+ <para> The <literal>@Restrict</literal> annotation applies a security restriction to the
component. It restricts access to the component allowing only logged-in users. The
- security chapter explains more about security in Seam.
- </para>
+ security chapter explains more about security in Seam. </para>
</callout>
<callout arearefs="booking-datamodel-annotation">
- <para>
- The <link linkend="datamodel-annotation"><literal>@DataModel</literal></link>
- annotation exposes a <literal>List</literal> as a JSF <literal>ListDataModel</literal>.
- This makes it easy to implement clickable lists for search screens. In this
- case, the list of hotels is exposed to the page as a <literal>ListDataModel</literal>
- in the conversation variable named <literal>hotels</literal>.
- </para>
+ <para> The <link linkend="datamodel-annotation">
+ <literal>@DataModel</literal>
+ </link> annotation exposes a <literal>List</literal> as a JSF
+ <literal>ListDataModel</literal>. This makes it easy to implement clickable lists for
+ search screens. In this case, the list of hotels is exposed to the page as a
+ <literal>ListDataModel</literal> in the conversation variable named
+ <literal>hotels</literal>. </para>
</callout>
<callout arearefs="booking-destroy-annotation">
- <para>
- The EJB standard <literal>@Remove</literal> annotation specifies that a stateful
- session bean should be removed and its state destroyed after invocation of the
- annotated method. In Seam, all stateful session beans should define a method
- marked <literal>@Destroy @Remove</literal>. This is the EJB remove method that
- will be called when Seam destroys the session context. Actually, the
- <link linkend="destroy-annotation"><literal>@Destroy</literal></link>
- annotation is of more general usefulness, since it can be used for any
- kind of cleanup that should happen when any Seam context ends. If you don't
- have an <literal>@Destroy @Remove</literal> method, state will leak and you
- will suffer performance problems.
- </para>
+ <para> The EJB standard <literal>@Remove</literal> annotation specifies that a stateful
+ session bean should be removed and its state destroyed after invocation of the annotated
+ method. In Seam, all stateful session beans should define a method marked
+ <literal>@Destroy @Remove</literal>. This is the EJB remove method that will be
+ called when Seam destroys the session context. Actually, the <link
+ linkend="destroy-annotation">
+ <literal>@Destroy</literal>
+ </link> annotation is of more general usefulness, since it can be used for any kind of
+ cleanup that should happen when any Seam context ends. If you don't have an
+ <literal>@Destroy @Remove</literal> method, state will leak and you will suffer
+ performance problems. </para>
</callout>
</calloutlist>
</programlistingco>
</example>
- <para>
- The main page of the application is a Facelets page. Let's look at the fragment which relates
- to searching for hotels:
- </para>
+ <para> The main page of the application is a Facelets page. Let's look at the fragment which relates to
+ searching for hotels: </para>
<example>
<programlistingco>
<areaspec>
- <area id="booking-support-element" coords="11"/>
- <area id="booking-status-element" coords="18"/>
- <area id="booking-outputpanel-element" coords="35"/>
- <area id="booking-link-element" coords="58"/>
+ <area id="booking-support-element" coords="14"/>
+ <area id="booking-status-element" coords="20"/>
+ <area id="booking-outputpanel-element" coords="37"/>
+ <area id="booking-link-element" coords="61"/>
</areaspec>
<programlisting><![CDATA[<div class="section">
-<h:form>
<span class="errors">
<h:messages globalOnly="true"/>
</span>
<h1>Search Hotels</h1>
+
+ <h:form id="searchCriteria">
<fieldset>
- <h:inputText value="#{hotelSearch.searchString}" style="width: 165px;">
+ <h:inputText id="searchString" value="#{hotelSearch.searchString}"
+ style="width: 165px;">
<a:support event="onkeyup" actionListener="#{hotelSearch.find}"
reRender="searchResults" />
</h:inputText>
 
- <a:commandButton value="Find Hotels" action="#{hotelSearch.find}"
- styleClass="button" reRender="searchResults"/>
+ <a:commandButton id="findHotels" value="Find Hotels" action="#{hotelSearch.find}"
+ reRender="searchResults"/>
 
<a:status>
<f:facet name="start">
@@ -2384,15 +2129,16 @@
<f:selectItem itemLabel="20" itemValue="20"/>
</h:selectOneMenu>
</fieldset>
+ </h:form>
-</h:form>
</div>
<a:outputPanel id="searchResults">
<div class="section">
<h:outputText value="No Hotels Found"
rendered="#{hotels != null and hotels.rowCount==0}"/>
- <h:dataTable value="#{hotels}" var="hot" rendered="#{hotels.rowCount>0}">
+ <h:dataTable id="hotels" value="#{hotels}" var="hot"
+ rendered="#{hotels.rowCount>0}">
<h:column>
<f:facet name="header">Name</f:facet>
#{hot.name}
@@ -2411,75 +2157,60 @@
</h:column>
<h:column>
<f:facet name="header">Action</f:facet>
- <s:link value="View Hotel" action="#{hotelBooking.selectHotel(hot)}"/>
+ <s:link id="viewHotel" value="View Hotel"
+ action="#{hotelBooking.selectHotel(hot)}"/>
</h:column>
</h:dataTable>
<s:link value="More results" action="#{hotelSearch.nextPage}"
rendered="#{hotelSearch.nextPageAvailable}"/>
</div>
-</a:outputPanel>]]></programlisting>
+</a:outputPanel> ]]></programlisting>
<calloutlist>
<callout arearefs="booking-support-element">
- <para>
- The Ajax4JSF <literal><a:support></literal> tag allows a JSF action event
- listener to be called by asynchronous <literal>XMLHttpRequest</literal> when
- a JavaScript event like <literal>onkeyup</literal> occurs. Even better, the
- <literal>reRender</literal> attribute lets us render a fragment of the JSF
- page and perform a partial page update when the asynchronous response is
- received.
- </para>
+ <para> The Ajax4JSF <literal><a:support></literal> tag allows a JSF action
+ event listener to be called by asynchronous <literal>XMLHttpRequest</literal> when a
+ JavaScript event like <literal>onkeyup</literal> occurs. Even better, the
+ <literal>reRender</literal> attribute lets us render a fragment of the JSF page and
+ perform a partial page update when the asynchronous response is received. </para>
</callout>
<callout arearefs="booking-status-element">
- <para>
- The Ajax4JSF <literal><a:status></literal> tag lets us display a cheesy
- annimated image while we wait for asynchronous requests to return.
- </para>
+ <para> The Ajax4JSF <literal><a:status></literal> tag lets us display a cheesy
+ annimated image while we wait for asynchronous requests to return. </para>
</callout>
<callout arearefs="booking-outputpanel-element">
- <para>
- The Ajax4JSF <literal><a:outputPanel></literal> tag defines a region of
- the page which can be re-rendered by an asynchronous request.
- </para>
+ <para> The Ajax4JSF <literal><a:outputPanel></literal> tag defines a region of
+ the page which can be re-rendered by an asynchronous request. </para>
</callout>
<callout arearefs="booking-link-element">
- <para>
- The Seam <literal><s:link></literal> tag lets us attach a JSF action
- listener to an ordinary (non-JavaScript) HTML link. The advantage of this
- over the standard JSF <literal><h:commandLink></literal> is that it
- preserves the operation of "open in new window" and "open in new tab". Also
- notice that we use a method binding with a parameter:
- <literal>#{hotelBooking.selectHotel(hot)}</literal>. This is not possible
- in the standard Unified EL, but Seam provides an extension to the EL that
- lets you use parameters on any method binding expression.
- </para>
+ <para> The Seam <literal><s:link></literal> tag lets us attach a JSF action
+ listener to an ordinary (non-JavaScript) HTML link. The advantage of this over the
+ standard JSF <literal><h:commandLink></literal> is that it preserves the
+ operation of "open in new window" and "open in new tab". Also notice that we use a
+ method binding with a parameter: <literal>#{hotelBooking.selectHotel(hot)}</literal>.
+ This is not possible in the standard Unified EL, but Seam provides an extension to the
+ EL that lets you use parameters on any method binding expression. </para>
</callout>
</calloutlist>
</programlistingco>
</example>
- <para>
- This page displays the search results dynamically as we type, and lets us choose a hotel
- and pass it to the <literal>selectHotel()</literal> method of the
- <literal>HotelBookingAction</literal>, which is where the <emphasis>really</emphasis>
- interesting stuff is going to happen.
- </para>
-
- <para>
- Now lets see how the booking example application uses a conversation-scoped stateful session
- bean to achieve a natural cache of persistent data related to the conversation. The following
- code example is pretty long. But if you think of it as a list of scripted actions that
- implement the various steps of the conversation, it's understandable. Read the class from top
- to bottom, as if it were a story.
- </para>
+ <para> This page displays the search results dynamically as we type, and lets us choose a hotel and pass it
+ to the <literal>selectHotel()</literal> method of the <literal>HotelBookingAction</literal>, which is
+ where the <emphasis>really</emphasis> interesting stuff is going to happen. </para>
+
+ <para> Now lets see how the booking example application uses a conversation-scoped stateful session bean to
+ achieve a natural cache of persistent data related to the conversation. The following code example is
+ pretty long. But if you think of it as a list of scripted actions that implement the various steps of
+ the conversation, it's understandable. Read the class from top to bottom, as if it were a story. </para>
<example>
<programlistingco>
<areaspec>
<area id="booking-extendedpersistencecontext-annotation" coords="7"/>
- <area id="booking-out-annotation" coords="10"/>
- <area id="booking-begin-annotation" coords="29"/>
- <area id="booking-end-annotation" coords="61"/>
- <area id="booking-dest-annotation" coords="78"/>
+ <area id="booking-out-annotation" coords="17"/>
+ <area id="booking-begin-annotation" coords="31"/>
+ <area id="booking-end-annotation" coords="72"/>
+ <area id="booking-dest-annotation" coords="85"/>
</areaspec>
<programlisting><![CDATA[@Stateful
@Name("hotelBooking")
@@ -2509,169 +2240,155 @@
@Logger
private Log log;
+ private boolean bookingValid;
+
@Begin
- public String selectHotel(Hotel selectedHotel)
+ public void selectHotel(Hotel selectedHotel)
{
hotel = em.merge(selectedHotel);
- return "hotel";
}
- public String bookHotel()
+ 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() );
-
- return "book";
}
- public String setBookingDetails()
+ public void setBookingDetails()
{
- if (booking==null || hotel==null) return "main";
- if ( !booking.getCheckinDate().before( booking.getCheckoutDate() ) )
+ Calendar calendar = Calendar.getInstance();
+ calendar.add(Calendar.DAY_OF_MONTH, -1);
+ if ( booking.getCheckinDate().before( calendar.getTime() ) )
{
- facesMessages.add("Check out date must be later than check in date");
- return null;
+ 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
{
- return "confirm";
+ bookingValid=true;
}
}
+ public boolean isBookingValid()
+ {
+ return bookingValid;
+ }
+
@End
- public String confirm()
+ public void confirm()
{
- if (booking==null || hotel==null) return "main";
em.persist(booking);
- facesMessages.add("Thank you, #{user.name}, your confimation number for #{hotel.name} is #{booking.id}");
+ 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.raiseEvent("bookingConfirmed");
- return "confirmed";
+ events.raiseTransactionSuccessEvent("bookingConfirmed");
}
@End
- public String cancel()
- {
- return "main";
- }
+ public void cancel() {}
@Destroy @Remove
public void destroy() {}
-
-}]]></programlisting>
+]]></programlisting>
<calloutlist>
<callout arearefs="booking-extendedpersistencecontext-annotation">
- <para>
- This bean uses an EJB3 <emphasis>extended persistence context</emphasis>, so that
- any entity instances remain managed for the whole lifecycle of the stateful
- session bean.
+ <para> This bean uses an EJB3 <emphasis>extended persistence context</emphasis>, so that any
+ entity instances remain managed for the whole lifecycle of the stateful session bean.
</para>
</callout>
<callout arearefs="booking-out-annotation">
- <para>
- The <link linkend="out-annotation"><literal>@Out</literal></link> annotation
- declares that an attribute value is <emphasis>outjected</emphasis> to a
- context variable after method invocations. In this case, the context variable
- named <literal>hotel</literal> will be set to the value of the
- <literal>hotel</literal> instance variable after every action listener
- invocation completes.
- </para>
+ <para> The <link linkend="out-annotation">
+ <literal>@Out</literal>
+ </link> annotation declares that an attribute value is <emphasis>outjected</emphasis> to
+ a context variable after method invocations. In this case, the context variable named
+ <literal>hotel</literal> will be set to the value of the <literal>hotel</literal>
+ instance variable after every action listener invocation completes. </para>
</callout>
<callout arearefs="booking-begin-annotation">
- <para>
- The <link linkend="begin-annotation"><literal>@Begin</literal></link> annotation
- specifies that the annotated method begins a
- <emphasis>long-running conversation</emphasis>, so the current conversation context
- will not be destroyed at the end of the request. Instead, it will be reassociated
- with every request from the current window, and destroyed either by timeout due to
- conversation inactivity or invocation of a matching <literal>@End</literal> method.
- </para>
+ <para> The <link linkend="begin-annotation">
+ <literal>@Begin</literal>
+ </link> annotation specifies that the annotated method begins a <emphasis>long-running
+ conversation</emphasis>, so the current conversation context will not be destroyed
+ at the end of the request. Instead, it will be reassociated with every request from the
+ current window, and destroyed either by timeout due to conversation inactivity or
+ invocation of a matching <literal>@End</literal> method. </para>
</callout>
<callout arearefs="booking-end-annotation">
- <para>
- The <link linkend="end-annotation"><literal>@End</literal></link> annotation
- specifies that the annotated method ends the current long-running conversation,
- so the current conversation context will be destroyed at the end of the request.
- </para>
+ <para> The <link linkend="end-annotation">
+ <literal>@End</literal>
+ </link> annotation specifies that the annotated method ends the current long-running
+ conversation, so the current conversation context will be destroyed at the end of the
+ request. </para>
</callout>
<callout arearefs="booking-dest-annotation">
- <para>
- This EJB remove method will be called when Seam destroys the conversation context.
- Don't ever forget to define this method!
- </para>
+ <para> This EJB remove method will be called when Seam destroys the conversation context.
+ Don't ever forget to define this method! </para>
</callout>
</calloutlist>
</programlistingco>
</example>
<para>
- <literal>HotelBookingAction</literal> contains all the action listener methods that implement
- selection, booking and booking confirmation, and holds state related to this work in its instance
- variables. We think you'll agree that this code is much cleaner and simpler than getting and
- setting <literal>HttpSession</literal> attributes.
- </para>
-
- <para>
- Even better, a user can have multiple isolated conversations per login session. Try it!
- Log in, run a search, and navigate to different hotel pages in multiple browser tabs.
- You'll be able to work on creating two different hotel reservations at the same time.
- If you leave any one conversation inactive for long enough, Seam will eventually time
- out that conversation and destroy its state. If, after ending a conversation, you
- backbutton 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.
- </para>
+ <literal>HotelBookingAction</literal> contains all the action listener methods that implement selection,
+ booking and booking confirmation, and holds state related to this work in its instance variables. We
+ think you'll agree that this code is much cleaner and simpler than getting and setting
+ <literal>HttpSession</literal> attributes. </para>
+
+ <para> Even better, a user can have multiple isolated conversations per login session. Try it! Log in, run a
+ search, and navigate to different hotel pages in multiple browser tabs. You'll be able to work on
+ creating two different hotel reservations at the same time. If you leave any one conversation inactive
+ for long enough, Seam will eventually time out that conversation and destroy its state. If, after ending
+ a conversation, you backbutton 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. </para>
</section>
<section>
<title>The Seam UI control library</title>
- <para>
- If you check inside the WAR file for the booking application, you'll find <literal>seam-ui.jar</literal>
- in the <literal>WEB-INF/lib</literal> directory. This package contains a number of JSF custom
- controls that integrate with Seam. The booking application uses the <literal><s:link></literal>
- control for navigation from the search screen to the hotel page:
- </para>
-
- <programlisting><![CDATA[<s:link value="View Hotel" action="#{hotelBooking.selectHotel}"/>]]></programlisting>
-
- <para>
- The use of <literal><s:link></literal> here allows us to attach an action listener
- to a HTML link without breaking the browser's "open in new window" feature. The standard
- JSF <literal><h:commandLink></literal> does not work with "open in new window".
- We'll see later that <literal><s:link></literal> also offers a number of other
- useful features, including conversation propagation rules.
- </para>
-
- <para>
- The booking application uses some other Seam and Ajax4JSF controls, especially on the
- <literal>/book.xhtml</literal> page. We won't get into the details of those controls
- here, but if you want to understand this code, please refer to the chapter covering
- Seam's functionality for JSF form validation.
- </para>
+ <para> If you check inside the WAR file for the booking application, you'll find
+ <literal>seam-ui.jar</literal> in the <literal>WEB-INF/lib</literal> directory. This package contains a
+ number of JSF custom controls that integrate with Seam. The booking application uses the
+ <literal><s:link></literal> control for navigation from the search screen to the hotel
+ page: </para>
+
+ <programlisting><![CDATA[<s:link value="View Hotel" action="#{hotelBooking.selectHotel(hot)}"/>]]></programlisting>
+
+ <para> The use of <literal><s:link></literal> here allows us to attach an action listener to a
+ HTML link without breaking the browser's "open in new window" feature. The standard JSF
+ <literal><h:commandLink></literal> does not work with "open in new window". We'll see
+ later that <literal><s:link></literal> also offers a number of other useful features,
+ including conversation propagation rules. </para>
+
+ <para> The booking application uses some other Seam and Ajax4JSF controls, especially on the
+ <literal>/book.xhtml</literal> page. We won't get into the details of those controls here, but if
+ you want to understand this code, please refer to the chapter covering Seam's functionality for JSF form
+ validation. </para>
</section>
<section>
<title>The Seam Debug Page</title>
- <para>
- The WAR also includes <literal>seam-debug.jar</literal>. If this jar is deployed in
- <literal>WEB-INF/lib</literal>, along with the Facelets, and if you set the following Seam
- property in <literal>web.xml</literal> or <literal>seam.properties</literal>:
- </para>
-
- <programlisting><![CDATA[<context-param>
- <param-name>org.jboss.seam.core.init.debug</param-name>
- <param-value>true</param-value>
-</context-param>]]></programlisting>
-
- <para>
- Then the Seam debug page will be available. This page lets you browse and inspect the Seam
- components in any of the Seam contexts associated with your current login session. Just
- point your browser at <ulink url="http://localhost:8080/seam-booking/debug.seam"><literal>http://localhost:8080/seam-booking/debug.seam</literal></ulink>.
- </para>
+ <para> The WAR also includes <literal>seam-debug.jar</literal>. The Seam debug page will be availabled
+ if this jar is deployed in
+ <literal>WEB-INF/lib</literal>, along with the Facelets, and if you set the debug property
+ of the <literal>init</literal> component:</para>
+
+ <programlisting><![CDATA[<core:init jndi-pattern="@jndiPattern@" debug="true"/>]]></programlisting>
+
+ <para> This page lets you browse and inspect the Seam components
+ in any of the Seam contexts associated with your current login session. Just point your browser at
+ <ulink url="http://localhost:8080/seam-booking/debug.seam">
+ <literal>http://localhost:8080/seam-booking/debug.seam</literal>
+ </ulink>. </para>
<mediaobject>
<imageobject role="fo">
@@ -2689,15 +2406,10 @@
<section id="dvdstore">
<title>A complete application featuring Seam and jBPM: the DVD Store example</title>
- <para>
- The DVD Store demo application shows the practical usage of jBPM for
- both task management and pageflow.
- </para>
+ <para> The DVD Store demo application shows the practical usage of jBPM for both task management and pageflow. </para>
- <para>
- The user screens take advantage of a jPDL pageflow to implement searching
- and shopping cart functionality.
- </para>
+ <para> The user screens take advantage of a jPDL pageflow to implement searching and shopping cart
+ functionality. </para>
<screenshot>
<screeninfo>DVD Store example</screeninfo>
@@ -2711,11 +2423,8 @@
</mediaobject>
</screenshot>
- <para>
- The administration screens take use jBPM to manage the approval and
- shipping cycle for orders. The business process may even be changed
- dynamically, by selecting a different process definition!
- </para>
+ <para> The administration screens take use jBPM to manage the approval and shipping cycle for orders. The
+ business process may even be changed dynamically, by selecting a different process definition! </para>
<screenshot>
<screeninfo>DVD Store example</screeninfo>
@@ -2736,10 +2445,8 @@
<section id="issues">
<title>A complete application featuring Seam workspace management: the Issue Tracker example</title>
- <para>
- The Issue Tracker demo shows off Seam's workspace management functionality:
- the conversation switcher, conversation list and breadcrumbs.
- </para>
+ <para> The Issue Tracker demo shows off Seam's workspace management functionality: the conversation switcher,
+ conversation list and breadcrumbs. </para>
<screenshot>
<screeninfo>DVD Store example</screeninfo>
@@ -2760,11 +2467,8 @@
<section id="hibernate">
<title>An example of Seam with Hibernate: the Hibernate Booking example</title>
- <para>
- The Hibernate Booking demo is a straight port of the Booking demo to an
- alternative architecture that uses Hibernate for persistence and JavaBeans
- instead of session beans.
- </para>
+ <para> The Hibernate Booking demo is a straight port of the Booking demo to an alternative architecture that
+ uses Hibernate for persistence and JavaBeans instead of session beans. </para>
<para>TODO</para>
<para>Look in the <literal>hibernate</literal> directory.</para>
@@ -2773,16 +2477,12 @@
<section id="blog">
<title>A RESTful Seam application: the Blog example</title>
- <para>
- Seam makes it very easy to implement applications which keep state on the
- server-side. However, server-side state is not always appropriate, especially
- in for functionality that serves up <emphasis>content</emphasis>. For this
- kind of problem we often need to let the user bookmark pages and have a
- relatively stateless server, so that any page can be accessed at any time,
- via the bookmark. The Blog example shows how to a implement RESTful
- application using Seam. Every page of the application can be bookmarked,
- including the search results page.
- </para>
+ <para> Seam makes it very easy to implement applications which keep state on the server-side. However,
+ server-side state is not always appropriate, especially in for functionality that serves up
+ <emphasis>content</emphasis>. For this kind of problem we often need to let the user bookmark pages and
+ have a relatively stateless server, so that any page can be accessed at any time, via the bookmark. The Blog
+ example shows how to a implement RESTful application using Seam. Every page of the application can be
+ bookmarked, including the search results page. </para>
<screenshot>
<screeninfo>Blog example</screeninfo>
@@ -2796,19 +2496,15 @@
</mediaobject>
</screenshot>
- <para>
- The Blog example demonstrates the use of "pull"-style MVC, where instead of using
- action listener methods to retrieve data and prepare the data for the view, the view
- pulls data from components as it is being rendered.
- </para>
+ <para> The Blog example demonstrates the use of "pull"-style MVC, where instead of using action listener methods
+ to retrieve data and prepare the data for the view, the view pulls data from components as it is being
+ rendered. </para>
<section>
<title>Using "pull"-style MVC</title>
- <para>
- This snippet from the <literal>index.xhtml</literal> facelets page displays a
- list of recent blog entries:
- </para>
+ <para> This snippet from the <literal>index.xhtml</literal> facelets page displays a list of recent blog
+ entries: </para>
<example>
<programlisting><![CDATA[<h:dataTable value="#{blog.recentBlogEntries}" var="blogEntry" rows="3">
@@ -2828,7 +2524,8 @@
<p>
[Posted on
<h:outputText value="#{blogEntry.date}">
- <f:convertDateTime timeZone="#{blog.timeZone}" locale="#{blog.locale}" type="both"/>
+ <f:convertDateTime timeZone="#{blog.timeZone}"
+ locale="#{blog.locale}" type="both"/>
</h:outputText>]
 
<h:outputLink value="entry.seam">[Link]
@@ -2840,14 +2537,11 @@
</h:dataTable>]]></programlisting>
</example>
- <para>
- If we navigate to this page from a bookmark, how does the data used by the
- <literal><h:dataTable></literal> actually get initialized? Well, what
- happens is that the <literal>Blog</literal> is retrieved
- lazily—"pulled"—when needed, by a Seam component named
- <literal>blog</literal>. This is the opposite flow of control to what is
- usual in traditional web action-based frameworks like Struts.
- </para>
+ <para> If we navigate to this page from a bookmark, how does the data used by the
+ <literal><h:dataTable></literal> actually get initialized? Well, what happens is that
+ the <literal>Blog</literal> is retrieved lazily—"pulled"—when needed, by a Seam
+ component named <literal>blog</literal>. This is the opposite flow of control to what is usual in
+ traditional web action-based frameworks like Struts. </para>
<example>
<programlistingco>
@@ -2874,42 +2568,33 @@
}]]></programlisting>
<calloutlist>
<callout arearefs="blog-seampc">
- <para>
- This component uses a <emphasis>seam-managed persistence context</emphasis>.
- Unlike the other examples we've seen, this persistence context is managed
- by Seam, instead of by the EJB3 container. The persistence context spans
- the entire web request, allowing us to avoid any exceptions that occur
- when accessing unfetched associations in the view.
+ <para> This component uses a <emphasis>seam-managed persistence context</emphasis>. Unlike
+ the other examples we've seen, this persistence context is managed by Seam, instead of
+ by the EJB3 container. The persistence context spans the entire web request, allowing us
+ to avoid any exceptions that occur when accessing unfetched associations in the view.
</para>
</callout>
<callout arearefs="blog-unwrap">
- <para>
- The <literal>@Unwrap</literal> annotation tells Seam to provide the return
- value of the method—the <literal>Blog</literal>—instead of the
- actual <literal>BlogService</literal> component to clients. This is the
- Seam <emphasis>manager component pattern</emphasis>.
- </para>
+ <para> The <literal>@Unwrap</literal> annotation tells Seam to provide the return value of
+ the method—the <literal>Blog</literal>—instead of the actual
+ <literal>BlogService</literal> component to clients. This is the Seam
+ <emphasis>manager component pattern</emphasis>. </para>
</callout>
</calloutlist>
</programlistingco>
</example>
- <para>
- This is good so far, but what about bookmarking the result of form submissions, such
- as a search results page?
- </para>
+ <para> This is good so far, but what about bookmarking the result of form submissions, such as a search
+ results page? </para>
</section>
<section>
<title>Bookmarkable search results page</title>
- <para>
- The blog example has a tiny form in the top right of each page that
- allows the user to search for blog entries. This is defined in a
- file, <literal>menu.xhtml</literal>, included by the facelets
- template, <literal>template.xhtml</literal>:
- </para>
+ <para> The blog example has a tiny form in the top right of each page that allows the user to search for
+ blog entries. This is defined in a file, <literal>menu.xhtml</literal>, included by the facelets
+ template, <literal>template.xhtml</literal>: </para>
<example>
<programlisting><![CDATA[<div id="search">
@@ -2920,12 +2605,10 @@
</div>]]></programlisting>
</example>
- <para>
- To implement a bookmarkable search results page, we need to perform a browser redirect
- after processing the search form submission. Because we used the JSF view id as the
- action outcome, Seam automatically redirects to the view id when the form is submitted.
- Alternatively, we could have defined a navigation rule like this:
- </para>
+ <para> To implement a bookmarkable search results page, we need to perform a browser redirect after
+ processing the search form submission. Because we used the JSF view id as the action outcome, Seam
+ automatically redirects to the view id when the form is submitted. Alternatively, we could have defined
+ a navigation rule like this: </para>
<example>
<programlisting><![CDATA[<navigation-rule>
@@ -2937,9 +2620,7 @@
</navigation-rule>]]></programlisting>
</example>
- <para>
- Then the form would have looked like this:
- </para>
+ <para> Then the form would have looked like this: </para>
<example>
<programlisting><![CDATA[<div id="search">
@@ -2950,13 +2631,11 @@
</div>]]></programlisting>
</example>
- <para>
- But when we redirect, we need to include the values submitted with the form as
- request parameters, to get a bookmarkable URL like
- <literal>http://localhost:8080/seam-blog/search.seam?searchPattern=seam</literal>.
- JSF does not provide an easy way to do this, but Seam does. We use a Seam
- <emphasis>page parameter</emphasis>, defined in <literal>WEB-INF/pages.xml</literal>:
- </para>
+ <para> But when we redirect, we need to include the values submitted with the form as request parameters, to
+ get a bookmarkable URL like
+ <literal>http://localhost:8080/seam-blog/search.seam?searchPattern=seam</literal>. JSF does not provide
+ an easy way to do this, but Seam does. We use a Seam <emphasis>page parameter</emphasis>, defined in
+ <literal>WEB-INF/pages.xml</literal>: </para>
<example>
<programlisting><![CDATA[<pages>
@@ -2967,16 +2646,11 @@
</pages>]]></programlisting>
</example>
- <para>
- This tells Seam to include the value of <literal>#{searchService.searchPattern}</literal>
- as a request parameter named <literal>searchPattern</literal> when redirecting to
- the page, and then re-apply the value of that parameter to the model before rendering
- the page.
- </para>
+ <para> This tells Seam to include the value of <literal>#{searchService.searchPattern}</literal> as a
+ request parameter named <literal>searchPattern</literal> when redirecting to the page, and then re-apply
+ the value of that parameter to the model before rendering the page. </para>
- <para>
- The redirect takes us to the <literal>search.xhtml</literal> page:
- </para>
+ <para> The redirect takes us to the <literal>search.xhtml</literal> page: </para>
<example>
<programlisting><![CDATA[<h:dataTable value="#{searchResults}" var="blogEntry">
@@ -2995,9 +2669,7 @@
</h:dataTable>]]></programlisting>
</example>
- <para>
- Which again uses "pull"-style MVC to retrieve the actual search results:
- </para>
+ <para> Which again uses "pull"-style MVC to retrieve the actual search results: </para>
<example>
<programlisting><![CDATA[@Name("searchService")
@@ -3018,7 +2690,9 @@
}
else
{
- return entityManager.createQuery("select be from BlogEntry be where lower(be.title) like :searchPattern or lower(be.body) like :searchPattern order by be.date desc")
+ return entityManager.createQuery("select be from BlogEntry be "" +
+ "where lower(be.title) like :searchPattern " +
+ "lower(be.body) like :searchPattern order by be.date desc")
.setParameter( "searchPattern", getSqlSearchPattern() )
.setMaxResults(100)
.getResultList();
@@ -3027,7 +2701,8 @@
private String getSqlSearchPattern()
{
- return searchPattern==null ? "" : '%' + searchPattern.toLowerCase().replace('*', '%').replace('?', '_') + '%';
+ return searchPattern==null ? "" :
+ '%' + searchPattern.toLowerCase().replace('*', '%').replace('?', '_') + '%';
}
public String getSearchPattern()
@@ -3048,17 +2723,13 @@
<section>
<title>Using "push"-style MVC in a RESTful application</title>
- <para>
- Very occasionally, it makes more sense to use push-style MVC for processing RESTful pages,
- and so Seam provides the notion of a <emphasis>page action</emphasis>. The Blog example uses
- a page action for the blog entry page, <literal>entry.xhtml</literal>. Note that this is a
- little bit contrived, it would have been easier to use pull-style MVC here as well.
- </para>
+ <para> Very occasionally, it makes more sense to use push-style MVC for processing RESTful pages, and so
+ Seam provides the notion of a <emphasis>page action</emphasis>. The Blog example uses a page action for
+ the blog entry page, <literal>entry.xhtml</literal>. Note that this is a little bit contrived, it would
+ have been easier to use pull-style MVC here as well. </para>
- <para>
- The <literal>entryAction</literal> component works much like an action class in a traditional
- push-MVC action-oriented framework like Struts:
- </para>
+ <para> The <literal>entryAction</literal> component works much like an action class in a traditional
+ push-MVC action-oriented framework like Struts: </para>
<example>
<programlisting><![CDATA[@Name("entryAction")
@@ -3080,9 +2751,7 @@
}]]></programlisting>
</example>
- <para>
- Page actions are also declared in <literal>pages.xml</literal>:
- </para>
+ <para> Page actions are also declared in <literal>pages.xml</literal>: </para>
<example>
<programlisting><![CDATA[<pages>
@@ -3099,19 +2768,15 @@
</pages>]]></programlisting>
</example>
- <para>
- Notice that the example is using page actions for some other functionality—the
- login challenge, and the pageview counter. Also notice the use of a parameter in
- the page action method binding. This is not a standard feature of JSF EL, but Seam
- lets you use it, not just for page actions, but also in JSF method bindings.
- </para>
-
- <para>
- When the <literal>entry.xhtml</literal> page is requested, Seam first binds the page parameter
- <literal>blogEntryId</literal> to the model, then runs the page action, which retrieves the
- needed data—the <literal>blogEntry</literal>—and places it in the Seam event context.
- Finally, the following is rendered:
- </para>
+ <para> Notice that the example is using page actions for some other functionality—the login
+ challenge, and the pageview counter. Also notice the use of a parameter in the page action method
+ binding. This is not a standard feature of JSF EL, but Seam lets you use it, not just for page actions,
+ but also in JSF method bindings. </para>
+
+ <para> When the <literal>entry.xhtml</literal> page is requested, Seam first binds the page parameter
+ <literal>blogEntryId</literal> to the model, then runs the page action, which retrieves the needed
+ data—the <literal>blogEntry</literal>—and places it in the Seam event context.
+ Finally, the following is rendered: </para>
<example>
<programlisting><![CDATA[<div class="blogEntry">
@@ -3122,17 +2787,16 @@
<p>
[Posted on 
<h:outputText value="#{blogEntry.date}">
- <f:convertDateTime timezone="#{blog.timeZone}" locale="#{blog.locale}" type="both"/>
+ <f:convertDateTime timezone="#{blog.timeZone}"
+ locale="#{blog.locale}" type="both"/>
</h:outputText>]
</p>
</div>]]></programlisting>
</example>
- <para>
- If the blog entry is not found in the database, the <literal>EntryNotFoundException</literal>
- exception is thrown. We want this exception to result in a 404 error, not a 505, so we
- annotate the exception class:
- </para>
+ <para> If the blog entry is not found in the database, the <literal>EntryNotFoundException</literal>
+ exception is thrown. We want this exception to result in a 404 error, not a 505, so we annotate the
+ exception class: </para>
<example>
<programlisting><![CDATA[@ApplicationException(rollback=true)
@@ -3146,9 +2810,7 @@
}]]></programlisting>
</example>
- <para>
- An alternative implementation of the example does not use the parameter in the method binding:
- </para>
+ <para> An alternative implementation of the example does not use the parameter in the method binding: </para>
<example>
<programlisting><![CDATA[@Name("entryAction")
@@ -3180,71 +2842,10 @@
</pages>]]></programlisting>
</example>
- <para>
- It is a matter of taste which implementation you prefer.
- </para>
+ <para> It is a matter of taste which implementation you prefer. </para>
</section>
</section>
- <section>
- <title>Running the Seam examples in JBoss using the JSF 1.2 RI</title>
- <para>
- JBoss AS 4.0 ships with the Apache MyFaces implementation of JSF 1.1. After many months of
- waiting, there is <emphasis>still</emphasis> no implementation of JSF 1.2 from MyFaces.
- For this and other reasons, JBoss AS 4.2 will embed the JSF 1.2 Reference Implementation
- by default. Soon after the release of 4.2, we will migrate the Seam examples to JSF 1.2.
- </para>
- <para>
- For those who can't wait, Seam is already compatible with JSF 1.2, and it's easy to get
- the Seam examples running with JSF 1.2 in JBoss 4.0.5. Let's start with the famous booking
- example:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- copy <literal>jsf-api.jar</literal>, <literal>jsf-impl.jar</literal>,
- <literal>el-api.jar</literal>, <literal>el-ri.jar</literal> to
- <literal>server/default/deploy/tomcat/jbossweb-tomcat55.sar/jsf-libs</literal>.
- </para>
- </listitem>
- <listitem>
- <para>
- delete <literal>myfaces-api.jar</literal> and <literal>myfaces-impl.jar</literal>
- from <literal>server/default/deploy/tomcat/jbossweb-tomcat55.sar/jsf-libs</literal>.
- </para>
- </listitem>
- <listitem>
- <para>
- edit <literal>server/default/deploy/tomcat/jbossweb-tomcat55.sar/conf/web.xml</literal>,
- replacing <literal>myfaces-impl.jar</literal> with <literal>jsf-impl.jar</literal>.
- </para>
- </listitem>
- <listitem>
- <para>
- edit <literal>examples/booking/resources/WEB-INF/web.xml</literal>, deleting the MyFaces
- listener, uncommenting the RI listener
- </para>
- </listitem>
- <listitem>
- <para>
- edit <literal>examples/booking/resources/WEB-INF/faces-config.xml</literal>, uncommenting
- the lines that install the <literal>SeamELResolver</literal> using the new JSF 1.2 XML
- schema declaration.
- </para>
- </listitem>
- <listitem>
- <para>
- edit <literal>examples/booking/resources/META-INF/application.xml</literal>, deleting the
- lines that declare <literal>el-api.jar</literal> and <literal>el-impl.jar</literal> as Java
- modules.
- </para>
- </listitem>
- </itemizedlist>
- <para>
- Restart JBoss, and type <literal>ant</literal> in the <literal>examples/booking</literal> directory.
- </para>
- </section>
-
</chapter>
More information about the jboss-cvs-commits
mailing list