I've been getting a little Motown-ish lately. My little boy loves the stuff; he's got great taste in music. And given the rather heated debates both here at MC and all around the industry, it felt right to set the mood with one of the greatest anti-war protest songs of all time.
Because this isn't a war, folks. Lately, I've been somewhat demonized by folks who take my architectural preferences to be opinions about specific products, and that's simply not the case. When it comes to product choices, your selection is a business decision and should be ultimately guided by whether or not the tool does what you need it to do, not by how it does it. But at the same time, you should treat any tool purchase as a partnership, and as such you need to go into it with both eyes wide open. And to that end, I'm going to spend this column talking about my architectural opinion of Java.
What Does Java Do Well?
Like any tool, Java does certain tasks particularly well and others not so well. Hammers are perfectly suited for driving nails, while tennis rackets are not. Hammers, on the other hand, are pretty useless when it comes to lobbing a tennis ball. It's all about using the right tool for the right job. Incidentally, tiered environments are especially good for striking this kind of balance. You can use one technology for the user interface and another for the business logic.
What Else?
For one thing, Java excels at code reuse. The concepts of inheritance and polymorphism make it easy to write the basic logic of a task once and then repeatedly reuse that same logic. Let's take a simple example:
First, you write a routine to bump a date by one day. I won't go into this in great detail, except to say that Java is highly internationalized, to the point where even the calendar can be modified. The only calendar regularly in use is the Gregorian calendar, although there is a draft API for the Buddhist calendar. But because of this flexibility, in order to properly manage date arithmetic in Java, a Calendar object such as the GregorianCalendar is required. Thus, the routine creates a Gregorian calendar, sets it to the passed-in Date value, increments it, and returns the new Date.
The routine looks like this:
{
GregorianCalendar gc = new GregorianCalendar();
gc.setTime(originalDate);
gc.add(Calendar.DATE, 1);
return gc.getTime();
}
Not rocket science by any means, but it encapsulates the complexity of the API, which is one of the great concepts of Java. Now, though, let's say you want a little flexibility. You want to be able to specify the number of days. Well, that can be accomplished relatively easily:
{
GregorianCalendar gc = new GregorianCalendar();
gc.setTime(originalDate);
gc.add(Calendar.DATE, days);
return gc.getTime();
}
Now you can call the same method name (bumpDate) either to add a day or, if you specify the second parameter, to add a number of days. However, you probably notice a lot of common code in the two routines. This is where Java gets really nice. You can simply change the code of the original method as follows:
{
return bumpDate(originalDate, 1);
}
And even though that method has been entirely rewritten, any program that called the old bumpDate method expecting it to just add one day will still work the same with no changes. But now, if you need to add more than one day, you can by simply including a second parameter.
Next, you could add another parameter that further modifies the duration, perhaps allowing you to add weeks, months, or years. You'd simply add some more wrapper routines, and the base code that does the real work stays the same. This shows how polymorphism can provide lots of flexibility at reduced code costs.
This gets even more important in a tiered environment. Let's say this is something a little more complex, and it involves a call to an RPG program. You could encapsulate the call in one base method and then put more methods around it to support a variety of parameters and situations. And yet, the base logic is still encapsulated in your RPG program. Then, let's say a database file changes or a new business rule is added; the RPG program is changed to reflect the new reality, and all of the Java routines are updated at the same time. No duplication of effort, no double editing, no problems.
This scenario, by the way, tends to be less effective when you use JDBC and is less effective the closer the JDBC moves toward the UI. If your JDBC code is largely encapsulated and limited to the bottom-most Java rungs of your tiered architecture, then you can still enjoy most of the benefits of an encapsulated database, since that bottom rung is in fact performing the encapsulation function. But if your JDBC code is up in your UI and is repeated across multiple Java classes, then you have some serious binding issues that will likely cause you lots of extra maintenance down the road.
Write Once, Run a Lot
Java also does multiple platforms well. Java applications can typically move seamlessly from one platform to another without recompiling. Even Web applications, if they're not written to use features specific to an application server, can be moved from one application server to another with little or no modification. This capability is especially powerful in conjunction with a tiered architecture. Whenever I recommend J2EE applications, I always recommend a Web server-agnostic architecture, which then allows you to offload the Web serving portion. This can be very important for performance, for cost, or even for security reasons.
What Doesn't Java Do Well?
Well, a number of things, actually. One thing Java isn't particularly good at is business arithmetic. Adding, subtracting, multiplying, and dividing data with specific decimal precision is not Java's strong suit. Unlike RPG, Java has no primitive type that corresponds to a packed decimal (or even zoned decimal) number. Thus, you are stuck between using floating points numbers, with their inherent inaccuracy, or the BigDecimal class, which performs so terribly that IBM wrote its own to replace Sun's.
Database I/O is another area where Java performs poorly, particularly the kind we do most often in business applications. Fetching a single record, checking a few fields, and then fetching another record from another file is not Java's strength. Since Java's primary I/O interface is through JDBC, which is just a veneer over ODBC, most Java I/O is SQL-based in nature. Set processing is very efficient; record-by-record access is not.
Finally, Java is particularly poor in situations where the logic changes regularly. Anytime business requirements change, programs need to be rewritten. This is the nature of maintenance programming. However, some languages are better suited for change than others. Programs written in procedural languages such as RPG are usually easily modified, although this depends to some degree on the level of modularization in the application. Modularization is the procedural version of encapsulation; code common to multiple applications is moved to a separate module, which can then be called (how the code is bound to the caller is academic; from an architectural standpoint, called programs, bound modules, and service programs are identical). The more modularized an application is, the less places that need to be modified when a business requirement changes.
Java, on the other hand, has the same shortcomings that all strongly typed OO languages have: Changes to the object hierarchy are costly and painful. Since the layout of an object ties directly to what the object represents, changes to the business logic will cause significant changes to the object interface. And if other classes are built from this class, they need to be refitted as well. This hidden cost is what makes Java unsuitable for situations in which the logic may change drastically.
So Where Does Each Fit?
Given the relative strengths and weaknesses of Java, we can quickly identify the areas where Java fits best (and worst). Let's take a standard n-tier application.
Figure 1. Here's a standard three-tier application with an optional fourth tier.
The standard three-tiered application is shown above (forget the fourth tier for a moment). This design, consisting of a user interface, an application controller, and business logic, is roughly equivalent to the Model-View-Controller (MVC) pattern in OO design. The user interface is the view, while the business logic is the model. As the name implies, the application controller takes the role of controller.
The fourth tier is for distributed databases. While originally rarely used, with the increased use of Storage Area Networks (SANs) and other offline storage devices, the need for a database server becomes more prevalent. The database server acts as a layer between the business logic and the physical storage of the data. For example, you may have current data located on the iSeries on fast, expensive disk drives, while you may have historical data stored on less-expensive offline storage. You want queries to be able to access both sets of data seamlessly. Database servers are also appropriate when merged companies are running different enterprise applications and you need a cohesive view over different data sets. A database server can provide that sort of link.
The Best Tool for the Job
Start with the user interface. On the iSeries, our primary choices are RPG-CGI and JSP Model II (servlets and JavaServer Pages). There's been a lot of debate as to which is better, but most of that boils down to business issues, not technical issues. From a purely technical standpoint, I find JSP Model II superior to RPG-CGI, and if you agree, the user interface becomes one of the first places where Java is really a good fit.
On the other end of the spectrum is the business logic server. A business logic server actually accesses the database and applies the rules of the business to transactions. If you want to query data, the business logic determines whether you have access to the information you're requesting. If you want to update the database, the business logic validates your transaction to make sure it follows all the rules of the business, as well as making sure you have the authority to execute that function. I find this sort of logic easier to write in RPG than Java, often by a long shot. From a purely technical standpoint, RPG's SELECT opcode is far more useful than the Java switch statement, and RPG generally lends itself better to reading the database and acting on data-driven switches. Since Java has no inherent ISAM-like access to the database, it typically has to rely on JDBC and suffer with all its inefficiencies at single-record access.
The fourth tier is interesting. If you find yourself having to use a fourth tier, it's composition will depend on the work being done. If your database is truly distributed, with part of the data residing on a non-DB2 database somewhere on your network, perhaps the easiest way to access the data is through a Type 4 JDBC driver. Unlike most JDBC drivers, which require some platform-specific component (a component that comes from the database vendor and typically is not available for the iSeries), a Type 4 driver is 100% Pure Java and can be used by any machine with a JVM. Since the iSeries in turn has a JVM, then you can reasonably conclude that a Type 4 JDBC driver can be used on the iSeries. Therefore, if the database server is written in Java, it can access any database on the network that has a Type 4 driver. If, however, you're talking more about attempting heterogeneous access to DB2 databases with different formats, then RPG is the better choice.
That leaves the application controller. This is entirely up for grabs. The language of choice really depends on your preferences and your staff's skill set. In the majority of shops I work with, RPG skills tend to outweigh Java skills, so I suggest writing this portion in RPG. This way, the Java exposure is limited to a very thin veneer of Java whose primary purpose is to convert EBCDIC data structures to Java objects and vice versa. In this architecture, a job is submitted for each HTTP session, and that job executes programs much like it would in a typical interactive session. The programs in this architecture are very similar to standard RPG programs, with the exception that instead of sending data to the display file with a WRITE or EXFMT, the programs send data to an API, which in turn forwards it to the servlet for this session. The RPG controller program determines which JSP to display next and, after sending all the data for that page, sends a command to the servlet to display it.
This isn't the only architecture available. You can just as easily write the application controller in Java. With a Java controller, the servlet actually invokes a controller class. This class determines the next page to display and then talks directly to the business logic servers to get data. In this environment, there's less RPG code to write, but all of your application logic has to be written in Java.
Some Final Points
This column focused primarily on traditional tiered architecture business applications, and only browser-based ones at that. There are a couple of other architectural approaches, both of which seem well-suited for Java. The first is the thick client interface, using either a true thick client or the latest terminology buzzword, "rich client." A thick client has actual business logic down on the client, which must be refreshed whenever the business rules change, while a rich client interface is sort of like a browser for the desktop; it simply receives instructions from the server and renders them graphically on the desktop, with all the accoutrements of a thick client, including integration with desktop applications.
While thick clients can technically be written in RPG (IBM's VisualAge for RPG is one technique, as is ASNA's Visual RPG), the real contenders here are Java and .NET. Java is much more portable, with the ability to run on Linux and Mac as well as Windows. Not only that, but the new Rich Client Platform (RCP) provides an interface that is much nicer than that of the old Swing applications. .NET is by all accounts easier to develop in because of Visual Studio, but you're limited to Windows deployment.
And finally, there are Web Services (and other lesser-used Internet interoperability protocols like XML-RPC). These are ways for external systems to access your business logic. You can write these in RPG, but it is far easier to do it in Java running under a Web application server. So, if you plan to expose any of your business processes to the outside world, Java has another place in your organization.
The idea here is to provide a balanced view of the future of application architecture. You can indeed run an entirely Java-free shop, but you need to be able to prove that the loss of productivity of JSPs is worth it. At the same time, you need to make a realistic assessment of your corporate skill set to decide whether your application controller is going to be written in Java or RPG. Whatever direction you choose, I hope it's a productive one for your company, and I hope I could help make the decision a little easier.
Joe Pluta is the founder and chief architect of Pluta Brothers Design, Inc. He has been working in the field since the late 1970s and has made a career of extending the IBM midrange, starting back in the days of the IBM System/3. Joe has used WebSphere extensively, especially as the base for PSC/400, the only product that can move your legacy systems to the Web using simple green-screen commands. Joe is also the author of E-Deployment: The Fastest Path to the Web, Eclipse: Step by Step, and WDSC: Step by Step. You can reach him at
LATEST COMMENTS
MC Press Online