This section describes the J2EE team’s experience developing the RRD implementation of ITS.
8.2.1 Architecture Summary 8.2.1.1 RRD Applications
The J2EE team divided the ITS system up into three RRD applications:
• The Customer Service application, including the Customer Service Web interface and message production.
• The Work Order console application, which includes message consumption and the Web service used by the Customer Service application. When RRD builds this application, the Web service is packaged as a separate EAR file and must be installed separately.
• The Work Order web application, which stands by itself.
8.2.1.2 Database Access
On the back end, the team chose to forego stored procedures and stick with explicit SQL through JDBC. The fact that RRD would generate JDBC logic automatically weighed against writing stored procedures. The team knew from prior experience that, for single database actions, a prepared statement invoked via JDBC performs at least as well as a stored procedure. So they expected that RRD’s generated logic would suffice for basic CRUD operations (which covered most cases).
There were cases, however, where they needed to customize that logic. For example:
• Work ticket searches. Both the Work Order Web application and the Web service used by the Customer Service Web application allow ticket queries based on various
combinations of criteria. For example, the Work Order Web application allows queries by any combination of customer ID, ticket creation date, work type, ticket status and technician assignment. The developers had to write code that determined which search criteria were used and constructed a custom SQL statement that uses only those criteria.
• Customer search. The Work Order Web application has a customer query function based on partial match of customer name. For every customer found it returns the number of tickets in each of three ticket status categories (created, in progress, completed). By default, RRD’s generated code would have separately counted tickets in each category for each customer, in other words three additional SQL actions per row of customer data returned. Developer B reduced that number to one action per customer by using a custom SQL statement with a GROUP BY clause4 to get all three counts in one action.
Later, during the tuning phase, the team discovered that some of the RRD-generated code performed poorly. In response they added other JDBC optimizations. See Section 10.4.9 for specifics.
8.2.1.3 Overall Shape of the Code
RRD lets you choose from a variety of code generation patterns that will produce different code from the same design. The team made its choices based on past experience with development projects as well as performance research.
• Persistence tier. RRD lets you choose EJB entity beans or plain ordinary Java objects (POJOs) for database operations. The team decided to avoid the overhead of entity beans and went with POJOs.
• Business tier. RRD offers session beans and POJOs. Again the team chose the latter because of its lower overhead. One exception was the code for message production in the Customer Service application; RRD wraps that code in stateless session beans.
• Web tier. For Web pages, RRD offers JSPs or ordinary servlets. Since neither would be edited directly, the choice was to a large degree arbitrary. The team chose straight servlets. Note also that RRD has its own Web application framework, so the use of an external MVC like Struts was not considered.
• Message consumption. EJB message-driven beans (MDBs) have long been the accepted technique for consuming JMS messages within an application server. They are simple and lightweight, and RRD generates them by default. The team did not deviate from that choice.
4 Here is the exact SQL statement:
SELECT count(ticketid) FROM worktickets WHERE customerid = ? GROUP BY ticketstatus ORDER BY ticketstatus
8.2.1.4 Distributed Transactions
Those database actions stemming from JMS message processing required a distributed transaction to span the database update and the JMS action. Both sides of the system – Customer Service and Work Order – had such requirements.
Because distributed transactions require a two-phase commit, they are slower than one-phase transactions within a single database. So the team did not want to use distributed transactions for all database actions. Instead they set up two JDBC data sources to each database: one using an ordinary driver for simple transactions, the other using an XA -capable driver for distributed transactions.
8.2.2 What Went Well 8.2.2.1 Web Interfaces
The team split up the work of developing the two Web interfaces. Developer A took on the Customer Service Web application, as well as building some generic login and page security functionality. Meanwhile Developer B tackled the Work Order Web application, but first setting up style sheets and look and feel for the applications.
This part went very smoothly. RRD’s facility for building Web pages and linking them to data structures are two of its strong points. By the end of Day 3 (where Day 1 was devoted mostly to installing software), the team had completed much of the simple logic linking the Web interfaces to database actions.
8.2.2.2 Web Service Integration
Part of this process went smoothly. Developer A discovered that RRD only supported scalar types in a message; no schema, no complex data. He found it at least a little restrictive for real applications. In the face of that restriction he decided the best approach was to have the Web service return XML strings for its results, and hand code the logic to generate and parse those XML strings. This took time, but the coding was straightforward.
Other aspects of the Web service piece caused confusion and loss of time. See Section 8.2.3.2 below.
8.2.3 Significant Technical Roadblocks 8.2.3.1 Holding Data in Sessions
In the Customer Service application a user can create one or more work tickets (work order requests), then submit them to the system. The specification requires that the tickets be held in memory before submission; the obvious place to hold them is in the client’s HTTP session, and the standard way to do that would be as instances of some work ticket Data Transfer Object (DTO) class.
Here, however, Developer A discovered one of RRD’s limitations. Because of the way it organizes its generated code, RRD does not lend itself to the standard solution. RRD organizes all the generated code for a given page in a page-specific package. This includes page specific classes representing the data structures used by that page. In other words, if two pages both use the WorkTicket class from the class model, RRD generates a different
WorkTicket Java class for each page, each in a different package. This means that if Page 1 creates an instance of its WorkTicket class and places it in a session, Page 2 cannot use it as an instance of its WorkTicket class.
Developer A used RRD’s preferred solution to this problem: store the data in session in XML form. He used RRD’s features to define an XML data structure and map it to classes in the
model. The generated code uses a DOM5 API to parse the XML. This solution is tedious, and a bit rankling to the hard core J2EE developer. Nevertheless, it did work.
(Fast forward to Phase 2, when the team found all this to-ing and fro-ing between XML and objects a major performance bottleneck. They ripped out the XML code and replaced it with logic that used a custom DTO class.)
8.2.3.2 Web Service Integration
At one point Developer A ran into an anomalous situation with respect to the Web service on the Work Order side. RRD was not building the Web service properly, and the reason was not apparent. In his previous experience with RRD he had found that RRD did not generate an IBM-specific deployment descriptor that seemed to be necessary.
In the past he had used another tool (WSAD) to generate the missing descriptor, so he did so again. He created and built a simple Web service project simply to generate a descriptor.
When he included this descriptor in the RRD application, however, it did not deploy correctly.
It turned out that the initial failure was related to a bad state in RRD, possibly a source control issue. One of the files was not open for writing, but RRD didn't tell him. So the Web service implementation hadn’t been saved properly and consequently didn’t work.
Once he tracked down and fixed the file problem, RRD did successfully build and deploy the Web service without the custom descriptor. At that point Developer A took yes for an answer and moved on without investigating the anomaly. But the detour cost him several hours.
8.2.3.3 Configuring and Using WebSphere MQ
Developer A, responsible for the Customer Service application, also worked out how to get RRD to talk to WebSphere MQ on the development machine. Despite the fact that all the software products involved are IBM’s, this process was not as simple as it could be. It took some time to work out the correct permutation of settings to get them all to work. Once he had built the message senders and receivers in RRD, putting data into them and getting it out was fairly straightforward (using XML).
Later on, when the team set up its production environment, MQ again gave Developer A a headache. This while he was setting it up in a remote fashion (one MQ installation that served three applications residing on different servers). The process was complicated by the
undocumented fact that IBM WebSphere’s MQ installation did not use the default port of 1414 for MQ. Rather it used 5558, not at all obvious.6
8.2.3.4 Handling Null Strings in Oracle
The team discovered that Oracle treats empty strings in updates as null. This caused errors since most fields in the ITS schemas don’t allow nulls. The team compensated through code in two ways:
• Many string attributes required non-empty values. This requirement was enforced in the input forms.
• For non-required attributes, the developer set the initial value to a space character.
8.2.3.5 Building the Handheld Module
Although, as noted earlier, the team used a different tool (Sun One Studio ME) to build the handheld application, they did involve RRD in the process. Given several ways for the
5 Document Object Model, an API that treats an XML document as a tree of objects.
6 The developer discovered this by getting a complete process list using the ps command (ps –efl). He noticed an entry strmqmlsr that had a port setting, tried this number and it worked.
handheld application to communicate with the Work Order application, they chose to use a Web service. Using RRD, Developer B added a Web service interface to the Work Order Web application, defining the five remote operations needed by the handheld application:
• Login (logout doesn’t require a remote call)
• View work orders for this technician for a certain status
• View an individual work ticket
• Mark a ticket as started
• Submit time spent and mark ticket completed
Meanwhile Developer A ran a stub generator in the J2ME wireless toolkit in Sun One Studio to create the Web service client.
This should have been easy, but it wasn’t. It turned out that the stub generator supported only document/literal Web services, whereas RRD only supported rpc/encoded. No manner of tweaking the WSDL would make the two talk to each other.
But they had all the logic ready to use in EJB methods. They needed a way to allow the PDA to execute them. Luckily the wireless toolkit also had a wizard for converting a service (basically a simple Java class with some methods in it) into a servlet and client piece using HTTP post and simple data (not web services). So they butchered the EJBs generated by RRD into POJOs, delegated to their methods from the service class and ran the wizard. With that, they had the two halves talking to each other.
The final step was to build the front end MIDLet in J2ME. Luckily this step was very easy and took only a couple of hours.
8.2.3.6 Miscellaneous RRD Headaches
Along the way RRD posed a variety of smaller challenges. Among the more interesting:
Inability to centralize common page logic. Because RRD does not let you work directly with JSPs and because the ITS specification prohibited use of frames in the pages, the team could not easily centralize page logic that was common to most or all pages. This logic included the navigation bar (links to other pages) and page authentication logic to verify that the user is logged in before displaying the page. In RRD this logic had to be copied to every page. A tedious but finite process, it would have been much worse had the number of pages been significantly greater.
False error when building an EAR file for WebSphere. Developer A discovered a glitch in RRD’s build process for WebSphere. The build script that RRD creates makes an EAR file under <websphere home>/RationalRDApps/<application name>. If there are no JARs in the application (EJB JARs or custom libraries), the script returns an error code and RRD aborts. This is true even if there need not be any jars. Developer A worked around it by drop any old jar in the folder (such as Oracle’s classes12.jar) to avoid the false error code.
Placement of the GlobalObject class. For each application, RRD generates a GlobalObject class that contains any global functionality you define. Although the class is application specific, RRD does not package it in the resulting application EAR file. Rather it is treated like an external library: It must be placed in the server’s class path. This means the team had to bounce WebSphere whenever the class changed. Also, since they used WebSphere’s admin console rather than RRD to deploy to the production servers, they had to manually copy the class to its proper destination. It cost them some time figuring out where the class belonged and ensuring it was properly updated.
Date handling in a Web page. Developer B discovered an apparent bug in RRD’s handling of date fields in a page: After constructing a page with date fields tied to date attributes of an object in the model, he enter a date using the correct format, then print its value to standard
error. The printed value is one day behind the entered value. He wrote a simple global method to increment a date value and compensate for the discrepancy.
Inconsistencies regarding editing and source control. RRD works seamlessly with Visual Source Safe; you can easily check files out and in of VSS from within RRD. And for the most part, RRD is smart about preventing you from editing files that you have not checked out.
But Developer B found gaps in this intelligence, leading to wasted time. For example. when he started to add a session attribute to a project, he was able to define the attribute, enter its name and set its initial value, before realizing he had not checked out the source file where the attribute would be stored. But without checking out that file he couldn’t save his work. And checking out the file caused him to lose his work.
Adding a static HTML page to a Web application. At one point Developer B wanted to add a static HTML page to the Customer Service Web application. This turns out not to be easy at all for RRD. RRD has no facility for directly adding an actual HTML file to the web app. Instead it has a way to let you take “snapshots” of pages that change little or not at all. You designate the page as “static” in the page properties, then you have to go through a two-step construction process. Developer B created a dummy page and tried this, but quickly got bogged down in the details. So he gave up and instead simply dropped an HTML file in a folder that was included in the WAR build. That did the trick.