IBM Skip to main content
Search for:   within 
      Search help  
     IBM home  |  Products & services  |  Support & downloads   |  My account

developerWorks > Java technology
developerWorks
Diagnosing Java code: Decoupling package dependencies
Discuss73KBe-mail it!
Contents:
Definition of component-based
The Jiazzi component system
How Jiazzi works
Unit testing and Jiazzi
Decoupling the past
Resources
About the author
Rate this article
Related content:
Designing "testable" applications
Diagnosing Java code series
Demystifying Extreme Programming series
Subscriptions:
dW newsletters
dW Subscription
(CDs and downloads)
Component-based programming brings more flexibility to wiring testable code

Level: Introductory

Eric E. Allen (mailto:eallen@cs.rice.edu?cc=&subject=Decoupling package dependencies)
Ph.D. candidate, Java programming languages team, Rice University
1 November 2002

Column iconIn testing programs, how do you meet the tough challenge of simulating outside resources and library connections? Component-based programming and decoupling package dependencies may be the answer. Often touted as a means of facilitating code reuse, component-based software development also makes for more testable code. Eric Allen illustrates this concept of programming with examples from Jiazzi, a powerful, free tool for component-based programming using the Java language. Share your thoughts on this article with the author and other readers in the discussion forum. (You can also click Discuss at the top or bottom of the article to access the forum.)

One issue that comes up again and again in test-first programming is that many parts of a program seem impossible to test automatically. In particular, programs that make heavy use of outside resources and libraries seem hard to test because there's no good way to simulate the program's connections to these outside resources.

However, although such programs can be hard to test with Java code alone, there is a style of programming (with development tools) that addresses this problem -- component-based programming.

Component-based programming and the Java language
What do I mean by component-based programming? I mean simply programming with units of program distribution, not such run-time "components" as JavaBeans or similar technologies.

Conceptually, these units of distribution correspond roughly to Java packages. However, packages in the Java language are quite limited in that they aren't decoupled from one another. The classes in each package are hardwired to the packages that they import (because they must refer to these packages explicitly).

Because the packages aren't decoupled from each other, it's difficult to uniformly replace package references in a program with other package references that provide the same functionality.

Also, separate development teams may accidentally use overlapping package names, causing trouble when teams try to use each other's packages. To ensure distinct package names, Sun has strongly urged the convention that each development team use the reverse of its Internet address as a prefix to all packages that the team develops. This convention is often followed, but not always.

Still, even if the package-naming convention were followed perfectly, there are other reasons programmers would want to decouple components from one another. One reason is that we can test the components much more effectively -- we'll explain this as we talk about a tool for component-based programming, the Jiazzi component system.

Jiazzi: A component system for the Java language
One promising system for component-based programming in the Java language that offers full compatibility with the JVM and complete decoupling of components is Jiazzi, a development project from the computer science department at the University of Utah. This system allows programmers to superimpose components and wire them together atop existing Java code. No modifications of the Java language, or the JVM, is required.

The developer's description
The developers of Jiazzi describe it as:

... a system that enables the construction of [adds support for] large-scale binary components in Java. Jiazzi components can be thought of as generalizations of Java packages with added support for external linking and separate compilation. Jiazzi components are practical because they are constructed out of standard Java source code. Jiazzi requires neither extensions to the Java language nor special conventions for writing Java source code that will go inside a component. Our components are expressive because Jiazzi supports cyclic component linking and mixins, which are used together in an open class pattern that enables the modular addition of new features to existing classes.

The current implementation of Jiazzi integrates into the Java platform using a linker (for manipulating components) and a stub generator (to allow Jiazzi to be used with normal Java source compilers). Components in Jiazzi contain, import, and export Java classes and the Java platform's in-language support for inheritance can be used across component boundaries. Besides being expressive, the components are robust -- the implementation and linking of a component can be separately type checked.

Watch it decouple components
To see how Jiazzi decouples components, let's consider a short example of a Java package, view, that makes use of the GUI library package. We'll call this package toolkit. To refer to all classes in the package, we would put an import statement at the top of the source files in our package:


package view;

import toolkit.*;

...

Normally, this would tie the view package to the toolkit package. But imagine that we wanted to compile this source file without nailing down which package we're actually importing. Instead of hardwiring the connections between the toolkit and view packages, we can envision defining a function over packages that takes a toolkit package and returns a view package that depends on it. In Jiazzi, these functions are called units.

Units are like LEGO bricks; they can be snapped together to create a program. If we view units as functions, we can say that Jiazzi provides for functional composition. Every unit takes one or more packages with specified "package signatures" and exports one or more packages, again with a specified signature.

Package signatures are like types; they constrain the shape of a package. A package signature would define the classes expected in a package, the method signatures of those classes, and so on. The signatures of the exported packages may depend on those imported.

There are two types of units:

  • Atoms, which are simple mappings between packages
  • Compounds, which are compositions of other units

Atoms describe the packages they import and export directly. Compounds inherit the imported and exported packages from the units they compose. If we view units as LEGO bricks, atoms are individual LEGO bricks and compounds are structures built from multiple LEGO bricks.

Units are described in separate files with a special specification language. This language assigns names to the packages input and output by a unit. For example, here is a simple atomic unit that takes in a toolkit package and outputs a view package:


atom app_view {
  import toolkit: toolkit_s;
  export view: view_s;
}

This unit is named app_view. It assigns the name "toolkit" to the package it imports. This package is declared to fit a particular package signature called toolkit_s. The package exported by the unit is called view and is declared to fit the signature view_s.

How Jiazzi works

javax.swing

signature toolkit_s {
  class Frame {
    public Container getContentPane();
    public Component getGlassPane();
    ...
  }
  
  class OptionPane {
    public Object getDialog();
    ...
  }
  ...
}

signature view_s {
  class EditorPane {...}
  class InteractionsPane {...}
  ...
}

signature toolkit_s<awt> {
  class Frame {
    public awt.Container getContentPane();
    public awt.Component getGlassPane();
    ...
  }
  
  class OptionPane {
    public Object getDialog();
    ...
  }
  ...
}

atom app_view {
  import 
    my_toolkit: toolkit_s%lt;awt_s>;
    my_awt: awt_s;     
  export my_view: view_s;
}

atom default_toolkit {
  import my_awt: awt_s;
  export my_toolkit: toolkit_s<my_awt>;
}

atom default_awt {
  export my_awt: awt_s;
}

compound app {
  export my_view: view_s;
  
  local v: app_view, a: default_awt, t: default_toolkit;

  link 
    a@my_awt to t@my_awt, a@my_awt to v@my_awt,
    t@my_toolkit to v@my_toolkit,
    v@my_view to my_view;
}
  • First, a set of signature and unit definitions are fed to the Jiazzi stub generator, which then generates stub class files for all of the classes imported in the signature of any unit given to it.

  • These class files are then used by a conventional Java compiler to compile the source files corresponding to the classes exported by the units given to the stub generator.

  • After compilation of the sources files, the Jiazzi unit linker checks that the resulting class files match the class signatures declared in the original units. This is necessary because:

    1. Any third-party compiler can be used with Jiazzi
    2. Jiazzi never examines the Java source files

(By the way, notice that Jiazzi's approach of never actually viewing the Java source code is a big win. It allows us to use Jiazzi with compilers for non-Java languages targeted for the JVM, such as Jython, JSR-14, and NextGen. In fact, Jiazzi itself is written in JSR-14.)

After checking, the component linker generates a JAR file for each unit given to it. This JAR contains the compiled sources and stubs, as well as the signature information, as metadata. These JARs can then be linked together by feeding them to Jiazzi alongside appropriate compound units.

The Jiazzi unit linker works offline and solely over class file constant pools, renaming hidden methods to avoid accidental method-name collision.

Units can also be linked online by a special class loader. However, because the stub classes that the units were compiled against are not available, type checking must be done in the class loader as an "incremental whole-program analysis." In fact, at present, Jiazzi programmers must use a combination of offline and online linking, as many classes in the standard Java libraries can be linked to only through the class loader.

Another limitation of the current system is that renaming interferes with JNI and the reflection libraries. In particular, native methods are not renamed, as they are written in C code. As a result, many of the class libraries (which rely heavily on JNI and reflection) can't be repackaged as Jiazzi components.

As noted, compound units describe connections between other units. The linkages are in relation to the bound names of the imported and exported units of the compound; the linked units are completely unaware that they are linked (the linker ultimately macro-expands these compound units into atoms).

In this way, we can create and distribute JAR files for a program in such a way that we could swap new packages in and out with a simple recompilation. Not a single line of Java source code need be touched.

Additionally, other users of Jiazzi can develop against the classes provided by our program before our JAR file is available; all they would need are the package signatures corresponding to what we export. And their extensions would link just as well to any other implementation of that package signature.

Unit testing and Jiazzi
Component-based programming offers big advantages. The most often touted advantage is that components allow for greater code reuse. Off-the-shelf components can be distributed separately and plugged into new applications at will. But this style of programming also allows for much more effective unit testing.

During testing, the constituent components of a program can be linked with special "mock" components whose classes simply record the actions of the components they test. In essence, these mock components serve a similar role to that of Recorders (see "Recorders test for proper method invocation" in Resources), but at the component level.

The tested components are like Cartesian "brains in vats"; they can't tell the difference between being wired to mock components or to real components. For example, in our sample application earlier, we could write a special test_app compound that wires our app_view unit with a test_toolkit unit, like this:


compound test_app {
  export my_view: view_s;
  
  local v: app_view, a: default_awt, t: test_toolkit;

  link 
    a@my_awt to t@my_awt, a@my_awt to v@my_awt,
    t@my_toolkit to v@my_toolkit,
    v@my_view ot my_view;
}

Package test_toolkit would export a package of the same signature as toolkit, but this package would consist solely of mock objects that fooled app_view into thinking that it was linked to a true toolkit. These mock objects could record the actions performed by app_view, as these recordings could be checked in the unit tests.

Compare this system with the Java package system, where every source file must explicitly state the packages it imports. In the Java package system, the only way to fool a whole package into linking to a test package would be to edit all of the source files and recompile. That procedure doesn't really lend itself to automated testing.

Unfortunately, because Jiazzi (quite reasonably) can't handle reflection or JNI, and because these facilities are used extensively by many of the built-in Java APIs, the existing API packages can't be converted into Jiazzi components. But if they could, we'd be able to perform much more powerful tests over our programs. For instance, a program that used the Swing API could be wired to a mock Swing component during testing that ensured all of the appropriate API calls were made without actually trying to draw graphics objects. In a similar manner, we could test interaction with the java.io package, Java3D, JDBC, RMI, and any other API where the performance or nature of the functionality makes clients difficult to test.

Okay, nice dream, right? But even if reflection and JNI prevent us from making existing APIs into first-class Jiazzi units, there is a potential compromise.

Although connections between the existing APIs couldn't be decoupled, connections from new units to these APIs could be. In essence, the APIs could be bundled in special units that only exported classes; their imports would still be hardwired through the existing Java package system. Doing so would give us 99 percent of the functionality we'd have if these APIs were bona fide units; the only programmers that wouldn't be happy would be those who would like to build third-party replacements to the existing APIs.

Hopefully, Jiazzi evolution will move in this direction, or on a similar path that allows us to use it with the existing APIs. In the meantime, it still provides a very powerful mechanism for testing packages that don't use reflection or JNI.

Decoupling the past
The original motivation behind Jiazzi was as a mechanism for feature extensibility -- decomposing a design into multiple features (in which each feature is put in a separate component). Component-based programming advocates envision a future in which developers build programs from off-the-shelf components provided by component vendors. Components with common signatures could be swapped in and out like car parts.

Whether or not that comes to pass, there are big advantages to component-based programming even without a ubiquitous component software market. In particular, this style of programming allows for much more effective unit testing.

As we've shown, component-based programming with Jiazzi provides a very powerful way to test program components, and it works with existing Java (or Jython, or JSR-14) source files. We can all benefit from this powerful tool. It and others like it are an essential part of test-oriented programming.

Next time, I'll discuss Jam, an extension of the Java language that allows for mixin-based programming. Just as Jiazzi provides a way to decouple package dependencies, mixins provide a way to decouple class dependencies. As you might have guessed, mixins provide us with yet another powerful mechanism for testing a program.

Resources

About the author
Eric Allen has a bachelor's degree in computer science and mathematics from Cornell University and is a PhD candidate in the Java programming languages team at Rice University. Before returning to Rice to finish his degree, Eric was the lead Java software developer at Cycorp, Inc. He has also moderated the Java Beginner discussion forum at Javaworld. His research concerns the development of semantic models and static analysis tools for the Java language, both at the source and bytecode levels. Eric is the lead developer of Rice's experimental compiler for the NextGen programming language, an extension of the Java language with added language features, and is a project manager of DrJava, an open-source Java IDE designed for beginners. Contact Eric at eallen@cs.rice.edu.


Discuss73KBe-mail it!

What do you think of this document?
Killer! (5) Good stuff (4) So-so; not bad (3) Needs work (2) Lame! (1)

Send us your comments or click Discuss to share your comments with others.



developerWorks > Java technology
developerWorks
  About IBM  |  Privacy  |  Terms of use  |  Contact