[jboss-cvs] jboss-seam/examples/jee5/view/exp ...
Peter Muir
peter at bleepbleep.org.uk
Sat Jun 23 14:34:01 EDT 2007
User: pmuir
Date: 07/06/23 14:34:01
Added: examples/jee5/view/exp loginExp.html
workspaceExp.html mainExp.html confirmExp.html
registerExp.html introExp.html bookingExp.html
Log:
JBSEAM-1449 - rename glassfish to jee5 and add support for OC4J (thanks to Richard Hoffman for this!)
Revision Changes Path
1.1 date: 2007/06/23 18:34:01; author: pmuir; state: Exp;jboss-seam/examples/jee5/view/exp/loginExp.html
Index: loginExp.html
===================================================================
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>In-depth Explanation</title>
<link href="../css/trailblazer_main.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="main">
<div class="trail">
<div class="numbox">2</div>
<h2>What happens when you login?</h2>
<img src="../img/header_line.gif" />
<form>
<input type="button" value="Close Window" onclick="window.close()"/>
</form>
<p>
The login page is defined using pure XHTML with JSF controls.
The form uses JSF EL value binding and method binding
expressions to refer to Seam components. For example,
<code>#{user.username}</code> refers to a property of the
<code>User</code> entity bean and <code>#{login.login}</code>
refers to a method of the <code>LoginAction</code> stateless
session bean. Both beans are EJB 3.0 POJOs managed by Seam.
</p>
<code class="block">
<div>
<h:outputLabel for="username">Login Name</h:outputLabel>
<h:inputText id="username" value="#{user.username}" />
</div>
<div>
<h:outputLabel for="password">Password</h:outputLabel>
<h:inputSecret id="password" value="#{user.password}" />
</div>
... ...
<div class="buttonBox">
<h:commandButton action="#{login.login}"
value="Account Login" class="button" />
</div>
</code>
<p>
The <code>User</code> enity bean is mapped to the Seam
context variable named <code>user</code> bean via the
<code>@Name</code> annotation. <code>User</code> is
a session scoped bean, meaning that the <code>user</code>
component value is retained for the entire session for
each user. You might also notice there are validation annotation
on the data properties. We will discuss those annotations in the
<a href="registerExp.html">next step</a>.
</p>
<code class="block">
@Entity
@Name("user")
@Scope(SESSION)
public class User implements Serializable {
private String username;
private String password;
private String name;
@NotNull
@Length(min=5, max=15)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Id
@Length(min=5, max=15)
public String getUsername () {
return username;
}
public void setUsername (String username) {
this.username = username;
}
// ... ...
}
</code>
<p>
<code>LoginAction</code> is an EJB 3.0 session bean mapped
to the Seam context variable named <code>login</code>. When
the login button is clicked, the JSF method binding
<code>#{login.login}</code> is evaluated, and the
<code>login()</code> method is invoked upon <code>LoginAction</code>.
</p>
<code class="block">
@Stateless
@Name("login")
public class LoginAction implements Login
{
@In @Out
private User user;
@PersistenceContext
private EntityManager em;
public String login()
{
List<User> results = em.createQuery("from User where username=:username and password=:password")
.setParameter("username", user.getUsername())
.setParameter("password", user.getPassword())
.getResultList();
if ( results.size()==0 )
{
FacesMessages.instance().add("Invalid login");
return "login";
}
else
{
user = results.get(0);
Contexts.getSessionContext().set("loggedIn", true);
return "main";
}
}
}
</code>
<p>
The <code>@In</code> annotation tells Seam to inject a <code>User</code>
when any method of <code>LoginAction</code> is invoked. The <code>@Out</code>
annotation indicates the <code>LoginAction</code> bean can change the
value of the <code>user</code> context variable and make the new instance
available to other session beans and JSF pages.
</p>
<form>
<input type="button" value="Close Window" onclick="window.close()"/>
</form>
</div>
</div>
</body>
</html>
1.1 date: 2007/06/23 18:34:01; author: pmuir; state: Exp;jboss-seam/examples/jee5/view/exp/workspaceExp.html
Index: workspaceExp.html
===================================================================
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>In-depth Explanation</title>
<link href="../css/trailblazer_main.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="main">
<div class="trail">
<div class="numbox">6</div>
<h2>The Workspace List</h2>
<img src="../img/header_line.gif" />
<p>
<center>
<form>
<INPUT type="button" value="Close Window" onclick="window.close()">
</form>
</center>
</p>
<p>
The workspace list allows the user to switch between multiple
conversations from the same browser tab. There is no Java code
to write, just the following JSF code to include:
</p>
<code class="block">
<h:dataTable value="#{conversationList}" var="entry">
<h:column>
<h:commandLink action="#{entry.select}" value="#{entry.description}"/>
<h:outputText value="[current]" rendered="#{entry.current}"/>
</h:column>
<h:column>
<h:outputText value="#{entry.startDatetime}">
<f:convertDateTime type="time" pattern="hh:mm"/>
</h:outputText>
-
<h:outputText value="#{entry.lastDatetime}">
<f:convertDateTime type="time" pattern="hh:mm"/>
</h:outputText>
</h:column>
</h:dataTable>
</code>
<p>
We do need to specify what text will be displayed by <code>#{entry.description}</code>.
We do this by creating a <code>pages.xml</code> file in our <code>WEB-INF</code>
directory.
</p>
<code class="block">
<pages>
<page view-id="/hotel.xhtml">View hotel: #{hotel.name}</page>
<page view-id="/book.xhtml">Book hotel: #{hotel.name}</page>
<page view-id="/confirm.xhtml">Confirm: #{booking.description}</page>
</pages>
</code>
<p>
Note that you don't need the <code>pages.xml</code> file if you don't want the
workspace list in your application.
</p>
<p>
<form>
<input type="button" value="Close Window" onclick="window.close()"/>
</form>
</p>
</div>
</div>
</body>
</html>
1.1 date: 2007/06/23 18:34:01; author: pmuir; state: Exp;jboss-seam/examples/jee5/view/exp/mainExp.html
Index: mainExp.html
===================================================================
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>In-depth Explanation</title>
<link href="../css/trailblazer_main.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="main">
<div class="trail">
<div class="numbox">4</div>
<h2>Stateful components</h2>
<img src="../img/header_line.gif" />
<form>
<input type="button" value="Close Window" onclick="window.close()"/>
</form>
<p>
The hotel search page is backed by the a stateful session bean named
<code>hotelSearch</code> and implemented by the class
<code>HotelSearchingAction</code>.
</p>
<code class="block">
<h:inputText value="#{hotelSearch.searchString}" />
<h:commandButton value="Find Hotels"
action="#{hotelBooking.find}"
styleClass="button" />
<h:outputLabel for="pageSize">Maximum results:</h:outputLabel>
<h:selectOneMenu value="#{hotelSearch.pageSize}" id="pageSize">
<f:selectItem itemLabel="5" itemValue="5"/>
<f:selectItem itemLabel="10" itemValue="10"/>
<f:selectItem itemLabel="20" itemValue="20"/>
</h:selectOneMenu>
</code>
<p>
When the button is clicked, the form is submitted and JSF sets the value
of the text box and drop down menu onto the <code>searchString</code> and
<code>pageSize</code> attributes of <code>HotelSearchingAction</code>
before calling the <code>find()</code> action listener method. We've used a
session-scope stateful bean because we want it's state (the search results) to
be held in the session between requests to the server.
</p>
<code class="block">
@Stateful
@Name("hotelSearch")
@Scope(ScopeType.SESSION)
@LoggedIn
public class HotelSearchingAction implements HotelSearching
{
@PersistenceContext
private EntityManager em;
private String searchString;
private int pageSize = 10;
@DataModel
private List<Hotel> hotels;
@DataModelSelection
private Hotel selectedHotel;
public String find()
{
String searchPattern = searchString==null ? "%" : '%' + searchString.toLowerCase().replace('*', '%') + '%';
hotels = em.createQuery("from Hotel where lower(name) like :search or lower(city) like :search or lower(zip) like :search or lower(address) like :search")
.setParameter("search", searchPattern)
.setMaxResults(pageSize)
.getResultList();
return "main";
}
public Hotel getSelectedHotel()
{
return selectedHotel;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public String getSearchString()
{
return searchString;
}
public void setSearchString(String searchString)
{
this.searchString = searchString;
}
@Destroy @Remove
public void destroy() {}
}</code>
<p>
The <code>find()</code> method retrieves a list of hotels from the database and
initializes the <code>hotels</code> field. The <code>hotels</code> field is marked
with the <code>@DataModel</code> annotation, so when the <code>find()</code> method
returns, Seam outjects an instance of <code>ListDataModel</code> to a context
variable named <code>hotels</code>. So, when the search page is re-rendered, the
result list is available to the JSF <code>dataTable</code>.
Each row of the data table has an associated command button or link
(see below). When the user
clicks on the button / link, the <code>Hotel</code> represented by that
row is injected into the <code>hotel</code> field annotated by the
<code>@DataModelSelection</code> annotation. Other application components
can then access the selected hotel via the
<code>HotelSearching.selectedHotel</code> property.
</p>
<code class="block">
<h:outputText value="No Hotels Found" rendered="#{hotels != null and hotels.rowCount==0}"/>
<h:dataTable value="#{hotels}" var="hot" rendered="#{hotels.rowCount>0}">
<h:column>
<f:facet name="header">Name</f:facet>
#{hot.name}
</h:column>
<h:column>
<f:facet name="header">Address</f:facet>
#{hot.address}
</h:column>
<h:column>
<f:facet name="header">City, State</f:facet>
#{hot.city}, #{hot.state}
</h:column>
<h:column>
<f:facet name="header">Zip</f:facet>
#{hot.zip}
</h:column>
<h:column>
<f:facet name="header">Action</f:facet>
<s:link value="View Hotel" action="#{hotelBooking.selectHotel}"/>
</h:column>
</h:dataTable>
</code>
<p>
The "View Hotel" link is the above mentioned command link associated
with each row of the data table. It is implemented
using a Seam <code><s:Link></code>, which is part of Seam's
extension of JSF controls.
This JSF control let's us call an action, and pass a request parameter, without
submitting any JSF form. The advantage of <code><s:link></code> is that,
unlike a standard JSF <code><h:commandLink></code>, there is no JavaScript
used, so "open link in new tab" works seamlessly.
</p>
<p>
When this link is clicked, the <code>selectHotel()</code> method of the
<code>HotelBookingAction</code> bean is called. It gets the selected
hotel from the <code>HotelSearching.selectedHotel</code> property,
merges it to the current persistence context (in case the same
hotel has been accessed before in the same session),
and starts a Seam conversation. We will discuss Seam conversations
in the next step.
</p>
<code class="block">
@Stateful
@Name("hotelBooking")
@LoggedIn
public class HotelBookingAction implements HotelBooking
{
... ...
@Begin
public String selectHotel()
{
hotel = em.merge( hotelSearch.getSelectedHotel() );
return "hotel";
}
}
</code>
<form>
<input type="button" value="Close Window" onclick="window.close()"/>
</form>
</div>
</div>
</body>
</html>
1.1 date: 2007/06/23 18:34:01; author: pmuir; state: Exp;jboss-seam/examples/jee5/view/exp/confirmExp.html
Index: confirmExp.html
===================================================================
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>In-depth Explanation</title>
<link href="../css/trailblazer_main.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="main">
<div class="trail">
<div class="numbox">7</div>
<h2>Ending Conversations</h2>
<img src="../img/header_line.gif" />
<form>
<input type="button" value="Close Window" onclick="window.close()"/>
</form>
<p>
The "Confirm" button is bound to the action method <code>confirm()</code>
of <code>HotelBookingAction</code>.
</p>
<code class="block">
<h:commandButton value="Confirm"
action="#{hotelBooking.confirm}"
class="button"/>
</code>
<p>
The <code>confirm()</code> method is tagged with the <code>@End</code> annotation,
which ends the long-running conversation and results in all state associated with
the conversation being destroyed at the end of the request. Actually, since the
<code>confirmed</code> outcome triggers a redirect, the state will not be destroyed
until <em>after the redirect completes</em>. Note that even the success message
that we create using the built-in <code>facesMessages</code> component is transparently
propagated across the redirect!
</p>
<code class="block">
@End
public String 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}");
log.info("New booking: #{booking.id} for #{user.username}");
events.raiseEvent("bookingConfirmed");
return "confirmed";
}
@End
public String cancel()
{
return "main";
}
@Destroy @Remove
public void destroy() {}
}
</code>
<p>
When the conversation state is finally destroyed, Seam calls the <code>@Destroy</code> method,
which results in removal of the SFSB.
</p>
<p>
Notice that the <code>HotelBookingAction.confirm()</code> method
raises a <code>bookingConfirmed</code> event before it finishes. The
event mechanism allows Seam components to communicate with each other
without direct coupling. In this case, the <code>BookingListAction</code>
component captures the <code>bookingConfirmed</code> event and refreshes
the existing booking list for the current user.
</p>
<code class="block">
public class BookingListAction implements BookingList, Serializable
{
... ...
@Factory
@Observer("bookingConfirmed")
public void getBookings()
{
bookings = em.createQuery("from Booking b where b.user.username = :username order by b.checkinDate")
.setParameter("username", user.getUsername())
.getResultList();
}
}
</code>
<form>
<input type="button" value="Close Window" onclick="window.close()"/>
</form>
</div>
</div>
</body>
</html>
1.1 date: 2007/06/23 18:34:01; author: pmuir; state: Exp;jboss-seam/examples/jee5/view/exp/registerExp.html
Index: registerExp.html
===================================================================
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>In-depth Explanation</title>
<link href="../css/trailblazer_main.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="main">
<div class="trail">
<div class="numbox">3</div>
<h2>What happens in the registration process?</h2>
<img src="img/header_line.gif" />
<form>
<input type="button" value="Close Window" onclick="window.close()"/>
</form>
<p>
The register.xhtml JSF form captures the user input for new account
registration. It validates the user input, binds the data to a
Seam data component, and invokes a Seam UI event handler method
when the form is submitted.
</p>
<code class="block">
<s:validateAll>
<div class="entry">
<div class="label">
<h:outputLabel for="username">Username:</h:outputLabel>
</div>
<div class="input">
<h:inputText id="username" value="#{user.username}"/><br/>
<span class="errors"><h:message for="username" /></span>
</div>
</div>
<div class="entry">
<div class="label">
<h:outputLabel for="name">Real Name:</h:outputLabel>
</div>
<div class="input">
<h:inputText id="name" value="#{user.name}" /><br/>
<span class="errors"><h:message for="name" /></span>
</div>
</div>
</s:validateAll>
... ...
<div class="input">
<h:commandButton value="Register"
action="#{register.register}"
class="button"/>
<h:commandButton value="Cancel" action="login" class="button"/>
</div>
</code>
<p>
The form fields are bound to properties of a Seam component named
<code>user</code> via JSF EL value binding expressions such as
<code>#{user.username}</code>. The form submit button is bound
to the <code>register()</code> method of the Seam component named
<code>register</code> using the JSF method binding expression
<code>#{register.register}</code>.
</p>
<p>
Notice that the input fields are enclosed by a <code><s:validateAll</code>
tag. This tag is part of Seam's extension to JSF. It tells the Seam
runtime to validate those input fields when the form is submitted.
The validation conditions are specified on the entity bean classes
those input fields map to (e.g., here the validation condition is
on the <code>User</code> class, see later).
This JSF form also includes <code><h:message></code> tags that
will display the results of any JSF validation failures.
</p>
<p>
The <code>User</code> class is an EJB 3.0 entity bean with a
<code>@Name</code> annotation that binds the bean instance to a
context variable named <code>user</code>. In addition to the standard
EJB 3.0 O/R mapping metadata, this bean features several Hibernate
Validator annotations such as
<code>@NotNull</code>, <code>@Length</code>. Due to the
<code><s:validateAll</code> tag in the form, these constraints are
automatically validated by Seam when the form is submitted. If the user
enters invalid data in the JSF form, the form will be redisplayed with
error messages.
</p>
<code class="block">
@Entity
@Name("user")
@Scope(SESSION)
public class User implements Serializable
{
private String username;
private String password;
private String name;
public User(String name, String password, String username)
{
this.name = name;
this.password = password;
this.username = username;
}
public User() {}
@NotNull
@Length(max=100)
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
@NotNull
@Length(min=5, max=15)
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
@Id
@Length(min=5, max=15)
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
}</code>
<p>
<code>RegisterAction</code> is an EJB 3.0 stateful session bean
bound to the Seam context variable named <code>register</code>.
The <code>register()</code> method is invoked when the form is
submitted.
</p>
<code class="block">
@Stateful
@Scope(EVENT)
@Name("register")
public class RegisterAction implements Register
{
@In
private User user;
@PersistenceContext
private EntityManager em;
@In(create=true)
private transient FacesMessages facesMessages;
private String verify;
public String register()
{
if ( user.getPassword().equals(verify) )
{
List existing = em.createQuery("select username from User where username=:username")
.setParameter("username", user.getUsername())
.getResultList();
if (existing.size()==0)
{
em.persist(user);
return "login";
}
else
{
facesMessages.add("username #{user.username} already exists");
return null;
}
}
else
{
facesMessages.add("re-enter your password");
verify=null;
return null;
}
}
public String getVerify()
{
return verify;
}
public void setVerify(String verify)
{
this.verify = verify;
}
@Destroy @Remove
public void destroy() {}
}</code>
<p>
The <code>@In</code> annotations inject Seam components into the <code>RegisterAction</code>
bean. The <code>user</code> component is our entity bean, of course. The <code>facesMessages</code>
component is a built-in Seam component that makes it very easy to display templated and localized
messages to the user, even when redirect after post is used.
</p>
<form>
<input type="button" value="Close Window" onclick="window.close()"/>
</form>
</div>
</div>
</body>
</html>
1.1 date: 2007/06/23 18:34:01; author: pmuir; state: Exp;jboss-seam/examples/jee5/view/exp/introExp.html
Index: introExp.html
===================================================================
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>In-depth Explanation</title>
<link href="../css/trailblazer_main.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="main">
<div class="trail">
<div class="numbox">1</div>
<h2>What is JBoss Seam</h2>
<img src="../img/header_line.gif" />
<p>
<center>
<form>
<INPUT type="button" value="Close Window" onclick="window.close()">
</form>
</center>
</p>
<p>
EJB 3.0 and JSF are perhaps the most exciting new developments in the Java EE 5 platform.
Seam is an innovative new application framework that integrates the EJB 3.0 component model
with JSF as a presentation tier. Seam builds upon the standard extension points provided by
both specifications and provides a set of Java annotations that extends the standard
annotations defined by the EJB specification. In addition, Seam introduces several innovative
new ideas: managed conversations, declarative and contextual application state management,
bijection and integrated business process management.
</p>
<p>
Traditional web applications are incredibly vulnerable to bugs and performance problems
relating to state management. Developers are forced to handle issues like back button
navigation, multi-window browsing, session size management in an utterly ad-hoc fashion.
Access to transactional components from the web tier has also been problematic. J2EE
provided no way to have simultaneous access to state held in contexts associated with
the web request, and state held in transactional resources accessed via EJB. Finally,
J2EE had no first-class construct for representing state associated with a long-running
business process. Seam tackles all these problems, and provides a uniform model for
stateful components in Java EE 5.
</p>
<p>
Like all JBoss software, this entire software stack is free. The full source code of this
sample application is available in the <code>examples/booking</code> directory of the
<a href="http://www.jboss.com/products/list/downloads#seam">Seam distribution</a>.
<!--
There is even a
<a target="_blank" href="http://docs.jboss.com/TrailBlazer/seam-booking/Seam.htm">
10-minute flash demo
</a>
showing how to build a Seam web application from ground up.
-->
</p>
<p>
<center>
<form>
<INPUT type="button" value="Close Window" onclick="window.close()">
</form>
</center>
</p>
</div>
</div>
</body>
</html>
1.1 date: 2007/06/23 18:34:01; author: pmuir; state: Exp;jboss-seam/examples/jee5/view/exp/bookingExp.html
Index: bookingExp.html
===================================================================
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>In-depth Explanation</title>
<link href="../css/trailblazer_main.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="main">
<div class="trail">
<div class="numbox">5</div>
<h2>Conversations</h2>
<img src="../img/header_line.gif" />
<form>
<input type="button" value="Close Window" onclick="window.close()"/>
</form>
<p>
The hotel booking "wizard" is implemented by a conversation-scoped
stateful session bean. All Seam components are in the
conversation scope by default.
The <code>HotelBookingAction</code> maintains
state associated with the booking process in the Seam conversation
scope. This ensures that if the user is working in multiple brower
tabs or multiple brower windows, the various conversations are
completely isolated from each other.
</p>
<p>
To see this working in practice, right click on the "View Hotel" button
in the search screen and select "open in new tab" or "open in new window",
and try working on multiple hotel bookings simultaneously.
In the <a href="workspaceExp.html">next step</a>, we will discuss
Seam's built-in components to manage multiple concurrent conversations.
</p>
<code class="block">
@Stateful
@Name("hotelBooking")
@LoggedIn
public class HotelBookingAction implements HotelBooking
{
@PersistenceContext(type=EXTENDED)
private EntityManager em;
@In
private User user;
@In(required=false) @Out
private Hotel hotel;
@In(required=false)
@Out(required=false)
private Booking booking;
@In(create=true)
private FacesMessages facesMessages;
@In(create=true)
private Events events;
@In
private HotelSearching hotelSearch;
@Logger
private Log log;
@Begin
public String selectHotel()
{
hotel = em.merge( hotelSearch.getSelectedHotel() );
return "hotel";
}
public String 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()
{
if (booking==null || hotel==null) return "main";
if ( !booking.getCheckinDate().before( booking.getCheckoutDate() ) )
{
facesMessages.add("Check out date must be later than check in date");
return null;
}
else
{
return "confirm";
}
}
@End
public String 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}");
log.info("New booking: #{booking.id} for #{user.username}");
events.raiseEvent("bookingConfirmed");
return "confirmed";
}
@End
public String cancel()
{
return "main";
}
@Destroy @Remove
public void destroy() {}
}</code>
<p>
The conversation begins when the <code>@Begin</code> annotated
<code>selectHotel()</code> is called, and ends when
the <code>@End</code> annotated
<code>confirm()</code> or <code>cancel()</code> is called. Between the
<code>@Begin</code> and <code>@End</code> methods, the user can do
any number of things with the application (i.e., invoke any
event handler method or use the BACK button etc.) and the
<code>hotelBooking</code> maintains its state throughout the process.
When the <code>@End</code> method is called, Seam destroys this
component and avoids any memory leak.
</p>
<p>
However, none of the <code>HotelBookingAction</code> bean methods
may be called outside of a long-running conversation.
So if we try to use the
back button after the end of the conversation, Seam will redirect to the main page, with an
error message.
</p>
<form>
<input type="button" value="Close Window" onclick="window.close()"/>
</form>
</div>
</div>
</body>
</html>
More information about the jboss-cvs-commits
mailing list