Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 92 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
92
Dung lượng
2,05 MB
Nội dung
750 CHAPTER 17 Introducing JBoss Seam of backing bean instances. The available scopes for a backing bean (where it lives) are the current HTTP request context, the current HTTP session context, and the global application context. You write application logic by creating beans and letting JSF manage their lifecycle in one of these contexts. You can bind model values from a backing bean to a visual component with an expression language. For example, you create a page with a text input field and bind it to a named backing bean field or getter/setter method pair. This backing bean name is then mapped in JSF configuration to an actual backing bean class, along with a declaration of how an instance of that class should be handled by JSF (in the request, in the HTTP session, or in the application context). The JSF engine automatically keeps the backing bean field (or property) synchronized with the state of the widget as seen (or manipulated) by the user. JSF is an event-driven presentation framework. If you click a button, a JSF ActionEvent is fired and passed to registered listeners. A listener for an action event is again a backing bean you name in your JSF configuration. The backing bean can then react to this event—for example, by saving the current value of a backing bean field (which is bound to a text input widget) into the database. This is a simplified explanation of what JSF does. Internally, each request from the web browser passes through several phases of processing. A typical request-processing sequence on the server, when you click a button on a JSF page, is as follows (this process is illustrated in figure 17.7): 1 Restore View of all widgets (JSF can store the widget state on the server or on the client). 2 Apply Request Parameters to update the state of widgets. 3 Process Validations that are necessary to validate user input. 4 Update Model Values that back the widget by calling the bound fields and setter methods of a backing bean. 5 Invoke Application, and pass the action event to listeners. 6 Render Response page the user sees. Obviously a request can take different routes; for example, Render Response may occur after Process Validations, if a validation fails. A nice illustration of the JSF lifecycle and the processing phases can be found in the already mentioned Sun Java EE 5 tutorial in chapter 9, “The Life Cycle of a JavaServer Faces Page.” We’ll also get back to the JSF processing model later in this chapter. The Java EE 5.0 programming model 751 Which response is rendered and what page is shown to the user depends on the defined navigation rules and what the outcome of an action event is. Outcomes in JSF are simple strings, like “success” or “failure.” These strings are produced by your backing beans and then mapped in a JSF XML configuration file to pages. This is also called free navigation flow; for example, you can click the Back button in your browser or jump directly to a page by entering its URL. JSF, combined with Facelets, is a great solution if you’re looking for a web framework. On the other hand, the backing beans of your web application—the components that implement the application logic—usually need to access transac- tional resources (databases, most of the time). This is where EJB 3.0 comes into the picture. 17.1.2 Considering EJB 3.0 EJB 3.0 is a Java EE 5.0 standard that defines a programming model for transac- tional components. For you, as a web application developer, the following features of EJB 3.0 are most interesting: ■ EJB 3.0 defines a component programming model that is primarily based on annotations on plain Java classes. ■ EJB 3.0 defines stateless, stateful, and message-driven components, and how the runtime environment manages the lifecycle of component instances. ■ EJB 3.0 defines how components are wired together, how you can obtain ref- erences to components, and how components can call each other. ■ EJB 3.0 defines how crosscutting concerns are handled, such as transactions and security. You can also write custom interceptors and wrap them around your components. ■ EJB 3.0 standardizes Java Persistence and how you can access an SQL data- base with automatic and transparent object/relational mapping. If you want to access an SQL database, you create your domain model entity classes (such as Item , User , Category ) and map them with annotations from the Java Persistence specification to a database schema. The EJB 3.0 persistence manager API, the EntityManager , is now your gateway for database operations. You execute database operations in EJB 3.0 components—for example, stateful or stateless session beans. These beans are plain Java classes, which you enable as EJBs with a few annotations. You then get the container’s services, such as automatic dependency injection (you get the EntityManager when you need it) and declarative transaction demarcation on component methods. Stateful session 752 CHAPTER 17 Introducing JBoss Seam beans help you to keep state for a particular client, for example, if a user has to go through several pages in a conversation with the application. Can you use EJB 3.0 components and entities as backing beans for JSF actions and widgets? Can you bind a JSF text field widget to a field in your Item entity class? Can a JSF button-click be directly routed to a session bean method? Let’s try this with an example. 17.1.3 Writing a web application with JSF and EJB 3.0 The web application you’ll create is simple; it has a search screen where users can enter an identifier for a particular item, and a detail screen that appears when the item is found in the database. On this detail screen, users can edit the item’s data and save the changes to the database. (We don’t think you should necessarily code this application while reading the examples; later, we make significant improvements by introducing Seam. That’s the time to start coding.) Start with the data model for the entity: an Item . Creating the entity class and mapping The Item entity class comes from CaveatEmptor. It’s also already annotated and mapped to the SQL database (listing 17.1). package auction.model; import ; @Entity @Table(name = "ITEM") public class Item implements Serializable { @Id @GeneratedValue @Column(name = "ITEM_ID") private Long id = null; @Column(name = "ITEM_NAME", length = 255, nullable = false, updatable = false) private String name; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name="SELLER_ID", nullable = false, updatable = false) private User seller; @Column(name = "DESCRIPTION", length = 4000, nullable = false) private String description; @Column( name="INITIAL_PRICE", nullable = false) Listing 17.1 An annotated and mapped entity class The Java EE 5.0 programming model 753 private BigDecimal initialPrice; Item() {} // Getter and setter methods } This is a simplified version of the CaveatEmptor Item entity, without any collections. Next is the search page that allows users to search for item objects. Writing the search page with Facelets and JSF The search page of the application is a page written with Facelets as the templat- ing engine, and it’s valid XML. JSF widgets are embedded in that page to create the search form with its input fields and buttons (listing 17.2). <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <head> <title>CaveatEmptor - Search items</title> <link href="screen.css" rel="stylesheet" type="text/css"/> </head> <body> <ui:include src="header.xhtml"/> <h:form> <span class="errors"><h:message for="itemSearchField"/></span> <div class="entry"> <div class="label">Enter item identifier:</div> <div class="input"> <h:inputText id="itemSearchField" size="3" required="true" value="#{itemEditor.itemId}"> <f:validateLongRange minimum="0"/> </h:inputText> </div> </div> <div class="entry"> Listing 17.2 The search.xhtml page in XHTML with Facelets B C D E F G H I J 754 CHAPTER 17 Introducing JBoss Seam <div class="label"> </div> <div class="input"> <h:commandButton value="Search" styleClass="button" action="#{itemEditor.doSearch}"/> </div> </div> </h:form> </body> </html> Every valid XHTML file needs the right document type declaration. In addition to the regular XHTML namespace, you import the Facelets and two JSF namespaces for visual HTML components and core JSF components (for exam- ple, for input validation). The page layout is handled with cascading stylesheets ( CSS) externalized to a sep- arate file. A common page header template is imported with <ui:import> from Facelets. A JSF form (note the h namespace) is an HTML form that, if submitted, is pro- cessed by the JSF servlet. JSF can output messages, such as validation errors. Each <div> is a label or a form field, styled with the CSS class label or input . The JSF input text component that renders an HTML input field. The identifier is useful to bind it to error-message output, the size defines the visual size of the input field, and user input is required when this form is submitted. The most interesting part is the value binding of the input field to a backing bean (named itemEditor ) and a getter/setter method pair (named getItemId() / set- ItemId() ) on that backing bean. This is the data model this input field is bound to, and JSF synchronizes changes automatically. JSF also supports input validation and comes with a range of built-in valida- tors. Here you declare that user input can’t be negative (item identifiers are positive integers). The submit button of the form has an action binding to the method doSearch() of the backing bean named itemEditor . What happens after the action executes depends on the outcome of that method. This is how the page looks rendered in the browser (figure 17.1). 1) B C D E F G H I J 1) The Java EE 5.0 programming model 755 If you look at the URL, you see that the page has been called with the suffix .jsf; you probably expected to see search.xhtml. The .jsf suffix is a servlet mapping; the JSF servlet runs whenever you call a URL that ends in .jsf, and after installation of Facelets, you configured it in web.xml to use .xhtml internally. In other words, the search.xhtml page is rendered by the JSF servlet. If you click the Search button without entering a search value, an error message is shown on the page. This also happens if you try to enter a noninteger or nonpositive integer value, and it’s all handled by JSF automatically. If you enter a valid item identifier value, and the backing bean finds the item in the database, you’re forwarded to the item-editing screen. (Let’s finish the user interface before focusing on the application logic in the backing bean.) Writing the edit page The edit page shows the details of the item that has been found in the search and allows the user to edit these details. When the user decides to save his changes, and after all validation is successful, the application shows the search page again. The source code for the edit page is shown in listing 17.3. <!DOCTYPE html PUBLIC <html xmlns= <head> <body> <h2>Editing item: #{itemEditor.itemId}</h2> <h:form> <span class="errors"><h:messages/></span> Listing 17.3 The edit.xhtml page with a detail form Figure 17.1 The search page with JSF widgets B 756 CHAPTER 17 Introducing JBoss Seam <div class="entry"> <div class="label">Name:</div> <div class="input"> <h:inputText required="true" size="25" value="#{itemEditor.itemName}"> <f:validateLength minimum="5" maximum="255"/> </h:inputText> </div> </div> <div class="entry"> <div class="label">Description:</div> <div class="input"> <h:inputTextarea cols="40" rows="4" required="true" value="#{itemEditor.itemDescription}"> <f:validateLength minimum="10" maximum="4000"/> </h:inputTextarea> </div> </div> <div class="entry"> <div class="label">Initial price (USD):</div> <div class="input"> <h:inputText size="6" required="true" value="#{itemEditor.itemInitialPrice}" > <f:converter converterId="javax.faces.BigDecimal"/> </h:inputText> </div> </div> <div class="entry"> <div class="label"> </div> <div class="input"> <h:commandButton value="Save" styleClass="button" action="#{itemEditor.doSave}"/> </div> </div> </h:form> </body> </html> You can place a value-binding expression outside of any component. In this case, the getItemId() method on the itemEditor backing bean is called, and the return value ends up in the HTML page. Again, a value binding is used to bind the input text field to a getter/setter method pair (or field) in the backing bean. C D B C The Java EE 5.0 programming model 757 This action binding references the doSave() method in the itemEditor backing bean. Depending on the outcome of that method, either the page is displayed again (with error messages) or the user is forwarded to the search page. Figure 17.2 shows the rendered page. Why is the URL showing search.jsf? Shouldn’t it be edit.jsf? Consider the request processing of the JSF servlet. If the user clicks the Search button on the search.jsf page, the backing bean’s doSearch() method runs after input validation. If the outcome of that method triggers a forward to the edit.xhtml page, this document is rendered by the JSF servlet, and the HTML is sent to the browser. The URL doesn’t change! Users can’t bookmark the edit page, which in this simple application is desirable. Now that you’ve completed the top layer of the application, the view, consider the layer that accesses the database (you might call this the business layer). Because accessing an SQL database is a transactional operation, you write an EJB. Accessing the database in an EJB If you’ve worked with EJB 2.x (and Struts) before, the code that accesses the data- base is most likely procedural code in a stateless session bean. Let’s do that in EJB 3.0 (listing 17.4). D Figure 17.2 The edit page with loaded item details 758 CHAPTER 17 Introducing JBoss Seam package auction.beans; import ; @Stateless @TransactionAttribute(TransactionAttributeType.REQUIRED) public class EditItemBean implements EditItem { @PersistenceContext EntityManager em; public Item findById(Long itemId) { return em.find(Item.class, itemId); } public Item save(Item item) { return em.merge(item); } } A @Stateless annotation turns this plain Java class into a stateless session bean. At runtime, a pool of instances is prepared, and each client that requests a session bean gets an instance from the pool to execute a method. All methods that are called on this session bean are wrapped in a system transac- tion, which enlists all transactional resources that may be used during that proce- dure. This is also the default if you don’t annotate the bean. A session bean needs an interface. Usually you implement this interface directly. The EditItem interface has two methods. When the runtime container hands out a session bean instance from the pool, it injects an EntityManager with a (fresh) persistence context scoped to the transaction. If a client calls findById() , a system transaction starts. The EntityManager opera- tion executes an SQL query in that transaction; the persistence context is flushed and closed when the transaction commits (when the method returns). The returned Item entity instance is in detached state. If a client calls save() , a system transaction starts. The given detached instance is merged into a (new) persistence context. Any changes made on the detached Item instance are flushed and committed to the database. A new handle to the now up-to-date Item instance is returned. This new Item instance is again in detached state when the method returns, and the persistence context is closed. Listing 17.4 A stateless session bean with a data access facade B C D E F G B C D E F G The Java EE 5.0 programming model 759 You can call the session bean shown in listing 17.4 a data access object (DAO). It can also be a session facade. The application isn’t complex enough to make a clear distinction; if more nondata access methods were added to its interface, the session bean would represent part of the business layer interface with a traditional (mostly procedural) session facade. A piece is still missing from the puzzle: The JSF input widgets and buttons have value and action bindings to a backing bean. Is the backing bean the same as the session bean, or do you have to write another class? Connecting the layers with a backing bean Without Seam, you have to write a backing bean that connects your JSF widget state and actions to the transactional stateless session bean. This backing bean has the getter and setter methods that are referenced with expressions in the pages. It can also talk to the session bean and execute transactional operations. The code for the backing bean is shown in listing 17.5. package auction.backingbeans; import public class ItemEditor { private Long itemId; private Item item; public Long getItemId() { return itemId; } public void setItemId(Long itemId) { this.itemId = itemId; } public String getItemName() { return item.getName(); } public void setItemName(String itemName) { this.item.setName( itemName ); } public String getItemDescription() { return item.getDescription(); } public void setItemDescription(String itemDescription) { this.item.setDescription( itemDescription ); } Listing 17.5 A JSF backing bean component connects the layers. B C D E [...]... integrate with an EJB 3.0 container Because Seam is now responsible for wiring component instances at runtime, it Improving the application with Seam 767 needs to know how to obtain EJBs through lookup The JNDI pattern shown here is for JBoss application server (Seam runs on any Java EE 5.0 server and even with and without EJB 3.0 in regular Tomcat We think it’s most convenient if you start with JBoss... traditional Java web application—for example, you can detach an Item instance from the session bean facade and transfer it into the JSF backing bean With EJB 2.x entity beans, you needed a data transfer object (DTO) to do this The code is much more compact With EJB 2.x, the session bean must implement the SessionBean interface with all its maintenance methods In EJB 3.0, this is resolved with a simple... search.xhtml and edit.xhtml These are the methods used by JSF to synchronize the backing beans internal model state with the state of UI widgets The Java EE 5.0 programming model 761 if the runtime environment supports the Java Servlet 2.5 specification (At the time of writing, Tomcat 5.5 implements only Java Servlets 2.4, and Tomcat 6 is in alpha stage.) The backing bean is a component that is managed by the... layer (if Facelets isn’t what you want) without much effort EJB 3.0 is much easier to tame than EJB 2.x, and it also has features (such as interceptors and dependency injection) that have never before been available in a standardized Java programming model The application can easily be tested with a testing framework like JUnit or TestNG All classes are plain Java classes; you can instantiate them,... you’ve spent a lot of time with EJB 2.x and Struts, you’ll probably say, ”This is great: I don’t have to manage the HTTP session myself anymore, and all the EJB boilerplate code is gone.” You’re right either way Java EE 5.0 and especially JSF and EJB 3.0 are a significant step forward for Java web applications, but not everything is perfect We’ll now look at the advantages of Java EE 5.0 and compare it... implementation in EditItemBean .java is a POJO class with a few extra annotations In listing 17.9, all Seam annotations are shown in bold Improving the application with Seam 769 Listing 17.9 The implementation of a stateful component package auction.beans; import B @Name("itemEditor") @Scope(ScopeType.CONVERSATION) C D @Stateful public class EditItemBean implements EditItem { @PersistenceContext EntityManager... method The EJB 3.0 container injects an EntityManager with a fresh persistence context into this bean, before a method is called by any client of the bean The persistence context is closed when the method returns (assuming this method call is also the scope of a system transaction, which is the default here) Why don’t you mark the doSave() method with @End, @Destroy, and @Remove? The doSave() method... This is a JSF page called login.xhtml, written with Facelets (listing 17.12) 780 CHAPTER 17 Introducing JBoss Seam Figure 17.8 The login screen of CaveatEmptor Listing 17.12 The login.xhtml page source code ... application with Seam The web application you’ve written to search and edit web items can be improved if you add Seam into the mix You start with basic Seam features: ■ Seam makes the JSF backing bean unnecessary You can bind JSF widget values and actions directly to EJB stateful and stateless session beans Seam introduces a unified component model: All your classes can be turned into Seam components with. .. multiple browser windows with no extra effort This is a short list of what Seam can do; there is much more that you’ll put to use later Let’s first create a basic conversational, stateful, simple Seam application Your first step is Seam setup and configuration If you want to follow the examples with code, download the CaveatEmptor package for Seam from http://caveatemptor .hibernate. org, and open it . (such as Item , User , Category ) and map them with annotations from the Java Persistence specification to a database schema. The EJB 3.0 persistence manager API, the EntityManager , is. application with Seam 767 needs to know how to obtain EJBs through lookup. The JNDI pattern shown here is for JBoss application server. (Seam runs on any Java EE 5.0 server and even with and without. already mentioned Sun Java EE 5 tutorial in chapter 9, “The Life Cycle of a JavaServer Faces Page.” We’ll also get back to the JSF processing model later in this chapter. The Java EE 5.0 programming