DekGenius.com
Previous Section  < Day Day Up >  Next Section

1.1 Bloat Drivers

I'll illustrate the bloat by comparing it with the famous Lewis and Clark expedition. They started with a huge, heavily loaded 55-foot keel boat. Keel boats were well designed for traversing massive rivers like the Missouri and the Mississippi, but quickly bogged down when the expedition needed to navigate and portage the tighter, trickier rivers out West. Lewis and Clark adapted their strategy; they moved from the keel boats to canoes, and eventually to horseback. To thrive, we all must do the same. Java has not always been hard, and it doesn't have to be today. You must once again discover the lighter, nimbler vessels that can get you where you need to go. If the massive, unwieldy frameworks hinder you, then don't be afraid to beach them. To use the right boat, you've got to quit driving the bloat.

Over time, most successful frameworks, languages, and libraries eventually succumb to bloat. Expansion does not happen randomly—powerful forces compel evolution. You don't have to accept my premise blindly. I've got plenty of anecdotal evidence. In this chapter, I'll show you many examples of the bloat in applications, languages, libraries, frameworks, middleware, and even in the operating system itself.

1.1.1 Enterprise Mega-Frameworks

Java developers live with a painful reality: huge enterprise frameworks are en vogue. That might be good news to you if you're among the 10% of Java developers who are working on the hardest problems, and your applications happen to fit those enterprise frameworks perfectly. The rest of us are stuck with excruciating complexity for little or no benefit. Successful J2EE vendors listen to the market:

  • Vendors can charge mega-dollars for mega-frameworks. Selling software means presenting the illusion of value. Big companies have deep pockets, so vendors build products that they can sell to the big boys.

  • It's hard to compete with other mega-frameworks if you don't support the same features. Face it. Software buyers respond to marketing tally sheets like Pavlov's dogs responded to the dinner bell.

  • Collaboration can increase bloat. Whenever you get multiple agendas driving a software vision, you get software that supports multiple agendas, often with unintended consequences. That's why we have two dramatically different types of EJB. The process satisfied two dramatically different agendas.

You can almost watch each new enterprise framework succumb to the bloat, like chickens being fattened for market. In its first incarnation, XML was slightly tedious, but it provided tremendous power. In truth, XML in its first iteration did almost everything that most developers needed it to. With the additions of XML Schema and the increased use of namespaces, XML is dramatically more cumbersome than ever before. True, Schema and namespaces make it easier to manage and merge massive types. Unfortunately, once-simple web services are taking a similar path.

But none of those frameworks approach the reputation that Enterprise JavaBeans (EJB) has achieved for bloat. EJB container-managed persistence (CMP) is the poster child for tight coupling, obscure development models, integrated concerns, and sheer weight that are all characteristic of the bloat (Figure 1-1).

Figure 1-1. In theory, EJB's beans simplify enterprise programming
figs/bflJ_0101.gif


Figure 1-1 shows the EJB container-based architecture. Beans plug into a container that provides services. The premise is sound: you'd like to use a set of system services like persistence, distribution, security, and transactional integrity. The EJB is a bean that snaps into the container, which implements the set of services that the bean will use. Within the bean, the developer is free to focus on business concerns in the bean.

My favorite childhood story was The Cat in the Hat by Dr. Seuss, who should have been a programmer. I loved the game called "Up, up, with the fish," in which the Cat tries to keep too many things in the air at once. As an EJB programmer, it's not quite as funny, because you're the one doing the juggling. Consider this very simple example in Example 1-1. I want a simple counter, and I want it to be persistent. Now, I'll play the Cat, and climb up on the ball to lob the first toy into the air.

Example 1-1. Counter example: implementation
    package com.betterjava.ejbcounter;

    import javax.ejb.*;
    import java.rmi.*;

    /**
     *  CMP bean that counts
     */


[1] public abstract class Counter implements EntityBean{

        private EntityContext context = null;

        public abstract Long getID( );
        public abstract void setID(Long id);

        public abstract int getCount( ); 
        public abstract void setCount(int count);
        
Ͽ      public abstract Object ejbCreate(Long id, int count);

          throws CreateException {

          setId(id);
          setCount(count);

          return null;
        }


        public void ejbPostCreate(Long id, int count)
          throws CreateException { }

        public void setEntityContext(EntityContext c) {
          context = c;
        }

        public void unsetEntityContext( ) {
          context = null;
        }

        public void ejbRemove( ) throws RemoveException { }
        public void ejbActivate( ) { }
        public void ejbPassivate( ) { }
        public void ejbStore( ) { }
        public void ejbLoad( ) { }

[3]       public void increment( ) {

          int i=getCount( );
          i++;
          setCount(i);
       }

         public void clear( ) {

           setCount(0);
         }

      }

The first file, called the bean, handles the implementation. Note that this class has the only business logic that you will find in the whole counter application. It accesses two member variables through getters and setters, the counter value and ID, which will both be persistent. It's also got two other methods, called clear and increment, that reset and increment the counter, respectively.

For such a simple class, we've got an amazing amount of clutter. You can see the invasive nature of EJB right from the start:

  1. [1] This class implements the EJB interface, and you've got to use it in the context of an EJB container. The code must be used inside a container. In fact, you can use it only within an EJB container. You cannot run the code with other types of containers.

  2. [2] You see several lifecycle methods that have nothing to do with our business function of counting: ejbActivate, ejbPassivate, ejbStore, ejbLoad, ejbRemove, setEntityContext, and unsetEntityContext.

  3. [3] Unfortunately, I've had to tuck all of the application logic away into a corner. If a reader of this application did not know EJB, he'd be hard-pressed to understand exactly what this class was designed to do.

I'm not going to talk about the limitations of container-managed persistence. If you're still typing along, you've got four classes to go. As the Cat said, "But that is not all, no that is not all." Example 1-2 shows the next piece of our EJB counter: the local interface.

Example 1-2. Local interface
package com.betterjava.ejbcounter;

import javax.ejb.*;

/**
 *  Local interface to the Counter EJB.
 */

public interface CounterLocal extends EJBLocalObject {

  public abstract Long getID( );
  public abstract void setID(Long);
  public abstract int getCount( ); 
  public abstract void setCount(int count);
  
}

This is the interface, and it is used as a template for code generation. Things started badly, and they're deteriorating. You're tightly coupling the interface to EJBLocalObject. You are also dealing with increasing repetition. Notice that I've had to repeat all of my implementation's accessors, verbatim, in the interface class. This example shows just one instance of the mind-boggling repetition that plagues EJB. To effectively use EJB, you simply must use a tool or framework that shields you from the repetition, like XDoclet, which generates code from documentation comments in the code. If you're a pure command-line programmer, that's invasive. But, "`Have no fear,' said the Cat." Let's push onward to Example 1-3.

Example 1-3. LocalHome interface
package com.betterjava.ejbcounter;

import javax.ejb.*;
import java.rmi.*;
import java.util.*;

/**
 *  Home interface to the local Counter EJB. 
 */
public interface CounterLocalHome extends EJBLocalHome {

  public Collection findAll( ) throws FinderException;

  public CounterLocal findByPrimaryKey(Long id) throws FinderException;

  public CounterLocal create(Long id, int count)
    throws CreateException;
}

In Example 1-3, you find the methods that support the container's management of our persistent object. Keep in mind that this class is a generic, standalone persistent class, with no special requirements for construction, destruction, or specialized queries. Though you aren't building any specialized behavior at all, you must still create a default local home interface that builds finder methods and templates for the lifecycle of the bean, like creation and destruction.

At this point, I'm going to trust that you've gotten the message. I'll omit the painful deployment descriptor that has configuration and mapping details and the primary key object. I'm also not going to include a data transfer object (DTO), though for well-documented reasons, you're not likely to get acceptable performance without one. Dr. Seuss sums it up nicely: "And this mess is so big and so deep and so tall, we cannot pick it up. There is no way at all."

You'd be hard-pressed to find a persistence framework with a more invasive footprint. Keep in mind that every persistent class requires the same handful of support interfaces, deployment descriptors, and classes. With all of this cumbersome, awkward goo, things get dicey. Some Cats have enough dexterity to keep all of those toys in the air. Most don't.

1.1.2 Progress

Developers do not want their programming languages to stay still. They want them to be enhanced and improved over time; so, we must continually add. Yet language vendors and standards boards can't simply remove older interfaces. In order to be successful, languages must maintain backwards compatibility. As a result, additions are not usually balanced with subtractions (Figure 1-2). That's a foolproof recipe for bloat.

Figure 1-2. Backwards compatibility with progress leads to bloat
figs/bflJ_0102.gif


If you'd like to see an example of this principle in action, look no further than the deprecated classes and methods in Java. Deprecated literally means "to disapprove of strongly," or "to desire the removal of." In Java, Sun warns against the use of deprecated classes and methods, because they may be removed in some future release. I assume that they are defining either remove or future very loosely, because deprecated methods never disappear. In fact, if you look at the AWT presentation library for Java, you'll find many methods that have been deprecated since Version 1.1, over a half a decade ago. You can also look at the other side of the equation. The next few versions of Java are literally packed with new features.

If you're wondering about the impact of these changes on the overall size of the Java runtimes, then you're asking the right questions. Let's take a very basic metric: how big was the Zip file for the Windows version of the standard edition SDK? Table 1-1 shows the story. In Version 1.1, you would have to download just under 3.7 megabytes. That number has grown to 38 megabytes for JDK 1.4!

Table 1-1. Zip file size for standard edition Java developer kit in Version 1.1 and Version 1.4

JDK version, for Windows

Zip file size

JDK 1.1

3.7 MB

J2SE 1.2

20.3 MB

J2SE 1.3

33.2 MB

J2SE1.4

38.0 MB


You may ask, so what? Computers are getting faster, and Java is doing more for me than ever before. It may seem like you've got a free ride, but the ever-growing framework will cost you, and others:

  • Some of the growth is occurring in the standard libraries. If the bloat were purely in add-on libraries, then you could perhaps avoid it by choosing not to install the additional libraries. But you can't dodge the standard libraries. That means that your resource requirements will increase.

  • Java is harder to learn. Early versions of Java allowed most programmers to pick up a few books, or go to class for a week. Today, the learning curve is steeper for all but the most basic tasks. While the steep curve may not directly affect you, it does affect your project teams and the cost of developers.

  • It's harder to find what you need. Since the libraries continue to grow, you need to wade through much more data to find the classes and methods that you need to do your job.

  • You need to make more decisions. As alternatives appear in the basic Java toolkits (and often in open source projects), you've got to make more decisions between many tools that can do similar jobs. You must also learn alternatives to deprecated classes and methods.

  • You can't fully ignore old features: people still use deprecated methods. How many Vectors have you seen in the past couple of years?

Platforms are not immune to the bloat. That's a fact of life that's beyond your control. My point is not to add needless anxiety to your life, but to point out the extent of the problems caused by the bloat.

1.1.3 Economic Forces

To be more specific, success drives bloat. The marketplace dictates behavior. Microsoft does not upgrade their operating systems to please us, or to solve our problems. They do so to make money. In the same way, commercial drivers will continue to exert pressure on Java to expand, so you'll buy Java products and align yourself with their vision. Beyond license fees, Sun does not make money directly from Java, but it's far from a purely altruistic venture. The Java brand improves Sun's credibility, so they sell more hardware, software, and services.

Market leaders in the software industry cannot stay still. They must prompt users to upgrade, and attract new customers. Most vendors respond to these challenges by adding to their feature set. For just one example, try installing Microsoft Office. Check out the size of the Word application. Though most users do little more than compose memos and email, Word has grown to near-Biblical proportions. Word has its own simple spreadsheet, a graphics program, and even web publishing built in. Most Word users have noticed few substantive changes over the years. To me, the last life-changing enhancements in Word were the real-time spelling checker and change tracking. Upgrade revenue and the needs of the few are definitely driving Word development today. Keep in mind that I'm an author, and spend way too much time in that application. Of course, we can't blame Microsoft. They're trying to milk a cash cow, just like everyone else. Yet, like many customers, I would be much happier with a cheaper word processor that started faster, responded faster, and crashed less.

Within the Java industry, BEA is an interesting illustration of this phenomenon. To this point, BEA has built a strong reputation by delivering an outstanding application server. From 2001 to the present, BEA and IBM have been fighting a fierce battle to be the market-leading J2EE application server. IBM increased their WebSphere brand to include everything from their traditional middleware (the layer of software between applications and the operating system) to extensions used to build turnkey e-commerce sites and portals. Two minor competing products, JBoss and Oracle9iAS, were starting to eat away at BEA's low-end market share. Both of these products were inexpensive. Oracle priced their product aggressively for users of their database, and JBoss was an open source project, so BEA was under tremendous pressure to build more value into their product and stay competitive. They responded by extending their server to enterprise solutions for building portal software, messaging middleware, and business integration. They also started a number of other initiatives in the areas of data (Liquid Data), user interface development (NetUI), and simplified application development (WorkBench). Building a great J2EE application server is simply not enough for BEA any more. They, too, must expand—and extend the inevitable bloat.

1.1.4 Misuse

Nothing drives bloat more than misuse. If you go to Daddy's toolkit and borrow his cool pipe wrench when you need to drive a nail, something's going to go awry. The book Antipatterns, by William J. Brown, et al. (Wiley & Sons), refers to this problem as the golden hammer. When you've got a golden hammer, everything starts to look like a nail . Misuse comes in many forms:


Framework overkill

I've seen a departmental calendar built with Enterprise JavaBeans. I've also seen tiny programs use XML for a two-line configuration file.


Design patterns

These days, it's almost too easy to use a design pattern. When you trade power for simplicity too many times, you get bloat.


Sloppy reuse

If you try to stuff a round peg in a square hole, you'll have to adapt the hole or the peg. Too many adaptations will often lead to bloat. Cut-and-paste programming also leads to bloat.


Poor process

Like fungus in a college refrigerator, bloat best grows in dark, isolated places. Isolated code with no reviews and one owner lets bloat thrive unchecked.

Many developers wear golden hammers as a badge of honor. Reaching for the wrong tool for the job is nearly a rite of passage in some of the places that I've worked. It's a practice that may save a few minutes in the short term, but it will cost you in the end.

    Previous Section  < Day Day Up >  Next Section