Many challenges exist in software development: Projects must be developed quickly, yet be high-quality. For better code maintainability and application stability, they should utilize useful design patterns. Development projects must also integrate seamlessly with existing legacy systems while providing a path to more advanced technology, without the downtime or expense. And they must offer the freedom to choose how you implement and use new technology.

In the face of these challenges, the software development community has turned to application frameworks to guide development. Application frameworks allow consistency in software design and provide low-level services that developers can use (and reuse) to speed development. The right application framework can dramatically reduce software development and maintenance costs. By using application frameworks, software developers can achieve the reusability and quality object-oriented programming promises but seldom delivers in the real world.

Application frameworks are available for a variety of development environments and technologies, both commercial and open source. Though these frameworks prove useful, they don't always work cleanly with one another. For example, your development team may find three separate frameworks, one for user interfaces, one for persistence (datastores), and one for data encryption. Each has its own dependencies and its own frame wherein development should occur.

To use all three, extra work is required; code must be written to allow these different frameworks to work together. While the work is usually well worth it, it slows development and introduces a new layer of complexity—one that negates the benefits the three separate frameworks bring to application development. This extra code is also tightly coupled to your application frameworks and possibly to the application itself, reducing its reuse in other projects.

Keel technology

Keel addresses these issues. Keel technology is an open source meta-framework specification and implementation—a framework specifically designed for components and services of any type to work together. Just as an application framework provides guidelines and services for application development, Keel provides a consistent interface in which you can configure different application frameworks and immediately access them in your application development.

By providing this additional abstraction layer, you can use a variety of application frameworks to develop your application and not worry about how the frameworks interact and work together. Using Keel in the example above, the scenario changes in that instead of tying the three frameworks together using special code, you let Keel do that for you, thus reducing time to market and increasing your application's value.

Keel's core: Avalon

Keel is based on the Apache Avalon open source project. Avalon is an abstract set of design principles that allows components and services (a group of one or more components that provide a complete solution, like an application framework) of varying scale to be created, managed, and shared among applications written to use it. The two most important patterns are Inversion of Control and Separation of Concerns.

Inversion of Control is the concept that a component is externally managed. Everything a component needs in the way of configuration is provided by the piece of code that created it. Implementing this pattern creates secure component interaction in your system. Separation of Concerns is the idea of looking at your problem space from different viewpoints. Each viewpoint becomes an area of concern. For example, a Web server must be secure, stable, manageable, configurable, and meet the HTTP specification. Each of those attributes is a separate area of concern.

Keel is an extension of Avalon, providing structure, code, example applications, installation tools, deployment tools, documentation, and services so that it can tie multiple application frameworks together while taking advantage of the software design patterns that Avalon provides. Keel is also an open source project, which means its source base is shared and open for customization and improvement.

Keel benefits

Keel offers great adaptability to new technologies, giving you the ability to leverage a new technology's advantages without worrying about how the system will react to such a change. The coupling between your system and its underlying framework is an important consideration. When you develop your application using Keel, you use a single interface to get the services the separate application frameworks provide. This allows you to seamlessly change an underlying framework to a newer version or specification without breaking the application.

Keel technology is an open source specification for a meta-framework. As such, you have the option to use Keel's default implementation or you can create your own implementation. The default implementation includes a variety of out-of-the-box services and applications that can make Keel useful immediately. You can also modify the default implementation so that you can plug in your existing services. Since Keel is open source, you have the entire code base at your disposal.

Keel in the real world: Create a new Keel service

Let's walk through a particular scenario I previously encountered and discuss how I approached the problem within Keel. In a Web application project I was working on, the user interface had many forms with large text fields. As I worked the project, the client pointed out that these text fields needed to have some form of spell checking so that users could spell check their entries. The client insisted this was a requirement for acceptance and that a spellchecker must be provided for a successful project delivery.

It is common for a customer to discover requirements throughout a project's life. This is one of the reasons why it is important to have a flexible development framework in which new features can be added as necessary. In this case, I had a number of options. I could build a spell-check module from scratch, and then write some code that would allow me bring it into the application. However, since I was already using Keel, a more consistent approach with the development framework was to find an open source spell-check package (if one didn't exist, then possibly create one) and wrap it as a Keel service. That allowed me to integrate the spell-checking module into the application consistently and immediately gave me a feature in Keel that would be available for all future projects.

Having settled on this approach, I was ready to tackle the task. I broke the job into three parts:

  • Create the Service Interface
  • Create the service implementation component
  • Integrate the implementation component into Keel

All source code discussed in this article can be downloaded from the Resources section.

Keel Service interface

First, I needed to find an open source spell-check package that provided the functionality I needed. I went to Sourceforge.net and did a keyword search on Java spell check (all words) and found Jazzy. I downloaded the source and started working with it, running its default build and playing with the API. It was well designed, lightweight, and behaved as expected. After a few extensive tests and some code review, I decided it was good enough to use (I used Jazzy 0.5, last updated on September 4, 2003). I even refactored some of the source and submitted the modified code to the project administrators for inclusion in the project, illustrating the power of the open source model.

With the spell-check package to my liking, I proceeded to create a spell-check service for Keel. In creating the interface I tried not to be concerned with the implementation. My objective was to define an interface in Keel that described the functionality the spell-checking service should provide. I proceeded to create the SpellChecker interface, in the org.keel.services.spellcheck package.

One thing I would like to point out is the ROLE variable in this interface. This variable is the key used by a consumer of this interface to get an implementation within the Keel framework. You will see how this role goes into effect later.

Keel service implementation component

With the interface in place, I proceeded to write a default implementation that would be bundled with the interface. In preparation for this task, I created a directory named $KEEL_DIR/svc-spellcheck-jazzy ($KEEL_DIR is the installation directory for Keel) and subdirectories for source (src), configuration files (conf), and classes (build/classes).

I wrote a class called the DefaultSpellChecker, which not only implements the SpellChecker interface, but several other interfaces that provide additional functionality for Keel and for Jazzy. I implemented the Configurable interface, which allows the component to be configured at runtime through a configuration file ($KEEL_DIR/svc-spellcheck-jazzy/conf/system.xconf, discussed later). I implemented the LogEnabled interface, which provides a mechanism for logging messages to a central Keel logger. I implemented a Jazzy SpellCheckListener interface, which defines an event listener for the Jazzy API. Finally, I implemented the actual Keel SpellChecker interface to implement the necessary functionality.

Along with the implementation, I also needed to create some descriptor files to define the component to the Keel meta-framework. The files are named system.xconf and model-roles.xconf and are saved in the $KEEL_DIR/svc-spellcheck-jazzy/conf directory. The xconf files are loosely formatted XML files that define components to Keel. These files merge with other system.xconf and model-roles.xconf files (from other components) during the Keel build process.

model-roles.xconf:

<keel xmlns:excalibur-configuration="http://www.keelframework.org" 
  excalibur-configuration:merge="true">
  
  <role name="org.keel.services.spellcheck.SpellChecker">
    <component shorthand="spellchecker"
      class="org.keel.services.spellcheck.DefaultSpellChecker"
      handler="org.apache.excalibur.fortress.handler.
      FactoryComponentHandler"/>
  </role>
</keel>

system.xconf:

<keel xmlns:excalibur-configuration="http://www.keelframework.com"
  excalibur-configuration:merge="true">
    
  <spellchecker id="default" activation="request">
    <dictionary-list>
    <!-- list the dictionaries here; there must be at least one named
         "default" -->
      <dictionary>
        <name>default</name>
        <filename>
            /usr/local/jazzy/english.0
           </filename>
      </dictionary>
    </dictionary-list>
    <config-file>
      /usr/local/jazzy/configuration.properties
    </config-file>
  </spellchecker>
</keel>

The model-roles.xconf defines the role, and the system.xconf provides configuration to the role. In this case, I designed the spellchecker element to allow for a list of dictionaries to be passed in for spell checking, as well as the Jazzy configuration to use. These values could be retrieved through the Configurable interface's implemented methods. The spellchecker element's structure could be anything I wanted it to be, as long as the Configurable interface was implemented in a way that knew the structure and could pull the values.

Below is a snippet of code from DefaultSpellChecker that shows the implementation of the one method the Configurable interface defines:

/**
 * This method reads the container-provided configuration
 * 
 * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
 */
public void configure(Configuration configuration)
throws ConfigurationException {
  this.configuration = configuration;
  // Get dictionaries
  Configuration dicList = configuration.getChild("dictionary-list");
  Configuration[] confDics = dicList.getChildren();
  dics = new HashMap();
  for (int j = 0; j < confDics.length; j++) {
    dics.put(confDics[j].getChild("name").getValue(),
      confDics[j].getChild("filename").getValue());
  }
  // Get config filename
  this.configFilename = configuration.getChild("config-file").getValue();
  if (dics == null) {
    throw new ConfigurationException(
      "Dictionary was not set in configuration.");
  }
}

The Configuration object that gets passed in to the configure method contains the configuration structure found in system.xconf. I could easily traverse the tree to get what I needed and set those values at runtime. This is an extremely useful feature in that I could rely on XML formatting and be flexible in my configuration structure.

The final piece to the default implementation of the spell-check service was the Ant build script, $KEEL_DIR/svc-spellcheck-jazzy/build.xml. This was simple because Keel currently uses Ant 1.6. Below is the build file:

Page 2 of 2
<!--
 *******************************************************************
 *
 * Copyright (c) 2002-2004, The Keel Group, Ltd. All rights reserved.
 *
 * This software is made available under the terms of the license found
 * in the LICENSE file, included with this source code. The license can
 * also be found at: http://www.keelframework.org/LICENSE
 *
 *******************************************************************
-->
<!-- Ant build file for the Spell Check Service of the Keel meta-framework
     project -->
<project name="svc-spellcheck-jazzy" default="Usage" basedir="../keel-build">
  <property name="keelmodule.name" value="${ant.project.name}"/>
  <property name="keelmodule" value="${basedir}/../${keelmodule.name}"/>
  <import file="${basedir}/import/keel-build-svc.xml"/>
</project>

Together, the build script, configuration files, and interface implementation made up the default implementation component of the spellchecker service.

New component integration with Keel

I made the interface and the implementation component. Now I had to integrate it into Keel. To do this, I modified two Keel configuration files: $KEEL_DIR/keel-build/import/keel-iterate.xml and $KEEL_DIR/keel-build/dprop/default-deploy.properties.

First, I modified default-deploy.properties. I just scrolled down to where the service properties were listed and created a property for my service. As you will see later, I used this property to test whether I wanted to include a component in a build:

service.spellcheck.jazzy=true

I set the value to true, but in reality, the value doesn't matter. Keel only tests for whether a service's property exists, so if I wanted to omit the component in a future build, I must remove the property or comment it out.

With default-deploy.properties set, I moved on to keel-iterate.xml. I scrolled down to the section that lists services and added my service as an ant target to the list:

<target name="keel_iterate:svc-set">
  <antcall target="svc-aop-aspectwerkz"/>
  <antcall target="svc-authentication-config"/>
  <antcall target="svc-authentication-jndi"/>
  .
  . <!-- other services -->
  .
  <antcall target="svc-script-bsf"/>
  <antcall target="svc-script-jelly"/>
  <antcall target="svc-usergroup-persist"/>
  <antcall target="svc-spellcheck-jazzy"/>
</target>

Next I had to add that new target to the file, preferably with the other service ant targets:

<target name="svc-spellcheck-jazzy" if="service.spellcheck.jazzy">
  <moduleant name="svc-spellcheck-jazzy" if="service.spellcheck.jazzy"/>
</target>

This automatically called the build file in $KEEL_DIR/svc-spellcheck.jazzy to build in the component. Notice the if attribute. This says to only integrate the component if the service.spellcheck.jazzy property exists. As I mentioned earlier, Keel performs this check so that I can easily choose to include or omit the component in the Keel build, which is effective in narrowing down problems to specific components.

How the service is called in Keel

After I created the service, defined an implementation, and included it in Keel's build, I rebuilt Keel to include the service.

To call the service, I simply called the ModelRequest object's getService() method:

SpellChecker sc = 
    (SpellChecker)request.getService(
        SpellChecker.ROLE, "default");

With this object reference in my application, I could now call methods that I defined in my interface for the service and get results back based on the default implementation. This works because of the configuration files bundled with the implementation component, which defines the role for that particular service implementation.

Benefits of using Keel to spell check

In relatively short order, I have added a spell-checking service to my application through Keel. I can now proceed to meet the client's requirement by adding logic to use the service. However, the service is not only a part of this particular client's application, but also all my other applications that use Keel—because I created the service for Keel, not just for the particular project that called for it. Moreover, I submitted my service interface and implementation to the CVS (Concurrent Versions System) repository for Keel, so now all Keel users can also use this service, again an illustration of the power of open source.

Conclusion

Development projects require a great deal of investment—in time, money and resources. When the challenges of software development surface, there are many approaches on how to do it best. The waters of technology are volatile. When they get rough, a system must be quick to adapt and embrace the newer technologies, without making the same investments. Just as a strong keel is vital to a ship's stability, so is Keel technology vital to keeping your software systems agile and adaptable to the future's technological challenges.

Daniel Silva is a consultant for PlatinumSolutions, a Northern Virginia-based software engineering firm. Daniel is also a contributor to a variety of open source projects, including Keel.

Learn more about this topic