4

## Stateful components

The hotel search page is backed by the a stateful session bean named `hotelSearch` and implemented by the class `HotelSearchingAction`.

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

When the button is clicked, the form is submitted and JSF sets the value of the text box and drop down menu onto the `searchString` and `pageSize` attributes of `HotelSearchingAction` before calling the `find()` 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.

`@Stateful @Name("hotelSearch") @Scope(ScopeType.SESSION) @LoggedIn public class HotelSearchingAction implements HotelSearching { @PersistenceContext private EntityManager em; private String searchString; private int pageSize = 10; private int page = 0; @DataModel private List<Hotel> hotels; @DataModelSelection private Hotel selectedHotel; public String find() { hotels = em.createQuery("select h from Hotel h where lower(h.name) " + "like #{pattern} or lower(h.city) like #{pattern} " + "or lower(h.zip) like #{pattern} or " + "lower(h.address) like #{pattern}") .setMaxResults(pageSize) .setFirstResult( page * pageSize ) .getResultList(); } public boolean isNextPageAvailable() { return hotels!=null && hotels.size()==pageSize; } public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } public String getSearchString() { return searchString; } public void setSearchString(String searchString) { this.searchString = searchString; } @Remove public void destroy() {} }`

The `find()` method retrieves a list of hotels from the database and initializes the `hotels` field. The `hotels` field is marked with the `@DataModel` annotation, so when the `find()` method returns, Seam outjects an instance of `ListDataModel` to a context variable named `hotels`. So, when the search page is re-rendered, the result list is available to the JSF `dataTable`. Each row of the data table has an associated command button or link (see below).

`<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(hot)}"/> </h:column> </h:dataTable>`

The "View Hotel" link is the above mentioned command link associated with each row of the data table. It is implemented using a Seam `<s:link>`, 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 `<s:link>` is that, unlike a standard JSF `<h:commandLink>`, there is no JavaScript used, so "open link in new tab" works seamlessly.

When this link is clicked, the `selectHotel()` method of the `HotelBookingAction` bean is called with the `hot` parameter that is specified in the query. The parameter values are evaluated at invocation time, not when the link is generated, so the `<s:link>` tag adds a dataModelSelection parameter that indicates the value of the `hot` loop variable for the given row.

The `selectHotel()` method merges the selected hotel into 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.

`@Stateful @Name("hotelBooking") @LoggedIn public class HotelBookingAction implements HotelBooking { ... ... @Begin public void selectHotel(Hotel selectedHotel) { hotel = em.merge(selectedHotel); } }`