7

## Ending Conversations

The "Confirm" button is bound to the action method `confirm()` of `HotelBookingAction`.

`<h:commandButton value="Confirm" action="#{hotelBooking.confirm}" id="confirm"/>`

The `confirm()` method is tagged with the `@End` annotation, which ends the long-running conversation and results in all state associated with the conversation being destroyed at the end of the request. Since the `pages.xml` file specifies a redirect for this action, the state will not be destroyed until after the redirect completes. Note that even the success message that we create using the built-in `facesMessages` component is transparently propagated across the redirect!

`@End 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}"); log.info("New booking: #{booking.id} for #{user.username}"); events.raiseEvent("bookingConfirmed"); } @End public void cancel() {} @Destroy @Remove public void destroy() {} }`

When the conversation state is finally destroyed, Seam calls the `@Destroy` method, which results in removal of the SFSB.

Notice that the `HotelBookingAction.confirm()` method raises a `bookingConfirmed` event before it finishes. The event mechanism allows Seam components to communicate with each other without direct coupling. In this case, the `BookingListAction` component captures the `bookingConfirmed` event and refreshes the existing booking list for the current user.

`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(); } }`