Industrial Logic -> Papers -> Techniques & Patterns For Writing Once And Running Anywhere

Techniques & Patterns For Writing Once And Running Anywhere

Version: 1.0
 Author: Joshua Kerievsky, Industrial Logic, Inc.
Written: March, 1998

This paper was delivered in presentation form at Object Expo '98 in New York City.

Abstract

Despite the proliferation of drag-and-drop tools for developing Java software for the Web, developers continue to face problems with inconsistent cross-platform look-and-feel, download delays, problems manipulating images, AWT limitations, browser bugs and VM speed. This tutorial discusses how to solve these problems using a small Java engine that avoids use of the AWT, provides image manipulation instructions, implements bug-workarounds and manages thread differences among browsers. The engine was built using a variety of Design Patterns that provide speed, adaptability and interoperability. So as well as describing the engine, this talk will show how to apply Design Patterns to Java.

The Promise of Java

Late in 1995, Sun introduces Java and describes it as:

A simple, object-oriented, distributed, interpreted, robust, secure, architecture-neutral, portable, high-performance, multithreaded, and dynamic language.

Memory management is replaced by garbage collection.

"Write Once, Run Anywhere!" is no longer a dream, and the Java Virtual Machine (JVM) promises to make nearly all platforms potentially "Java-enabled."

Netscape releases Navigator 2.0, furnished with a JVM, opening doors to a whole new way of doing Client/Server programming.

"Just In Time" compilers greatly improve Java's speed.

The Reality Today

All JVMs are not created equal. Nuances in JVM implementation make for unpredictable behavior.

"Write Once, Run Anywhere" is still a dream.

"Write Once, Debug Anywhere" is closer to the truth.

Netscape's and Microsoft's browsers sit on millions of desktops world-wide, containing different and buggy JVMs.

Java's Abstract Windowing Toolkit (AWT) is buggy and produces user interfaces that look and behave diffrently across platforms.

Major improvements like JavaSoft's Java Foundation Classes (JFC, a.k.a. Swing) are not widely available in browsers.

Bridging Promise with Reality

Sun's description of Java back in 1995 pointed toward an ideal.

Although that ideal is still not completely attainable today, there are ways to get closer to it.

Programming techniques and patterns, combined with building blocks already in Java, get us a lot closer to realizing Sun's ideal.

Today, you'll see commercial, Web-based Java applications and learn about the problems that emerged during design and development, along with the solutions to those problems.

Along the way, you'll learn about a Java Engine that was built to make it a little easier to "Write Once and Run Anywhere".

Overview and Objectives

Overview:

Objectives:

  • Describe design alternatives that overcome weaknesses in Java and in developing Java for the Web.

  • Describe techniques and patterns to help developers write platform-independent Java code.

Requirement: Same Look & Feel Across Browsers

August 1996: Industrial Logic wins a contract to build the Java version of MTV.com.

Despite the gap between Sun's ideal and the reality of Java/Web programming, MTV wants it's Jave version to:

Problem: Java's AWT UIs

Java's Abstract Windowing Toolkit (AWT) UIs don't look the same across platforms or inside browsers.

AWT components are buggy and require a good deal of sub-classing to add the functionality you need.

Sub-classing, or using third-party controls, requires larger, time-intensive downloads, and many third-party controls aren't lightweight.

AWT font support is lacking: It supports only simple fonts, and fonts rarely look the same across platforms and browsers.

AWT layout managers don't produce great-looking UIs.

Drag-n-drop tools don't produce great UI code and suffer from the same AWT cross-platform bugs.

Solution: Images & Code

Use images and write code to remove ties to the AWT.

+ GIFs and JPEGs really and truly look the same across platforms or inside browsers.
+ Using images to represent components frees you up to create stunning, professional-looking UIs.
+ Using images, you can easily separate a UI's "look" from its "behavior."
+ You can use whatever fonts you like, since fonts will ultimately be image-based.
+ You no longer rely on AWT layout managers.
- You must write "behavior" code for your components.

Example: Image Maps

Because MTV wanted a very specific look for its UI, Industrial Logic used MTV's trademark GIFs and created a generic image-map component to control behavior.

Summary

Java's AWT limitations and bugs make it difficult to guarantee that your UI will look and behave the same across platforms and browsers.

GIFs and JPEGs always look the same across platforms and browsers.

Platform-independent UIs can be written by using images and writing your own user-interaction code.

You're not limited by AWT fonts: You can use whatever fonts you or your customer wants.

Buttons, radio/check boxes, image maps, tabbed-panels, and the like are relatively easy to write. Grids and drop-down list boxes require a little more work.

Requirement: Support Change

MTV and Industrial Logic hammered out a clear, functional specification, describing the exact behavior MTV wanted for the Java version of its site.

For example, if a visitor to MTV were to click on "MTV Sports," MTV wanted the clicked UI component to change color and a specificied HTML page to load inside an HTML frame.

MTV also wanted a rotating list of news tickers and promotional graphics, plus the ability to specify the contents and order of that rotating list.

Many more such requirements were part of the MTV functional specification.

Problem: Too Many Parameters

Given the requirements, Industrial Logic began thinking of simple ways to let MTV parameterize its site.

One way would be to let MTV's staff update applet parameter tags. But the list of these tags could easily exceed 100, creating an unwieldy, complex mess.

Next, we considered writing off-line tools, to manage and write out the applet parameter tags. But the resulting HTML page would be too large and slow to download.

A third approach involved writing out and reading, at runtime, a flat file containing parameter information. But then our "thin" Java client would have to be very intelligent about how to parameterize UI components.

Solution: Use A Language

Place parameters in a language that clients can easily interpret.
+ A language can be composed of sentences that contain parameters, along with additional elements that signify behavior.
+ A language can include information about how a program will actually run.
+ A language can be created from friendly GUIs, text editors, HTML pages, or whatever your users like.
+ A language can be read and written, tokenized and detokenized, compressed and decompressed.
+ A client program can interpret a language in a predefined and consistent way, keeping the client fairly "dumb," and thus "lightweight."

Example: MTV's Language

A sample of a language used on the MTV project:

Each sentence in this language is composed of:

Summary

Businesses want software to support change easily.

Parameterizing programs may support changes, but users may have difficulty managing highly parameterized systems.

Using a language simplifies programs that are highly parameterized.

Client programs can implement simple interpretation strategies to turn languages into useful runtime information.

Languages can be stored and read back in many ways.

Languages can include sophisticated behavioral information that may exceed what parameters convey.

Requirement: Support New Behavior

Good software must support the addition of new behavior with a minimum of fuss.

The notion of Object Linking and Embedding is based on the idea of supporting new, unforeseen behavior.

After the first release of the Java version of MTV.com, MTV asked Industrial Logic to make its news tickers and promotions clickable. MTV wanted certain user clicks to trigger a number of UI events.

For future versions of its site, MTV expressed an interest in adding sophisticated animations, user-click tracking, sound effects, user recognition, legacy-system integration, and so on.

Problem: Add New Behavior

Given a language (and some client-side support), users can parameterize existing application behavior however they like.

But we still haven't provided a way for them to add new behavior and configure UI components to use it.

With a language, users can easily specify that an image map should load HTML page "X" rather than "Y," but they can't tell the image map to play a sound, show an animation, and then load an HTML page.

There's still no simple way to configure our system with new behavior.

Java's Class.forName() could help but still doesn't associate our UI components with particular classes.

Solution: Name & Execute Behavior

Name behavior and execute it using a standard protocol.
+ Create a simple Java interface containing a method called execute().
+ Make all your behavior classes implement this interface.
+ Assign names to your behavior classes: e.g., a class that loads images might be dubbed �LoadImage� or �LI�.
+ Let your language include named behavior
+ Let your behavior classes interpret language-based parameters.
+ Let one or many behavior classes be executed by UI elements.

Example: LoadCommand

Example usage of behavior class, "LoadCommand":
GetImageMan^LoadCommand^ili_ImageMan^ImageMan

Sample implementation:

public class ili_LoadCommand
  extends Object implements ili_Command
{
  ...
  public void execute(String id)
  {
    String _className = null;
    ...
    _className = _parms.nextElement().toStr();
    ili_Command _cmd = (ili_Command)
      (Class.forName(_className).newInstance());
    _command.init(_context);
    _context.getCmdStore().putCmd(
        _parms.nextElement().toStr(), _cmd);
    ...
  }
}

Summary

Good software must accommodate the addition of new functionality.

Adding new functionality should not necessarily involve changing a system's "guts".

By "naming" behavior, and using a standard protocol and language, users can make client programs execute requests, without letting the client program know what they are executing.

The levels of indirection in this solution allow client programs to remain "lightweight" while being capable of executing complex requests.

Behavior can easily be "batched" to support the execution of chained (or macro) behaviors.

Requirement: Download & Start Intelligently

Example Usage: IBM's e-Commerce Advisor

IBM wanted to:

Problem: Lengthy Downloads

In a network environment, if your users must wait a long time for a download, they will very likely get impatient.

Java does not come with any tools to help display progress meters while applets download.

Most browsers don't support multple archives (.zips, .jars, .cabs), so developers tend to download all of their .class files within one archive, making users wait and wonder.

Class files that are not used until some user action requires them are often downloaded anyway, regardless of whether they'll be needed.

Without any visual feedback, network bottlenecks appear to be a problem with your program.

Solution: Use A Microkernal

Download a minimal functional Microkernal first
+ A Microkernal separates a minimal functional core from extended functionality.
+ Using a Mircokernal, you can perform initial tasks both before and while your user waits
+ A Microkernal can serve to coordinate your language, your parameters and variables and your behavior classes
+ Mircokernals are typically the key element in adaptable systems.
- If you place your Microkernal code in an archive, it will download quickly, but you may then have to load classes individually (i.e. sloowly).

Example: e-Commerce Advisor

Industrial Logic's implementation of IBM's e-Commerce Advisor displays a descriptive splash screen first, and then makes the IBM logo gradually grow, indicating progress during the download.

Summary

Developing for any network environment requires sensitivity to network speed and a user's time.

Visual feedback goes a long way in helping ease the wait.

A Microkernal can be downloaded quickly to get some informative messages to a user.

You can leverage the great power of the Microkernal to "plug in" new behavior based on need.

The Microkernal can become the coordinator of your language, your parameters and variables, and your behavior classes.

Final Thoughts

Until Java matures (most notably in the major browsers), saying good-bye to the AWT is one of the first steps in beginning to write platform-independant Java code.

Working with behavior classes (Commands), languages, and a microkernal is an equally important step in making your Java programs behave the way you want them to.

Objects, Interfaces, and K Size

Requirement: Very small download files/archives.

Problem: Because you've used so many Design Patterns, you have an explosion of classes and interfaces that make your download k size rather high.

Solution when bandwidth is an issue:

Example: On one project, Industrial Logic noticed that it had almost 200K in objects and interfaces. We had an extreemely flexible implementation, but it was too damn big! We later got the total size down to 92K.

Cut, Consolidate, Create, and Tile Images

Requirement: You need to download many images.

Problem: It takes forever to download images.

Solution:

Example:

Handling Browser Bugs

Requirement: You must install a bug workaround. Problem: You don't want to modify "guts" of your code.
Solution: Create a behavior class and "plug" it into the Microkernal when appropriate.

Example: a nasty Macintosh Navigator VM bug.

The Proxy Pattern

Requirement: Download as little as possible.

Problem: Your UI may require a "heavyweight object," but you'd rather not download it until some request requires its presence.

Solution: Use a "lightweight" proxy object in place of the real object. Your code will talk to either the proxy or the real object the same way. But when a call comes in to the proxy object, it will load the real object and delegate the request.

Example: (While Proxy is an incredibly useful pattern, Industrial Logic has not used it in the way described above).

Adaptable Systems

What are Adaptable Systems?

Systems evolve over time--new functionality is added and existing services are changed. Adaptable Systems must support new versions of operating systems, user-interface platforms, or third-party components and libraries. Adaptation to new standards or hardware platforms may also be necessary. During system design and implementation, customers might request new features, often urgently and at a late stage. You may also need to provide services that differ from customer to customer.

--from Pattern-Oriented Software Architecture (POSA) by Buschmann, Meunier, Rohnert, Sommerlad and Stal.

Design Patterns

Forget the Hype.

The following are two excellent definitions of patterns:

Each pattern is a three-part rule, which expresses relations among a certain context, a problem, and a solution.
Christopher Alexander
--The Timeless Way of Building

A pattern is a named nugget of insight that conveys the essence of a proven solution to a recurring problem within a certain context amidst competing concerns.
Brad Appleton
--Patterns and Software: Essential Concepts and Terminology

When using patterns, remember:

Patterns may make you rich.

artist: Jay Munro, originally printed in DOC Magazine

Using Patterns

Industrial Logic's "Java Engine," the code powering the MTV and IBM applets, has a very high patterns density.

Principal patterns used in the "Java Engine" include:

Other patterns and techniques used:

  • Doug Lea's "Concurrent Programming in Java: Design Principles and Patterns"
  • Javascript: for controlling browser windows and parameter passing
  • Factory Method
  • Abstract Factory
  • Decorator

MTV & The ILI Java Engine

Problem Pattern
Bandwidth Issues Proxy, Composite, Microkernel
Change Requests Interpreter, Command
Integration Interpreter, Command
New Behavior Command, Composite, Interpreter, Microkernal
Browser Bugs Factory Method, Command
Timing Issues Null Object & Concurrency Principles

The Command Pattern

The Command pattern is one of the most powerful and useful patterns there are.

Problems it helps solve: change requests, integration, new behavior, browser bugs.

Intent: Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.

This pattern was referred to as "behavior classes" in the main presentation.

ILI Java Engine Implementation:

The Command Interface:

  public interface ili_Command
  {
    public void init(ili_Context context);
    public void execute(String s);
  }

Sample Usage:

  public class MyCommand
    extends Object implements ili_Command
  {
    protected ili_Context _context;
    public MyCommand() { }

    public void init(ili_Context context)
    { _context = context; }

    public void execute(String id)
    {
      // do something useful
    }
  }

MacroCommand: Run Multiple Commands

public class MacroCommand
 extends Object implements ili_CommandI
{
  protected ili_ListEnumerator _parms;
  ...
  public void execute(String id)
  {
    ...
    _parms = _context.getListEnumerator(id);
    try
    {
      while ( _parms!=null &&
              _parms.hasMoreElements() )
      {
         _context.execute(
           _parms.nextElement().toStr() );
      }
    }
    catch (Throwable e)
    {
      ... exception handling code
    }
  }
}

The Interpreter Pattern

Problems it helps solve: change requests, integration, new behavior.

Intent: Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.

The ILI Java Engine's Interpreter

We let Commands perform interpretation.

Project goal: let users fully control Commands,
thereby controlling client program behavior.

Commands are asked to interpret a language
whenever their execute() method is called.

Commands interpret sentences, and do so
however they see fit.

Sentences are composed by users.

Users create sentences by working with developer-defined "vocabularies" and "sentence formats."

Users create collections of sentences that combine to form intruction sets.

Ultimately, the Java Engine is controlled, at runtime, by instruction sets.

The POSA Microkernal Pattern

This Pattern

The ILI Java Engine's Microkernal

Problems it helps solve: new behavior, bandwidth issues.

The Java Engine's Microkernal helps:

  Industrial Logic, Inc.

 

Contents

The Promise of Java
The Reality Today
Bridging Promise With Reality
Overview and Objectives

Images & Code

Requirement: Same Look & Feel Across Browsers
Problem: Java's AWT UIs
Solution: Images & Code
Example: Image Maps
Summary

Use A Language

Requirement: Support Change
Problem: Too Many Parameters
Solution: Use A Language
Example: MTV's Language
Summary

Name & Execute Behavior

Rquirement: Support New Behavior
Problem: Add New Behavior
Solution: Name & Execute Behavior
Example: LoadCommand
Summary

Use A Microkernal

Requirement: Download & Start Intelligently
Problem: Lengthy Downloads
Solution: Use A Microkernal
Example: e-Commerce Advisor
Summary

Final Thoughts

Mini-Patterns

Objects, Interfaces, and K Size
Cut, Consolidate, Create, and Tile Images
Handling Browser Bugs
The Proxy Pattern

Using Patterns

Adaptable Systems
Design Patterns
Using Patterns

Case Study

MTV & The ILI Java Engine
The Command Pattern
The Interpreter Pattern
The ILI Java Engine's Interpreter
The POSA Microkernal Pattern
The ILI Java Engine's Microkernal

FacebookFacebook  TwitterTwitter  linked inLinkedIn