Conversations
 
    
       
    
       The hotel booking "wizard" is implemented by a conversation-scoped
       stateful session bean. All Seam components are in the 
       conversation scope by default. 
       The HotelBookingAction 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.
    
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 next step, we will discuss Seam's built-in components to manage multiple concurrent conversations.
@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() {}
}
    
       The conversation begins when the @Begin annotated
       selectHotel() is called, and ends when 
       the @End annotated 
       confirm() or cancel() is called. Between the
       @Begin and @End 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 
       hotelBooking maintains its state throughout the process.
       When the @End method is called, Seam destroys this
       component and avoids any memory leak.
    
      However, none of the HotelBookingAction 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.