Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 49 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
49
Dung lượng
0,99 MB
Nội dung
rules and other elements in a JSF configuration file, see Chapter 4, “JSF Configuration.” We’ll talk about how you bind an action to a user interface component shortly when we visit the JSF View. Before we do that, let’s take a look at the JSF Model. Model When thinking in terms of existing Web frameworks like Struts, it may be a lit- tle confusing at first to see a JSF component’s data referred to as the Model. This distinction becomes more difficult in JSF because we now have compo- nents similar to what we find in Swing. In reality, what is called a Model in JSF is often similar to a Struts ActionForm that stores and marshals data between the View and Model layers of a Web application. This is a very important dis- tinction. Whether or not you decide to use business objects directly to back your user interface components, you must resist the temptation to let JSF com- ponent data objects influence your real Model (business objects) and vice versa. Failing to do so often results in high coupling between the View and Model layers of the Model-2 architecture. There are some (even among the authors of this book) who prefer to use business objects to negate the need for a separate set of objects just for the user interface components. In fact, there have been several heated debates in the Struts community alone regarding this preference. As long as you understand the tendency for business object models to be corrupted by such a direct con- nection (and the separation of Model and View to become blurred), you may certainly do as you wish. Just be aware of the consequences. That being said, when thinking strictly in terms of UI components, dubbing a component’s backing data the Model makes more sense. This is especially true when coming from Swing. So the whole debate over whether or not a component’s data is the Model or not is really just based on your point of ref- erence. Just make sure that you are aware of this distinction so you can avoid some of the pitfalls that are associated with it. With respect to JSF, Figure 1.5 provided a good example of what a user inter- face component model object looks like. You’ll notice that this object is not part of the business object model that is interfaced with in the action handler. You could choose to use the User object (if it wasn’t an EJB) directly, but we chose not to do that here. Since this object is just your typical JavaBean, there isn’t much more to discuss about it in particular. However, we still need to figure out how we bind this object with our user interface components. The first thing we’ll do is declare this JavaBean as a managed bean in our JSF configuration file. An example of how you would do this is provided in List- ing 1.6. JSF Patterns and Architecture 23 05_462071 Ch01.qxd 5/18/04 8:36 AM Page 23 <managed-bean> <managed-bean-name>login</managed-bean-name> <managed-bean-class>logindemo.LoginBean</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property> <property-name>userId</property-name> <null-value/> </managed-property> <managed-property> <property-name>password</property-name> <null-value/> </managed-property> </managed-bean> Listing 1.6 Declaring a Model object as a managed bean. When using managed beans, you must ensure that each property you declare exists in the declared class following JavaBean naming conventions. Once declared in this manner, each managed bean and its declared properties can be referenced and bound to user interface components. Your chosen JSF implementation will also handle the task of creating a LoginBean instance when one of its properties is invoked by a user interface component. You’ll notice that we declaratively initialized each property to null. In addition to creating an instance of our LoginBean, the JSF implementation will also ini- tialize each property as we have instructed. As you can see, managed beans are very convenient from a management perspective. We’ll talk more about them later in Chapter 4, “JSF Configuration,” and elsewhere where appropriate. For now, let’s take a look at how you create a JSP View in JSF and bind its compo- nents to the Model object. View As you have probably figured out by now, the View layer in JSF consists pri- marily of the component tree. One benefit of JSF is that individual components or the whole component tree can be rendered differently to support multiple client user interface types. In most cases, you will be dealing with a markup lan- guage such as HTML that is used in a JSP. Depending on the client device type, components could render themselves in the markup language appropriate for that device. We will take a detailed look at JSF component rendering later in Chapter 2, “Elements of JSF,” and in Chapter 10, “Custom JSF Components.” Another benefit of JSF is that components bring a more event-driven style to Java Web applications. User interaction with a component (clicking a check box) may result in events being fired that ultimately result in changes to the appearance of that component or others in the component tree. We’ll explore events in more detail later in this chapter. 24 Chapter 1 05_462071 Ch01.qxd 5/18/04 8:36 AM Page 24 For now, let’s take a look at what our login interface might look like in JSF. A JSP example of our JSF login user interface is provided in Listing 1.7. <!doctype html public “-//w3c//dtd html 4.0 transitional//en”> <html> <head> <meta http-equiv=”Content-Type” content=”text/html; charset=iso-8859-1”> <title>Login</title> <%@ taglib uri=”http://java.sun.com/jsf/core/” prefix=”f” %> <%@ taglib uri=”http://java.sun.com/jsf/html” prefix=”h” %> </head> <body> Log in with your User ID and Password.<br/><br/> <f:view> <h:form> <h:outputLabel for=”userId”> <h:outputText value=”User ID”/> </h:outputLabel> <h:inputText id=”userId” value=”#{login.userId}”/><br/> <h:outputLabel for=”password”> <h:outputText value=”Password”/> </h:outputLabel> <h:inputSecret id=”password” value=”#{login.password}”/><br/> <h:commandButton action=”#{login.login}” value=”Login”/> </h:form> </f:view> </body> </html> Listing 1.7 JSF login form using a JSP page. The first thing we do in our JSP is to declare two of the standard JSF tag libraries. All JSF tags used within a JSP page must be declared within the core <view> tag. Inside this tag, we make use of the standard JSF HTML tag library (see Chapter 5, “JSP Integration in JSF,” for more information on this and other JSF tag libraries) to construct our login form. Our User ID and Password properties are both represented here, one via a text input field using the <inputText> tag and the other via a text field using the <inputSecret> tag that hides what the user enters. Both of these input JSF Patterns and Architecture 25 05_462071 Ch01.qxd 5/18/04 8:36 AM Page 25 fields are associated via their id attributes with matching labels that provide instructions for users. You’ll notice that each input field has a value attribute. This attribute allows you to bind the input fields to properties on an object. In this case, we are binding the input fields to properties on our LoginBean JavaBean. Since this class has been declared as a managed bean, an instance of LoginBean will be created automatically (if it doesn’t already exist), initialized, and placed into Request scope when the JSP page is evaluated. The key to this binding is the first part of the value expression. The text login must match the name given in the <managed-bean-name> tag used when declaring the Login- Bean as a managed bean in the JSF configuration file (refer back to Listing 1.6 for this). This simple mechanism for binding user interface components to the managed bean allows you to capture user input and validate it with business objects via the login() action handler. Furthermore, the simple navigation rules specified earlier will ensure that the user is directed to the next appropri- ate page only after the login is successful. Our chosen JSF implementation takes care of all the dirty work in between. You could have made use of Converters, Validators, and even custom Ren- derers as part of the user interface. In fact, a Converter and Validator compo- nent would be useful even in this simple example. These components are very useful for delegating and reusing common user interface tasks. In the interest of simplicity, we decided not to use these special JSF components in our little login example. We’ll introduce them in Chapter 2, “Elements of JSF,” and then examine them in detail in Chapter 8, “Validation and Conversion.” Compo- nents for rendering will be covered in Chapter 10, “Custom JSF Components.” For now, just be aware that they exist. Composite Components In most modern component architectures, components are typically combined to form user interfaces. The primary component is usually a form, frame, or page. Within these root components, other components are arranged hierar- chically to achieve the desired interface. This composable nature of compo- nents supports the creation of rich user interfaces. The Composite (Gamma, Helm, Johnson, and Vlissides 1996) Pattern describes this feature of compo- nents and is important in understanding most MVC-based architectures. You have probably already seen the composable nature of user interface components in each of the three frameworks we have been discussing. What we’ll do here is provide a useful summary of the Composite Pattern and then 26 Chapter 1 05_462071 Ch01.qxd 5/18/04 8:36 AM Page 26 describe how each framework implements the pattern. This exercise will give us important insight into the design of JSF user interface components. Composite Pattern We begin by describing the Composite Pattern in general terms as it relates to user interfaces. The intent of this pattern is to form whole-part component hierarchies of arbitrary complexity, while at the same time treating individual components and compositions of components with a uniform interface. Let’s first take a look at the motivation behind this pattern. Motivation User interfaces are commonly constructed with two basic types of compo- nents: container components and primitive components. A container compo- nent may have any number of components nested within it, while a primitive component may not. Using these two simple component types, developers can build larger components that then may be composed into even larger, more complex components. Such component hierarchies allow for the creation of those rich user interfaces we spoke of earlier. When dealing with complex component hierarchies, having to distinguish between container components and primitive components is undesirable. You would routinely be forced to determine the type of component you were deal- ing with before performing operations common to all components. This adds still more complexity to the equation. The Composite Pattern allows us to treat these component types identically through a common interface. The Composite Pattern is appropriate if the following statements are true: ■■ We are representing “part-whole hierarchies” (Gamma, Helm, Johnson, and Vlissides 1996). ■■ We wish to treat composite components and individual components the same throughout a component hierarchy. This allows uniform treat- ment of all components, regardless of type. Now let’s take a look at how the Composite Pattern resolves these issues. Solution The structure of the Composite Pattern is provided in Figure 1.7. JSF Patterns and Architecture 27 05_462071 Ch01.qxd 5/18/04 8:36 AM Page 27 Figure 1.7 Composite Pattern structure. There are a number of participants in this Pattern. We will first provide a summary of each participant’s responsibilities and then explain how they col- laborate to provide the uniform interface we are seeking. AbstractComponent. This participant defines an interface common to all components. It implements this common interface to provide default behavior, a portion of which is used for accessing and managing nested components. SimpleComponent. This participant represents and defines the behavior of primitive components. CompositeComponent. This participant represents and defines the behavior of container components. It provides a mechanism for storing and managing nested components by implementing those portions of the AbstractComponent interface. Client. This participant accesses and manipulates component hierarchies through the AbstractComponent interface. When a Client performs an operation on a component, a uniform response is provided regardless of the underlying structure. If the component is a Sim- pleComponent, the operation is performed directly. Otherwise, we have a CompositeComponent that asks each nested component to perform the The Client invokes an operation(). Client AbstractComponent +add() +remove() +getChild() +operation() CompositeComponent +add() +remove() +getChild() +operation() PrimitiveComponent +operation() +children 28 Chapter 1 05_462071 Ch01.qxd 5/18/04 8:36 AM Page 28 requested operation. The CompositeComponent may perform additional operations before responding to the Client. In either case, these interactions are hidden beneath a common interface. There are a few well known consequences to using the Composite Pattern— not all of them good. ■■ As stated in the intent, this Pattern provides component hierarchies of arbitrary complexity, while at the same time treating individual compo- nents and compositions of components with a uniform interface (Open/Closed Principle). NOTE The Open/Closed Principle is a software design principle that requires components to be open for extension but closed for modification. Composite components satisfy this principle nicely since they allow you to extend and aggregate existing components without modification. ■■ Client code is much simpler because it uses a uniform interface to manipulate both composite and primitive components (Liskov Substi- tution Principle [Liskov 1987]). NOTE The Liskov Substitution Principle is a software design principle that allows the instance of a component to function as an instance of its super- component. This is accomplished by extending a new component directly or indirectly from AbstractComponent. ■■ Adding new component types is very straightforward and requires no change to existing client code. ■■ Nothing in the composite structure distinguishes one component type from another—they are all treated the same. While this provides a great deal of simplicity and flexibility, it can result in overly generic designs. ■■ When calling a method such as add( ) on a primitive component, an error may result if the component does not support this operation. The Client may then need to account for this possibility, which results in a violation of the Liskov Substitution Principle. In other words, our overly generic interface may require the Client to have knowledge of what that specific component is capable of instead of being able to rely on an appropriate implementation of all the generic interface’s methods. Swing Components and Containers Swing components form a containment hierarchy that consists of what is called a top-level container and any number of child components. A top-level JSF Patterns and Architecture 29 05_462071 Ch01.qxd 5/18/04 8:36 AM Page 29 container may be a frame (JFrame), dialog (JDialog), or applet (JApplet). These top-level containers essentially provide a window within which compo- nents can paint themselves. A simplified view of the Swing Composite structure is provided in Figure 1.8. All components descend from the Component class, which provides basic component properties and services. This includes event handling, painting, and positioning. The Container class adds the ability for a component to contain others. This includes managing a list of child components, ordering them, and setting layout managers. The Container class in Swing is what provides the uniform Composite interface we discussed in the previous sec- tion (referred to as AbstractComponent). Figure 1.8 Swing Composite structure. Top-level components JFrame and JDialog descend from this component. Window All other components descend from this component. Some of them (like JPanel) may contain other components, while others (like JLabel) may not. Container +add(component : Component) +remove(component : Component) +getComponent(id : int) +paint(g : Graphics) Component +paint(g : Graphics) JComponent +paint(g : Graphics) +children +parent 0 * 30 Chapter 1 05_462071 Ch01.qxd 5/18/04 8:36 AM Page 30 All components that are not top-level containers descend from the JCompo- nent class, which is a direct subclass of Container. JComponent adds access to the application-wide pluggable look and feel, comprehensive key- stroke handling, tool tips, accessibility support, custom component properties, and more painting functionality (most notably double buffering and support for borders). Let’s return to our simple login example to see what a Swing composite component structure would look like. The containment hierarchy for our login form is provided in Figure 1.9. You’ll notice that the LoginFrame contains something called a content pane. Each top-level container (in this case a JFrame) has this intermediate container. Instead of adding components to the frame itself, you add them to the frame’s content pane. If we consider the content pane part of the frame, then this simple login form is a composite component with a hierarchy of child components four levels deep. Figure 1.9 Swing login containment hierarchy. Password : JPasswordFieldPasswordLabel : JLabelUserIDLabel : JLabel UserID : JTextField FieldPanel : JPanelLabelPanel : JPanelOkButton : JButton ButtonPanel : JPanel DataPanel : JPanel LoginPanel : JPanel LoginForm : JFrame ContentPane JSF Patterns and Architecture 31 05_462071 Ch01.qxd 5/18/04 8:36 AM Page 31 Let’s explore this composite a bit more with its source code representation in Listing 1.8. The source code only deals with creating components and laying them out. We have conveniently left out event handling for now because that topic will be covered later in this chapter. class LoginForm extends JFrame { LoginForm() { super(“Login”); Container contents = getContentPane();; contents.add(getLoginPanel(), BorderLayout.CENTER); contents.add(getButtonsPanel(), BorderLayout.SOUTH); pack(); setVisible(true); } JPanel getLoginPanel() { JPanel panel = new JPanel(new BorderLayout()); JPanel labelPanel = new JPanel(new GridLayout(2,0)); JLabel userIdLabel = new JLabel(“User ID:”); labelPanel.add(userIdLabel); JLabel passwordLabel = new JLabel(“Password:”); labelPanel.add(passwordLabel); panel.add(labelPanel, BorderLayout.WEST); JPanel fieldPanel = new JPanel(new GridLayout(2,0)); JTextField userIdField = new JTextField(); fieldPanel.add(userIdField); JTextField passwordField = new JTextField(); fieldPanel.add(passwordField); panel.add(fieldPanel, BorderLayout.CENTER); return panel; } JPanel getButtonPanel() { JPanel panel = new JPanel(new FlowLayout()); JButton okButton = new JButton(“OK”); panel.add(okButton); return panel; } } Listing 1.8 Swing login form source code. 32 Chapter 1 05_462071 Ch01.qxd 5/18/04 8:36 AM Page 32 [...]... Listing 2. 2 Facets in tabbed pane JSP In this example, a facet called label is used to identify the... (PaneComponent) kid; UIComponent facet = (UIComponent)pane.getFacet(“label”); if (facet != null) { if (pane.isRendered() && (selectedClass != null)) { facet.getAttributes().put(“paneTabLabelClass”, selectedClass); } else if (!pane.isRendered() && (unselectedClass != null)) { facet.getAttributes().put(“paneTabLabelClass”, unselectedClass); } facet.encodeBegin(context); } } } Listing 2. 3 Facets in tabbed pane... throughout the same application and a host of others 49 50 Chapter 2 This opens up the possibility of additional libraries of components that cover these common supporting tasks So what makes a user interface component? Every user interface component in JSF implements the javax.faces.component.UIComponent interface This comprehensive interface defines methods for navigating the component tree, interacting... UIOutput Figure 2. 2 Component tree Facets The methods listed above allow you to manipulate and navigate composite components through a generic interface as described by the Composite Pattern JSF provides an additional facility for defining the roles of subordinate components that may be independent of or orthogonal to the parent-child relationship These roles are called facets To understand how facets may... identify the label of each tab in the tabbed component You can already see that the facet is being used here to logically identify a specific component that appears in each tab To see how the facet is actually used, take a look at an excerpt from the TabbedRenderer class in Listing 2. 3 57 58 Chapter 2 public void encodeEnd(FacesContext context, UIComponent component) throws IOException { // Render the... is provided in Listing 2. 2 Elements of JSF Powered by Faces components: . chapter. 24 Chapter 1 05_4 620 71 Ch01.qxd 5/18/04 8:36 AM Page 24 For now, let’s take a look at what our login interface might look like in JSF. A JSP example of our JSF login user interface is. would do this is provided in List- ing 1.6. JSF Patterns and Architecture 23 05_4 620 71 Ch01.qxd 5/18/04 8:36 AM Page 23 <managed-bean> <managed-bean-name>login</managed-bean-name> <managed-bean-class>logindemo.LoginBean</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property> <property-name>userId</property-name> <null-value/> </managed-property> <managed-property> <property-name>password</property-name> <null-value/> </managed-property> </managed-bean> Listing. hides what the user enters. Both of these input JSF Patterns and Architecture 25 05_4 620 71 Ch01.qxd 5/18/04 8:36 AM Page 25 fields are associated via their id attributes with matching labels that