Login | Register   
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Discover Seam and Sew Up Your Java Projects Faster than Ever : Page 3

In the tradition of Spring, JBoss offers Seam, which uses a declarative state model, extensive use of annotations, and two-way dependency injection to make automation of huge portions of your complex Java EE apps not just possible, but downright sensible.


advertisement
Comparing the Two Implementation Approaches
To demonstrate the amount of coding saved in the Seam experiment, it's useful to take a comparative look at portions of the application.

Example No. 1
The original application did a lot of work involving the maintenance of state for the HttpSession. It involved a wrapper around HttpSession that added state and associated with an event. When an event occurred the wrapper would clean HttpSession, removing all events at that level and lower. With Seam, none of that needs to be written or maintained.

To wit, the following code from the original application uses SessionManager (the HttpSession wrapper) to get XDelegate out of httpSession. If it's not there or is not the right type I create a new instance and put it into httpSession. The EventLevel is Screen so when moving between screens this will be removed from http session automatically.



XDelegate delegate = getDelegate(request); … protected XDelegate getDelegate(HttpServletRequest request) { Object delegate = SessionManager.getAttribute(request, DELEGATE_KEY); if ((delegate == null) || (!(delegate instanceof XDelegate))) { delegate = new XDelegate(); SessionManager.setAttribute(request, DELEGATE_KEY, delegate, EventLevel.Screen); } return (XDelegate) delegate; }

In Seam, the code looks like:

@In(value="xDelegate", create=true) @Out XDelegate delegate; … @Name("xDelegate") @Scope(PAGE) public class XDelegate implements AbstractDelegate{ … }

The @In injects XDelegate into the object. If it doesn't exist, (create=true) creates the object for me and @Scope associates the instance at the page level. So that when I go to a new page the old delegate will get cleaned up and a new one will be generated when needed. I don't have to write any code over and over to confirm receipt of a value or to check if the value is of the right instance. I don't have to write any code to manage the interface to session manager or debug it or make sure the objects are being cleaned. In the Seam example, I just annotate the objects I want managed and injected as well as the instances where I want that injection or out-jection to occur.

Example No. 2
With a team of more than 60 developers, the original application frequently suffered from a lack of cohesive methods. ReturnResults objects are used frequently in the application to record activity results in a central location. And these objects return all over the place to collect and hand back the work done on the data. The Map, SessionKey, and security objects created similarly messy issues. These three objects are passed around in all my methods between layers and way too many methods in between. While some methods in the chain of method calls will need these objects, most don't. Still I need to pass them around because the next layer might need them. This clutters up my method signature with irrelevant objects. I can now inject these objects exactly where I need them and no where else. I use @Name to identify which objects I want to be a part of my declarative managed state. I use @Scope to tell Seam when to create new ones and when to throw them away. The same is true for ReturnResults objects; I simply inject the ReturnResult object anywhere I want it and outject any changes I want.

Managing Conversation State
I would be remiss if I did not mention that you can begin a conversation with the @Begin annotation and end a conversation with @End annotation. That's where the ability to code in a conversation paradigm begins to happen. Take a look at the requirements for my system in Table 2.

Table 2. These system requirements will be used to design the application's business logic.

Actor System
1. The user indicates the creation of a reservation. 2. The system responds with a reservation Create interface.
3. The user searches for a customer. 4. List’s Customer’s matching criteria
5. The user identifies Customer 6. The system associates customer information with new reservation
7. The user Searches for pickup location 8. The system lists locations matching criteria
9. The user identifies pickup location and pickup time 10. The system associates pickup location and time with the reservation.
11 The user searches for drop off location 12. The system lists locations matching criteria
13. The user identifies the drop off location and time 14. The system associates the drop off location and time with the reservation.
15. The user searches for car class 16. The system lists car class matching criteria
17 The user identifies car class 18. The system associates Car Class with Reservation and calculates estimated charges.

Here you see a series of interactions to create an item of business value to the stakeholders of the system. But how does that map into code? In the original application, the Stateful Session bean has a significant amount of that logic mapped into its interface:

public CustomerListTR retrieveCustomers() public ReservationTR addCustomer(ICustomerVO cust) public AllLocationsTR retrieveAllLocations() public ReservationTR assignPickUp(ILocationVO loc, Date dat) public ReservationTR assignDropOff(ILocationVO loc, Date dat)

Behind those methods is a significant amount of code that gets the component and asks it to do something, manages state, and more. In the original application most of the interactions with external systems involve an asynchronous method call. For performance reasons I needed to keep the response of the asynchronous call around over several user-initiated interactions with the system. I have tried storing that state in http session, stateful session beans, and entity beans but none of these solutions are conversationally oriented. They require me to write code to put the object in my state management system and to take it out once I'm done. And any user who exits the conversation in a way that isn't explicitly handled, could create troublesome bugs.

In the Seam app I'm injecting objects as they are needed and I'm starting and ending conversations as needed. This allows me to focus my objects on doing the business work not on managing conversation state in a state management model that doesn't support conversational state. Now my code begins to look like my requirements. Very @powerful!

@In(value="reservationFinder", required = false) private transient ReservationFinder reservationFinder; @Begin(join = true) @IfInvalid(outcome = Outcome.REDISPLAY) public String create() { @IfInvalid(outcome = Outcome.REDISPLAY) public String update() { @End(ifOutcome = "find") public String delete() { @End(ifOutcome = "find") public String done() { @In(create = true) private transient CustomerEditor customerEditor; public String customer() { @Begin(join = true) public String selectCustomer() { @In(create = true) private transient CarclassEditor carclassEditor; public String carclass() { @Begin(join = true) public String selectCarclass() { @In(value="pickUpLocationEditor",create = true) private transient LocationEditor pickUpLocationEditor; public String pickUpLocation() { @Begin(join = true) public String selectPickUpLocation() { @In(value="dropOffLocationEditor",create = true) private transient LocationEditor dropOffLocationEditor; public String dropOffLocation() { @Begin(join = true) public String selectDropOffLocation() {

The Front End
Finally, I'd like to look at the front end of my application to see how Seam performed there. The AppCoord layer (see Figure 3) of the original application communicates with several different types of clients including Struts, Swing, SOAP, and JMS. To make that burden easier, data specific to a given request is moved from a value object (VO) to a transfer object (TO). I provide TOs to hide modifications to VOs and provide an abstraction layer to decouple my VOs from the clients of the AppCoord layer. It also means that I only exchange the data needed to perform the selected operations.

The TOs wrap all the primitive types along with String and Date so that I can associate messages, problems, and decoration indicators with each field. The wrappers allow me to associate this information at the class level. Creating the code to move this information from the ReturnResult and VOs into the TO and wrappers takes a long time.

With Struts, for example, you have to build the Form object, the Action object, and the JSP itself. To get the data ready to be displayed for my Struts action I move a set of data out of one or more VOs into a TO. I also move error messages and field decorator indicators into the TOs and wrappers. I hand the TO to a struts action which then moves all that information into the Struts-based form object. All that data movement is code that must be written, debugged, and maintained.

All that code to move all that data is gone in Seam (see Table 3). Seam provides extensions to JSF that also remove the need for Struts actions. The beauty of the Seam framework is that I could add the TO concept if I wanted. If I were sharing data in a JMS- or SOAP-based message I probably wouldn't supply VOs, but the power of the integration with Seam components and JSF is too powerful to ignore.

Table 3. Comparative Line Counts with Struts vs. Seam.

Class for Struts (to list customers) Line Count
customerList.jsp 41
CustomersAction.java 65
CustomerForm.java 181
CustomersForm.java 61
CustomerListTR.java 49
Total 397
Class for Seam( to retrieve, list, and select customers) Line Count
findCustomer.jsp 193
Total 193



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap