Java is a great language for developing enterprise applications. It's powerful, scalable, robust, secure, and typically very complex. As a software developer, I want to solve business problems, not spend man-months building the plumbing for my applications. This article will demonstrate how you can speed up the development and simplify the maintenance of enterprise-class Swing applications by keeping things simple. We'll look at ways to reduce the complexity of your application and the amount of custom code written for it. By limiting the complexity and the amount of plumbing code required, you'll develop more quickly, the application will be easier to maintain, and you can focus on the business logic that provides value to the customer.
In particular, I'll show you how to address three of the primary issues facing developers today:
According to the Progress Software Web site, "An R.B. Webber study concluded that coding and configuring object relational (O-R) data access typically accounts for 30% to 40% of total project effort."
There are development and performance repercussions due to this mismatch. Developers must spend time writing code that breaks objects into pieces that can be saved to and re-assembled from relational tables and determining the best way to map objects to relational tables becomes more difficult as the objects grow in complexity. There is also a certain amount of overhead due to this assembly and breakdown of objects that impacts performance. Finally, queries can take longer since the application has to perform multi-table joins when retrieving complex objects from the database rather than just opening the object.
Historically programmers have dealt with this by writing a data access layer that manages the reading/object assembly from the database and object disassembly/writing to the database. Recently there's been an explosion in the number of XML-based mapping tools that attempt to replace the native language data access code with frameworks that define the mapping using XML. The problem with this approach is that while it provides a mechanism for persisting and retrieving objects, letting the developer to deal with objects exclusively, it requires quite a bit of additional work (defining and maintaining the XML mappings), incurs a performance hit when the objects are read from and saved to the database, and often requires the user to query the database with non-standard object query languages that aren't as mature and powerful as SQL.
Eliminating the Object-Relational Impedance Mismatch
One way to eliminate the object-relational impedance mismatch altogether is to use an object database for your application's persistence layer. Object databases are now mature, robust, and fast. More importantly, they provide an easy mechanism for persisting and retrieving objects without the overhead of a mapping framework. This means faster development, better performance, and less code to maintain.
You can browse a list of object-oriented databases at Service-Architecture.com. As with any technology, every vendor's implementation provides slightly different features and functionalities. It's important to review each one to find the one that best fits your project's needs. Next, I'll demonstrate how using an object database instead of a relational one can dramatically simplify and speed up the development of your entire application.
Example # 1: Defining and Accessing Objects Using An Object-Oriented Database
For these examples, I'll use InterSystems Corporation's Caché Database. The databases from db4objects, Matisse, and Progress also provide Java interfaces and transparent object persistence. However, features and implementations are specific to each vendor's database.
With InterSystems Caché you create your database by defining the objects that it will contain. The objects contain properties that are analogous to standard SQL data types and are mapped to Java datatype equivalents. The objects support aggregation and inheritance and can be projected as Java classes that include methods for retrieving, modifying, and persisting the object.
Note that Caché also lets you access your data/objects via an SQL projection, meaning you can access your data in either a relational or object-oriented fashion, whichever is most appropriate for the task at hand.
The object database replaces four pieces of the application software stack. In addition, you will improve performance by eliminating the overhead of any XML mapping layer.
Just so you have an idea of some of the effort being eliminated, here's an example of a JDO XML map for a very simple object with only two fields. When using a mapping framework, you would normally create one of these for each object class in your database. By using an object database, you eliminate this step.
<?xml version="1.0"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Object Mapping DTD Version 1.0//EN"
"http://castor.org/mapping.dtd">
<mapping>
<class name="myapp.ProductGroup" identity="id">
<description>Product group</description>
<map-to table="prod_group" xml="group" />
<field name="id" type="integer" >
<sql name="id" type="integer"/>
</field>
<field name="name" type="string">
<sql name="name" type="char" />
</field>
</class>
</mapping>
Example: Object Definition That Demonstrates Aggregation and Inheritance
Now I'll demonstrate how easy it is to create persistent Java objects using Caché by creating a very simple employee database.
Caché comes with a GUI tool called Studio that's used to simplify the definition of classes. You define classes in much the same way you would define relational tables - except when you're done, you can access the objects directly without writing data access code or using an object-relational mapping layer. Much of the class definition text can be generated by wizards in the application - but it can also be manually coded if desired. We'll define four classes including:
/// This is a sample embeddable class representing an address.
Class base.Address Extends %SerialObject [ ClassType = serial, ProcedureBlock ]
{
Projection JavaClient As %Projection.Java;
/// Specify the proper Java Package
Parameter JAVAPACKAGE = "com.egrok.db.base";
/// The street address.
Property Street As %String(MAXLEN = 80);
/// The city name.
Property City As %String(MAXLEN = 80);
/// The 2-letter state abbreviation.
Property State As %String(MAXLEN = 2);
/// The 5-digit U.S. Zone Improvement Plan (ZIP) code.
Property Zip As %String(MAXLEN = 5);
}
The Person Class
Note that the Person class is being projected as a Java class in the package "com.egrok.db.base" and that the Person class has two properties that are derived from the serial address class: HomeAddress and WorkAddress. When you access these properties from the Java class, you'll use standard dot notation as follows:
Class base.Person Extends %Persistent [ ClassType = persistent, ProcedureBlock ]
{
Projection JavaClient As %Projection.Java;
/// Specify the proper Java Package
Parameter JAVAPACKAGE = "com.egrok.db.base";
Property Name As %String [ Required ];
Property LastName As %String;
Property HomeAddress As base.Address;
Property WorkAddress As base.Address;
Property Notes As %String(MAXLEN = 1000);
Property DateCreated As %TimeStamp;
/// Person is registered with the mailing list
Property IsRegistered As %Boolean;
}
person.getHomeAddress().getStreet();
The Employee Class
Class base.Employee Extends base.Person [ ClassType = persistent, ProcedureBlock ]
{
Projection JavaClient As %Projection.Java;
/// Specify the proper Java Package
Parameter JAVAPACKAGE = "com.egrok.db.base";
/// Specify the required property username
Property UserName As %String [ Required ];
/// Specify the username property must be unique
Index UserNameIndex On UserName [ Unique ];
Property Password As %String [ Required ];
/// The employee's current work status. Used when assigning jobs dynamically
Property WorkStatus As %String(VALUELIST = ",Available,Sick,Vacation,UnAvailable");
/// Defines a one to many relationship between the Department Object and Employee Objects
Relationship Department As base.Department [ Cardinality = one, Inverse = Employees ];
/// Defines an index on the Department property
Index DepartmentIndex On Department;
/// Specify the Employee's Manager - Example of Aggregation
Property Manager As base.Employee;
}
Here you can see that the Employee class extends the Person class and thus inherits all the properties and methods associated with the Person class. New properties specific to the Employee class are also added including a one-to-many relationship with the Department class and a reference to another employee, the Manager (an example of aggregation). We won't go into the details of parent/child and one-to-many relationships here since it's beyond the scope of this article.
Once again a Java Projection is defined. When you compile your class definitions, Java classes that provide access to the objects in the database will be created in the specified package. Simply by defining your database, you get access to pure Java objects that can be instantiated and persisted without any other code or mapping frameworks.
Tip: As with any Java class, you can extend the Java classes created by Caché (Java Projections) and add custom methods to them. Your application should then access these extended classes. This will insulate your application from most changes made to the class definition and preserve your custom methods, which would be overwritten the next time you compiled the database definition if you modify the Caché projection classes directly.
Example: Instantiating a Persistent Object
Listing 1 shows how to instantiate and access the Employee Java object from your Java code as well as some examples of how to access the data just as you would any other Java object.
That's it - straightforward Java objects. Just like any other persistence mechanism, you must connect to the database. However, once you've done that, you can access the object using standard dot notation. As you can see, you can also access linked objects (department and manager, for example) in the same manner without the need for creating multi-table SQL Joins. And remember - you've eliminated the need to define and maintain complex XML mappings for a JDO persistence layer!
Caché Tip: Use Caché's SQL projection when you are doing complex selection queries and updates to multiple objects at the same time. Use Caché's Object projection when dealing with a single object or linked objects.
Why Too Many Technologies Make Development and Maintenance Difficult
At this point, you've seen how to dramatically simplify and accelerate development on the server side, but you still need to get the data to the client and present it to the end user. Look under the hood of many Web -based applications today and you'll see a vast array of technologies and frameworks in use. A typical Struts application will require knowledge of HTML, JSP, XML, Java, and perhaps JSF and JavaScript. In addition, you have to learn Struts and possibly Tiles and some sort of Validation framework. (We'll forget about SQL and JDO for now, since we've solved that by using an object database.)
While Struts and Tiles add a layer of complexity, overall they're an improvement over custom code since they replace custom MVC and Tiling frameworks with proven, tested, and well documented frameworks that are based on industry standards such as Java/JSP/XML. (We'll explore why frameworks are good in the next section.) However, there are still some serious consequences to using all these technologies:
The Web stack provides mature and well-understood transport and binding layers via HTTP, JSP, JSF, AJAX, and frameworks Struts. If we are to use Swing to provide a richer user interface to the user, we must also provide a simpler data binding and transport solution that is at least as good as that provided using the typical Web stack. Fortunately the Java space has been maturing in this area and there are a number of options available.
What To Do When There's More Application "Plumbing" Code Than Custom Business Logic
What is plumbing? Communications and binding are part of what I call application plumbing - i.e., required for the application to function, but which aren't seen by the customer and don't provide value to the customer (as the business logic should).
The problem with complex distributed applications (enterprise apps) is that they require quite a bit of plumbing. Frequently you spend more time on the plumbing than on the code that provides value to the customer. This is drudge code. It has to be written, but solves well-understood problems and doesn't require creativity or (typically) differentiate your product from any other product.
Savings #3: Reduce Code by Using Frameworks Based on Open Standards
As previously referenced, just the plumbing needed to save and retrieve information from the database can comprise 30%-40% of your application code. When you add data transport, data binding, and perhaps a Model-View-Controller to the application, your application may consist of 80%-90% plumbing. The goal is to spend as little time on plumbing as possible so you can focus on the business logic that provides value to the customer. What's the solution?
Development Simplification Rule
Use frameworks based on open standards to provide the "plumbing" for your application so you can focus on the custom business logic. In general, the frameworks should:
In our case, we have to identify frameworks we can use with a Swing client to provide data binding and transport functionality to our application. What are we talking about when we say "data transport" and "data binding"?
Data Transport
Data Transport is how the information is sent between the client and the server. Since Swing is written in Java, you can use just about any kind of transport technology and protocol you want to - Sockets, RMI, XML-RPC, HTTP, etc. It really comes down to which one meets your application's needs. These day's it's common to communicate over port 80 so you don't have to open other firewall ports to use your application. If you're running only in a corporate network, perhaps you'll leverage Swing's strong support for managing Sockets. Alternately, you may want to use HTTP as demonstrated in the article "Struts Meets Swing" (see references) that explains how to connect a Swing client to a Struts application.
Frameworks available that provide data transport include: Canoo's ULC, Insitech's XTT and JDNC. Canoo manages communications between "half-objects" synchronizing the objects on the client with those on the server. Insitech's XTT uses XML over HTTP to pass data and objects between the client and the server.
Data Binding
Data binding is simply a term for synchronizing the presentation layer with the model layer or to put it more simply, tying a form input field to an object property. A robust data binding solution will automatically update the presentation layer if the model layer changes and vice versa. If your presentation layer and persistence layer are on the same machine, this is very straightforward. However, if you're operating across a network, you have to deal with the data transport as well.
The number of data binding solutions has increased significantly in the last year or so - so you'll have to do your homework and review them to decide which one best meets your needs. Here's a sampling: JGoodies, Lazlo, JDNC, Canoo ULC, Insitech XTT, Oracle AD, and Spring Rich. Besides synchronizing the data between the presentation and models, some of these frameworks take care of data transport and persistence as well. By using these binding and transport frameworks, more of the plumbing of an application is taken care of automatically, further reducing the development time and the amount of code that has to be maintained. I'm now going to demonstrate how much work can be saved (and how quickly you can develop) by using some of the available frameworks.
Visualizing the Savings
Let's see how two different binding and transport frameworks assist us in reducing the amount of code we have to develop for the left side of the software application diagram (see Figure 4 and Figure 5).
Canoo's ULC framework simplifies your life by synchronizing data on the server with the presentation layer. It provides a library of components, half-objects, that are placed on your forms and bound to their other half on the server. You don't have to write the binding code or worry about the client/server communications - it's taken care of for you.
Insitech's XTT provides binding and data synchronization between standard Swing components and relational or object data sources. As you can see from the diagram above, not only does XTT bind data with the presentation layer, it also transports it to the server and interacts directly with the persistence layer (either an object or relational database). This further reduces the amount of code you have to write and maintain. XTT ships with a collection of standard Swing components that have been extended to make them XTT-aware, but any Java component can be extended to make it usable by the framework. XTT also provides a simple mechanism for making remote method calls to server-side objects and returning serializable objects to the client.
Using either the Canoo or Insitech framework will dramatically speed up your development.
Wizards
Wizards can speed up development even more by generating code for you. However, make sure the code generated is readable and maintainable. Figure 6 shows a fully functional Swing form generated with the XTT Form Wizard that shows a department and all its employees.
It took less than five minutes to generate and since it's just Java Swing, it can easily be customized and extended for use in an application. You can see a detailed demo of XTT on JavaLobby. Tools like these can really kick your development into overdrive.
More Synergies: Combining Swing Frameworks with Object Databases
By combining a Swing framework with an object database, you can cover 80%-90% of your application's plumbing. Now you can focus on developing the user interface and business logic.
Visualizing Code Coverage
Figure 7 depicts using XTT with an object database, in this case InterSystems' Caché. The specific implementation will differ somewhat for other object databases and Swing frameworks. The purpose here is to illustrate what percent of the software stack we can address by using these technologies. Here XTT fully addresses the presentation layer, data binding, and data transport layers of the application. Note that XTT can bind directly to the SQL/relational interface of the database or to POJOs that are then synchronized with objects on the server. In this configuration, developers only have work with objects on both the client and server and data persistence from the client is automatically handled by XTT.
Summary
By using object databases, Swing, and Swing binding/transport frameworks you can reduce the plumbing code you develop by up to 80%-90% for the typical enterprise application. Each database and framework provides its own unique functionality and features and should be evaluated to see which best meets your needs - but most of them can significantly reduce the amount of code you have to develop and maintain. With 80% less code to write, you're well on your way to RAD.
References
Object Databases
Data Binding
Networking
Swing IDEs and Visual Editors
Swing Components
Miscellaneous
-SIDEBAR-
Swing on the Desktop
There's been a lot of noise about Swing making a comeback on the desktop lately. Performance concerns are a thing of the past and new projects like the Java Desktop Integration Components (JDIC), Java Desktop Network Components (JDNC), Data Binding, and SwingX are adding much needed capabilities to Swing that improve the user experience and simplify development by providing much needed functionality in easy to use libraries. This, continuing our theme of simplification, saves you from writing and maintaining your own code.
-SIDEBAR 2-
Development Simplification rules of Thumb