Seam-Seamless integration of JSF, part 2nd: Talking with seam Building stateful CRUD applications with seam |
|
Level: Intermediate
Dan Allen (dan.allen@mojavelinux.com), Senior Java engineer, Coderyte, Inc.
June 04, 2007 it is easy to develop stateful CRUD applications with Seam. In the second installment of the Seamless JSF series, Dan Allen shows you how to create, read, update, and delete use cases using Java™server Faces (JSF) and Seam for the Web-based Golf Course directory. In the process, he highlighted Seam's two enhancements to the JSF lifecycle--The conversation scope and configuration through custom Java 5 annotations--and explained why it reduced server load and reduced development time.
Seam is described in the first installment of this three-part series, which is an application framework that can significantly enhance JSF functionality and implement a component-based architecture. In this article, I explain the differences between seam and other Web frameworks that are often used in conjunction with JSF, demonstrating how easy it is to add seam to an existing JSF application, and finally outlining seam's enhancements to the JSF application lifecycle, as well as stateful conversations, factory components to and use annotations for stealth configuration.
Although this article may have raised your interest in Seam, you may not be sure that it will improve the JSF development experience. Integrating a new set of tools is usually much more complicated than reading it, and sometimes it's not worth it. In the second article in the Seamless JSF series, you'll find out for yourself whether Seam can achieve its promise to simplify JSF development. After using seam to build a simple application that performs standard CRUD operations, I'm sure you'll think seam is a necessary extension of the JSF framework. As a result, Seam can also help reduce the load on the database layer's limited resources.
|
about this series Seamless JSF describes Seam as the first application framework for JSF, and the ability to fix major vulnerabilities that other extension frameworks cannot fix. Read the series and decide for yourself if Seam is a proper complement to JSF. |
|
Open 18 Applications
Open 18 is a web-based application that allows users to manage a list of golf lessons that have been experienced and to record scores for each session. To reflect the purpose of this discussion, the scope of the application is limited to managing the golf course catalog. The first screen shows the list of courses that have been entered and lists some relevant fields for each course, such as the course name, location, and the phone number of the store. From there, users can view the full course details, add new courses, edit existing courses, and eventually delete the course.
When I talk about how to use Seam to develop use cases for Open 18 applications, I focus on how it simplifies the code, automates the management of the State during a series of requests, and performs data model validation on the input data.
One of the goals of this series is to prove that Seam can be integrated into any existing JSF application and that it does not need to be converted to Enterprise JavaBeans (EJB) 3. Therefore, the Open 18 application does not rely on the JPA Entitymanager integration of Seam for transactional database access, nor does it depend on EBJ3 stateful session beans for state management. (Most of the examples that are included with Seam use both techniques.) Open 18 is designed to use a stateless hierarchical schema. The service layer and the data Access (DAO) layer are bound together using the Spring framework. I believe that the design is a viable option because of the ubiquity of Spring in the WEB application domain. The application shows how to introduce stateful behavior into JSF managed beans by using the conversation scope. Remember that these beans are simple POJO.
You can download the Open 18 source file and Maven 2 to compile and run the sample code. To get you started quickly, I've configured the application to use Seam and SPRING-JSF integration. If you want to set up Seam in your project, you can find the full how-to instructions in the first article in this series. See Resources for more information about integrating JSF and Spring.
Two story of a container
The first step in building a JSF application that leverages the spring framework is to configure JSF so that it can access the beans in the spring container. The Spring-web package is part of the Spring release and comes with a custom JSF variable parser to build this bridge. First, the Spring parser delegates to the local parser that came with the JSF implementation. The local parser tries to match a value binding reference (such as #{coursemanager}) to a managed bean in a JSF container. The bean name is made up of characters between the #{} expression delimiters, in this case CourseManager. If the lookup fails to find a match, the custom parser checks the Webapplicationcontext of spring to find a spring bean with a matching id attribute. Keep in mind that seam is an extension of the JSF framework, so any variables that JSF can access can also be accessed by seam.
The Spring variable parser is configured in the Faces-config.xml file using the variable parser node, as shown in Listing 1:
Listing 1. Configuring the spring Variable parser
<variable-resolver> Org.springframework.web.jsf.DelegatingVariableResolver </variable-resolver>
|
|
Back to the top of the page |
|
The context component of Seam
To reflect the purpose of this article, I assume that the service layer based on Spring is self-evident. In addition to the Jsf-spring integration layer-the layer is responsible for exposing the spring bean to JSF (and therefore exposing the bean to Seam) and not using spring in depth. The service layer object is treated as a stateless interface, and the CRUD operation can be delegated to the interface. Once you have solved these application details, you are free to focus on how Seam transforms managed beans into stateful components that define their role in facilitating user interaction with the application.
The open 18 application is developed by creating a support bean named Courseaction to support the view of managing the Golf course catalog. The managed bean exposes a collection of golf course objects and then responds to actions that manage those instances. The persistence of this data is delegated to the service layer based on Spring.
In a typical JSF application, use the managed Bean tool to register the Courseaction bean, and then inject the bean with its delegate object (or "dependencies"). To do this, you must open the Faces-config.xml file, and then add the new Managed-bean node with the name and class of the bean, as shown in Listing 2. Specify the dependencies to inject into the properties of the class by adding a child managed-property node that references the other managed bean by using a value-binding expression. In this example, the only dependency is the stateless service object CourseManager, which is implemented using the Genericmanager class from the Appfuse project (see Resources).
Listing 2. Courseaction defined as a JDF managed bean
<managed-bean> <managed-bean-name>courseAction</managed-bean-name> <managed-bean-class>com.ibm.dw.open18.CourseAction</managed-bean-class> <managed-property> <property-name>courseManager</property-name> <value>#{courseManager}</value> </managed-property> </managed-bean>
|
Annotations simplify the XML.
Now that you remember how troublesome it is to use the local JSF method to define managed beans, forget that you've seen the Managed-bean XML declaration-because you don't need it anymore. In the application that Seam builds, the bean is declared only using Java 5 annotations. Seam calls these beans context components. Although you may find the term esoteric, it simply describes a component (or named instance) that is related to a given scope (or context).
Seam manages the component during the lifetime of the scope that the context component is assigned to. Seam components are more like Spring beans than JSF managed beans because they are inserted into complex, aspect-oriented frameworks. On the functional side, Seam framework is much better than JSF's basic control inversion (IOC) container. Observe the declaration of courseaction in Listing 3. The Courseaction class was refactored to take advantage of Seam's annotations.
Listing 3-courseaction as defined by Seam components
@Name ("Courseaction") public class Courseaction { @In ("#{coursemanager}") Private Genericmanager<course, long> CourseManager; }
|
|
deep integration with Spring Since version 1.2, Seam has started to include spring's custom namespace handler, which allows the spring bean to be exposed as a Seam component. Adding <seam:component/> Tags to the Spring bean definition allows you to use @In annotations in the basic format (from listing 3) without explicitly specifying a value-binding expression. In this example, seam matches the name of the property to the seam component, and now the search includes a Spring bean that is exposed as a seam component. See Resources for more information about Seam 1.2 's new features. |
|
Note that all XML statements have been removed. In short, this is the beauty of Seam annotation. The @Name annotation of the class instructs the variable resolver of Seam to handle variable requests that match the name to the value in the annotation. Seam then instantiates an instance of the class, injects any dependencies assigned @In annotation, and exposes the instance with the variable name. Using Listing 3 As an example, Seam creates an instance of the Courseaction class, injects the CourseManager Spring bean into the CourseManager property, and returns the instance when a request to the variable courseaction is received. The extra benefit is that the bean's configuration is close to code, so it's more transparent to new developers who inherit the code base (even for those of you who have studied for only 6 months).
@In note tells Seam to inject the value of the binding expression #{coursemanager} into the property that defines it. After installing jsf-spring integration, the expression resolves to a bean named CourseManager defined in the Spring bean configuration.
|
Back to the top of the page |
|
Prepare a list of courses
Now that you are ready, you can continue to study the first use case. In the opening screen of the Open 18 application, the user is provided with a list of all the courses currently stored in the database. With the H:datatable component tag, the page definition in Listing 4 is fairly intuitive and does not allow any Seam-specific elements:
Listing 4. Initial course List View
No courses found. rendered= "#{courseaction.courses.rowcount GT 0}" > <!--column definitions go-->
|
Java code may be a little hard to understand. Listing 5 shows how to use local JSF to prepare a course collection in the scope-scoped support bean for the request. For simplicity, the injected Spring bean is removed.
Listing 5. Open Course as Datamodel
public class Courseaction { // ...
Private Datamodel Coursesmodel = new Listdatamodel ();
Public Datamodel getcourses () { SYSTEM.OUT.PRINTLN ("Retrieving courses ..."); Coursesmodel.setwrappeddata (Coursemanager.getall ()); return Coursesmodel; }
public void setcourses (Datamodel coursesmodel) { This.coursesmodel = Coursesmodel; } }
|
The Java code in Listing 5 looks pretty intuitive, doesn't it? The following is a study of the performance issues associated with JSF's use of support beans. When you provide a list of entities, you may use one of two methods. The first is to apply conditional logic to render h:datatable supported by a collection of at least one item, and the second is to display an informational message stating that no entity is found. To make a decision, you may need to consult #{courseaction.courses} and then call the associated getter method on the support bean.
If you load a page that has been developed as of today, and then view the final server log output, you will see:
Retrieving courses ... Retrieving courses ... Retrieving courses ...
|
So, brother. If you put this code into production, it's best to find a safe hiding point that DBAs can't find. This kind of code execution is a burden to the database. Worse, the situation deteriorates when the postback occurs, and additional redundant database calls may occur.
Let the database rest.
If you've ever used JSF to develop an application, you'll understand that it's inappropriate to blindly get data in the Getter method. Why. Because the getter method is called more than once in a typical JSF execution lifecycle. The workspace attempts to isolate the data retrieval process from the subsequent data access process through the delegate object. The goal is to avoid the computational cost of running a query each time the access function that supports the bean is consulted. The solution includes initializing the Datamodel (static block), or "Init" managed property in the constructor, caching the result in the bean's private property, using the HttpSession or scope as the support bean for the session, and relying on another layer of O/R caching mechanism.
Listing 6 shows an alternative: temporary caching of lookup results using the scope's private properties for the requested bean. You will find that this can at least eliminate redundant fetching at the page rendering stage, but it will still be discarded when the bean is outside the scope on subsequent pages.
listing 6. As a Datamodel public course, get only one
public class Courseaction { // ...
Private Datamodel Coursesmodel = null;
Public Datamodel getcourses () { if (Coursesmodel = = null) { SYSTEM.OUT.PRINTLN ("Retrieving courses ..."); Coursesmodel = new Listdatamodel (Coursemanager.getall ()); } return Coursesmodel; }
public void setcourses (Datamodel coursesmodel) { This.coursesmodel = Coursesmodel; } }
|
The method in Listing 6 is just one of the attempts to cut off data retrieval and data access. No matter what solution you develop, keeping data availability until you are no longer needed is the key to avoiding redundant data acquisition. Fortunately, this kind of context state management is what Seam is good at.
|
Back to the top of the page |
|
Context State Management
Seam initializes the non-component objects and collections using Factory mode. Once the data has been initialized, Seam can place the generated objects in an available scope, which can then be read over and over again without resorting to factory methods. This particular context is the conversation scope. The conversation scope provides a way to temporarily maintain state during a well-defined set of requests.
Until recently, there were very few WEB application architectures that provided any type of structure that could represent a conversation. None of the existing contexts provide an appropriate level of granularity for handling multiple request operations. You'll find that dialogs provide a way to prevent short-term storage loss, which is common in Web applications, and is the root cause of misuse of databases. Using Dialogs with factory component patterns makes it possible to consult the database when appropriate, rather than to regain data that the application could not track.
|
Double Shot Dual-shot is the extension of Seam's concept of dependency injection. In addition to receiving context variables to set the Component property value, the dual-shot allows the component property value to be rolled out to the target context, which is called outjecting. The difference between a double shot and a dependency injection is that it is dynamic, context-sensitive, and bidirectional. In other words, the dual-shot is dynamically consistent, i.e. import and export value operations when the component is invoked. Therefore, dual-shot is more suitable for stateful components, such as those used in WEB applications. |
|
Use dialogs to prevent storage loss
To accomplish a task, the application often has to instruct the user to browse a series of screens. This process usually requires a post to the server multiple times, either by the user submitting the form directly, or by an Ajax request. In either case, you should be able to track the application during the use case by maintaining the state of the server-side object. dialogs correspond to logical units of work. It allows you to create a separate context for individual users in a single browser window with a determined starting point and end point. The user's interaction with the application is maintained for the entire conversation.
Seam provides two types of dialogs: temporary conversations and long-running conversations. Temporary dialogs exist throughout the request process, including redirection. This feature addresses one of the challenges of the JSF development process, where redirects inadvertently discard information stored in Facescontext, such as the Facesmessage instance. Temporary dialogs are standard operating modes in Seam: You can get these patterns for free. This means that any value that is removed after the redirection is still available, without requiring you to perform additional work. This feature is a safety net that allows Seam to freely use redirection at any appropriate time.
In contrast, long-running dialogs can maintain variables in the scope during a series of well-defined requests. You can define a dialog boundary in a configuration file, use annotations to declare it, or use the Seam API to control it programmatically. Long-running conversations are a bit like small sessions, isolated in their own browser options performers (or Windows) that can be automatically purged at the end of the conversation or when the timeout occurs. In contrast to the corresponding session, one of the main points of the conversation scope is that the conversation scope will take place on the same application screen where the activity is separated from multiple browser tabs. Simply put, the use of dialogs eliminates the risk of concurrent conflicts. (See resources for a detailed discussion of how Seam isolates concurrent dialogs.) )
Seam Dialogue is a major improvement on the AD-HOC session management approach, which is improvised on site or encouraged by other frameworks. The introduction of the conversation scope also addresses the problem many developers point out that JSF uses objects to disrupt HttpSession and does not provide any automated garbage collection (GC) mechanisms. Dialogs allow you to create stateful components without using HttpSession. With the conversation scope, it is almost no longer necessary to use session scopes, and you can use them more freely.
|
Back to the top of the page |
|
To create an object by using Seam
Back to the course list example, at which point the refactoring code is used to take advantage of the factory pattern. The purpose is to allow Seam to manage the course collection so that it remains available during requests, including redirects. If you want seam to manage the collection, you must use the appropriate annotations to give seam the creation process.
Seam uses the build function to instantiate and assemble components. These build functions are declared by annotations in the Bean class. In fact, you've seen one of these examples: @Name comments. @Name note tells Seam to create a new instance using the default class constructor. To build your own list of courses, you do not want to use a component instance, but instead use a collection of objects. To do this, you want to use @Factory annotations. @Factory Note attaches a method to the creation of the extracted variable, which is specified in the value of the annotation and is used when no value is bound to the variable.
In Listing 7, the factory Method Findcourses () (located in the Courseaction Class) is used to initialize the value of the courses property, which is extracted into the view as Datamodel. The factory method instantiates the collection of course objects by delegating this work to the service layer.
Listing 7. Open Course using Datamodel annotations
@Name ("Courseaction") public class Courseaction { // ...
@DataModel Private list<course> courses;
@Factory ("courses") public void findcourses () { SYSTEM.OUT.PRINTLN ("Retrieving courses ..."); Courses = Coursemanager.getall (); } }
|
Please note that there is no getcourses () and Setcourses () method. With Seam, the data is extracted into the view using the name and value of a private property marked with @DataModel annotation. Therefore, property access functions are not required. In this scenario, @DataModel annotations perform two functions. First, it extracts or exposes the property so that the JSF variable parser can access it through the value-binding expression #{courses}. Second, it provides an alternative way of manually wrapping a list of courses in the Datamodel type (as shown in Listing 4). Instead, Seam automatically embeds a list of courses in the Datamodel instance so that it can be easily used with UIData components, such as h:datatable. Therefore, the support bean (courseaction) becomes a simple POJO. The framework then handles JSF-specific details.
Listing 8 shows the corresponding refactoring that occurred in the view. The only difference from Listing 5 is the value-binding expression. Using the extraction mechanism of Seam, the abbreviated value binding expression #{courses} is used instead of using #{courseaction.courses} to consult the support Bean's access method. The extracted variable is placed directly in the context of the variable and is not bound by its support bean.
Listing 8. Course list view using the extracted Datamodel
No courses found. rendered= "#{courses.rowcount GT 0}" > <!--column definitions goes here-->
|
Now that the page is accessed again, the following message appears only once in the console:
Using the factory build function and the temporary conversation scope can persist the data during the request and ensure that the variable courses is instantiated only once, regardless of how many times it has been accessed in the view.
Step-by-Step analysis to create a scenario
You might want to know when @Factory comments work. To prevent annotations from becoming too cryptic, we will step through the creation scenario just described. You can follow the sequence diagram in Figure 1:
Figure 1. Seam extraction Datamodel Using the factory method initialization