ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Building Modular Applications with Seppia

by Lorenzo Puccetti
03/16/2005

Software development has changed. Developers no longer build large systems from scratch, but rather assemble them from existing components. Many products, both open and closed source, are now stable enough to build on top of these components and developers have finally tired of re-inventing the wheel and have begun to work with them. This is very good, as it seems that object-oriented software construction is delivering on one of its best promises: reusability. However, assembling independently developed components is itself a non-trivial task that can lead into complex integration problems, as well as tightly coupled code, if not performed correctly.

What we need now is a tool that blends separately developed components into a well-crafted finished product; a tool that protects major subsystems of the architecture from each other. It should be easy to understand because of what it encourages: rapid development against a very modular environment. It should let you assemble applications from existing components, putting in as little glue as possible. And it should endeavor to simplify the need for highly customizable applications to have tailor-made functionality.

Seppia was designed with those goals in mind.

Introducing Seppia

Seppia is a simple Java technology to build any Java application from constituent parts. Seppia is structured around the concept of a module. A module is a self-contained part of a system. Modules are stored in folders. A hypothetical word processor built in Seppia could contain modules like this:

 |
+ modules
      +-- org.hypotheticalwordproc.spellchecker
      +-- org.hypotheticalwordproc.ui-swing
      +-- org.hypotheticalwordproc.io
      +-- org.hypotheticalwordproc.searchengine
      +-- ...

Under this model, each module is responsible only for providing a specific piece of functionality to the system.

The behavior of each module is split between Java code and JavaScript code. The Java code, stored in .jar files, provides an API for the JavaScript code to work with. The JavaScript code, stored in .js files, defines the services provided by the module.

The spellchecker module could look like this:

 +-- org.hypotheticalwordproc.spellchecker
            +-- jars
            |    +--- jazzy.jar // open source Java API.
            |
            +-- javascripts
            |       +--- WordFinder.js
            |       +--- SentenceAnalyzer.js
            |       +--- ...
            |
            +--- dictionaries
                   +--- english.dic
                   +--- france.dic

In this example, the JavaScripts WordFinder.js and SentenceAnalyzer.js are the entry points of the module.

In this article, we will build a fully functional application using Seppia technology. We will make use of some open source products and show you how to glue them into a finished product.

But before we can do that, we need to take a step back and tell you a bit more about how Seppia works.

Quick Tour Of Seppia

The binary distribution of Seppia consists of the following directory layout:


+--- jars          // contains the jars to bootstrap seppia.
+--- modules       // contains the modules. Initially there
                       is only one module,  "org.Seppia.bootstrap"
+--- StartUp.class // the class to run seppia.

We begin by assuming that we have installed the latest version of Seppia into a folder called c:\myFirstSeppia. (Note: the instructions here assume you are a Windows user. This is mainly a convenience to keep the writing concise. It should be quite easy to adapt the paths in these instructions to other systems.)

To run Seppia, use the command:


c:\myFirstSeppia\> java -cp . StartUp

This produces the output:

Seppia successfully installed at URL file:/C:/myFirstSeppia/ 

The "successfully installed" message comes from the JavaScript StartUp.js in the module org.seppia.bootstrap. Seppia is hardcoded to look for the module org.seppia.bootstrap and execute StartUp.js. This means that StartUp.js is our first place to look. This is its original code:


function main()
{
   java.lang.System.out.println(
      "SEPPIA successfully installed at URL "+module.environment.url);
}

If you fancy editing the file, you can easily transform it into a "Hello World" Swing message:


var JFrame = Packages.javax.swing.JFrame;

function main()
{
    var frame = new JFrame("Hello World");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(360,80);
    frame.setVisible(true);
}

When you relaunch Seppia, you will see a window like Figure 1:

Figure 1
Figure 1. "Hello World" in Seppia

Before you go experimenting with your Seppia installation, it is important that we spell out the rules that Seppia sets to ensure a consistent design for any application. Read them carefully, as they should save you time by anticipating most of your likely questions.

Building a Sample Application

Once you understand the basics of using JavaScripts with Seppia, it is time to build something a little more challenging with it. The application we want to build is a simple GUI that provides a chart about the domains extension distribution of outlinks from any HTML page. The application should allow the user to type any URL, retrieve the web page, find the links in the HTML tags, and provide a chart of their distribution. We call this application Link-Vision. Figure 2 shows it in action.

Figure 2
Figure 2. Link-Vision querying the O'Reilly website

Decomposing The Application

The first step is to think how to design this application as separate components. Even without the help of an object-oriented guru, it seems obvious that the charting aspect of the application has nothing to do with finding the HTML links. It follows that we are going to have two modules:

We call these modules org.link-vision.html-analyzer and org.link-vision.chart, respectively, and prepare manually the folders for them.


myFirstSeppia
    +--- jars
    +--- modules
    |       +--- org.seppia.bootstrap
    |       |              +--- javascripts
    |       |                      +--- StartUp.js
    |       |
    |       +--- org.link-vision.html-analyzer
    |       |              +--- javascripts
    |       |              +--- jars
    |       |
    |       +--- org.link-vision.chart
    |                      +--- javascripts
    |                      +--- jars
    |
    +--- StartUp.class

The next step is to search for some existing products to reuse in our modules. This leads to the following two open source products:

Please note that at this point, while I do like these products, many alternatives are available. My choice was purely driven by the constraints of the sample application. Choosing the right tool to embed into your software system still remains a significant job. Only by reusing suitable, mature, tested products can we reduce the effort in creating new applications.

Once we have downloaded the HTML parser, we can copy htmlparser.jar from its lib directory into org.link-vision.html-analizer/jars. Similarly, for the JFreeChart we can copy jfreechart-0.9.21.jar and jcommon-0.9.6.jar (the code from JFreeChart relies upon the work in JCommon) into org.link-vision.chart/jars.

Building The HTML Parser

Now we can finally define the services provided by our modules by writing the appropriate JavaScripts. For org.link-vision.html-analyzer, we write the JavaScript LinkFinder.js to provide a function to extract the links from a URL.


var URL =    java.net.URL
var LinkTag= Packages.org.htmlparser.tags.LinkTag;
var Parser=  Packages.org.htmlparser.Parser;
var NodeClassFilter =
  Packages.org.htmlparser.filters.NodeClassFilter;

/*
This function returns a JavaScript object with
one method  that given a url string gives you an
array of URLs (outlinks from the given url)
*/
function main()
{
   var obj = new Object();
   obj.getLinks = getLinks;
   return obj;
}

function getLinks(url)
{
   var f = new NodeClassFilter(LinkTag);
   var parser = new Parser (url);
   var list = parser.extractAllNodesThatMatch(f);
   var array = [];
   for (var i = 0; i < list.size (); i++)
   {
      var linkTag =  list.elementAt(i);
      // skip if no http link.
      if (linkTag.isHTTPLink()==false) continue;
      var link = linkTag.extractLink();
      // skip any relative link.
      if (link =="" ) continue;
      array[array.length] = new URL(link);
   }
   return array;
}

Although this JavaScript makes usage of some unfamiliar classes from the HTMLParser API, it should be quite easy to read. The point here is that to the other modules, org.link-vision.html-analyzer is just a provider of a service to get the links from a URL. Its implementation is hidden between the JavaScript code and htmlparser.jar and could be easily changed or rewritten to use different .jars. Provided that the JavaScript object continues to return the function getURL (the only contract exposed by the module), its implementation is free to change.

It might not seem as powerful as we want to make it, but this feature effectively decouples major subsystems of your architecture from each other, which is crucial for building successful large systems.

Building The Chart

Let's move back to the other module. For org.link-vision.chart, we write a JavaScript, PieBuilder.js, that creates a JavaScript object with two methods:

Here's the implementation:


var JLabel =      Packages.javax.swing.JLabel;
var ImageIcon =   Packages.javax.swing.ImageIcon;
var ChartFactory =
  Packages.org.jfree.chart.ChartFactory;
var DefaultPieDataset =
  Packages.org.jfree.data.general.DefaultPieDataset;

var pieDataset_ = new DefaultPieDataset();

function main()
{
   pieDataSet_ = new DefaultPieDataset();
   obj = new Object();
   obj.createPair = createPair;
   obj.createGuiComponent = createGuiComponent;
   return obj;
}

function createPair(name, value)
{
   pieDataset_.setValue(name,value);
}

function createGuiComponent(title)
{
   var chart = ChartFactory.createPieChart(
                 title,pieDataset_,true,true,true);
   var image = chart.createBufferedImage(500,400);
   var lblChart = new JLabel();
   lblChart.setIcon(new ImageIcon(image));
   return lblChart;
}

Once again, despite the fact that there may be few classes unfamiliar to you, the code should be quite easy to read.

Gluing The Modules Together

Now that we have completed the two modules, we need a third one to combine them together. We call this module org.link-vision.application and write one JavaScript: Main.js.

Main.js uses the functionality of the two previous modules to create the Swing application. It is important to note that the module does not need additional .jars.


var HashMap =      java.util.HashMap;
var Integer =      java.lang.Integer;
var BorderLayout = java.awt.BorderLayout;
var ActionListener=java.awt.event.ActionListener;
var JLabel =       Packages.javax.swing.JLabel;
var JPanel =       Packages.javax.swing.JPanel;
var JFrame =       Packages.javax.swing.JFrame;
var JTextField =   Packages.javax.swing.JTextField;

function main()
{
   // creates the LinkFinder object.
   var linkFinder = run(
      "org.link-vision.html-analyzer",
      "LinkFinder");
   var panel = new JPanel(new BorderLayout());
   var txtURL = new JTextField(
      "http://www.java.sun.com");

   txtURL.addActionListener(function f()
   {
      // actionListener implementation.
      var url = txtURL.getText();
      var array = linkFinder.getLinks(url);
      var map = new HashMap();

      // loops thru all the urls updating
      // the map: extensionName --> count.
      for (var i=0;i<array.length;i++)
      {
         var host = array[i].getHost();
         var extension =
           host.substring(1+host.lastIndexOf('.'));
         if (map.containsKey(extension))
         {
            var t = new Integer(1+
               (map.get(extension)).intValue());
            map.put(extension,t);
         }
         else
         {
            map.put(extension,new Integer(1));
         }
      }

      // creates the pieBuilder object.
      var pieBuilder = run("org.link-vision.chart",
          "PieBuilder");
      var iterator = map.keySet().iterator();
      while (iterator.hasNext())
      {
         var ext = iterator.next();
         var hitsCount = map.get(ext);

         // creates the pairs.
         pieBuilder.createPair(ext,hitsCount);
      }

      // creates the component for the main panel
      var component=pieBuilder.createGuiComponent(
         "Domain Extensions Distribution");
      panel.removeAll();
      panel.add(component,BorderLayout.CENTER);
      panel.revalidate();
      panel.repaint();
   });

   var f = new JFrame("Link-Vision");
   f.getContentPane().add(txtURL,BorderLayout.NORTH);
   f.getContentPane().add(panel,BorderLayout.CENTER);
   f.setSize(510,470);
   f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   f.setVisible(true);
}

Now there is only one piece missing from this jigsaw puzzle: if you recall, Seppia starts up by executing the JavaScript StartUp.js in org.seppia.bootstrap. This JavaScript needs to be redirected to execute the JavaScript org.link-vision.application Main.js:

function main()
{
   run("org.link-vision.application","Main");
}

That's it--all done. If you have managed to follow all of the instructions up to this point, you should be able to run Link-Vision.

This is how the directory structure should look:


myFirstSeppia
   +--- jars   // the classes to bootstrap Seppia
   +--- modules
   |       +--- org.seppia.bootstrap
   |       |          +--- javascripts
   |       |                    +--- StartUp.js
   |       |
   |       +--- org.link-vision.application
   |       |          +--- javascripts
   |       |                    +--- Main.js
   |       |
   |       +--- org.link-vision.html-analyzer
   |       |          +--- javascripts
   |       |          |      +--- LinkFinder.js
   |       |          +--- jars
   |       |                 +--- htmlparser.jar
   |       |
   |       +--- org.link-vision.chart
   |              +--- javascripts
   |              |          +--- PieBuilder.js
   |              +-jars
   |                  +--- jfreechart-0.9.21.jar
   |                  +--- jcommon-0.9.6.jar
   |
   +--- StartUp.class


Related Reading

Jakarta Commons Cookbook
By Timothy M. O'Brien

Final Considerations

Having finished the application, you might wonder whether it was worth the effort. Of course, you could have written this application as a standard Java program, but if you had done so, you would have missed some of the benefits that come with the Seppia technology. Here's a short list of some of the most significant benefits:

Resources

Lorenzo Puccetti is a software developer/architect living and working in London, England.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.