ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Using the Jakarta Commons, Part 1

by Vikram Goyal
06/25/2003

The Jakarta Commons is a Jakarta subproject that creates and maintains independent packages unrelated to any other framework or product. The packages are a collection of components that serve small, useful purposes in their own right, and are usually server-centric.

The Commons project is divided into two parts: the Sandbox and the Commons repository. The Sandbox is a test bed for trying out ideas by the Jakarta committers. This article explains the components that make up the repository. It will show you when to use a component in each repository, where to get it, and how to use it with a basic example.

Introduction

Reusability is the name of the game for the Jakarta Commons project. Packages that form part of this project are conceived with the aim of making them reusable. Some of them, like the commons logging package, were developed for other projects, such as Jakarta Struts. When the committers saw how useful this package could be to other projects and realized that other packages could cut across project lines, they decided to form a "common" place for all such packages. This is the Jakarta Commons project.

Related Reading

Java Enterprise Best Practices
By The O'Reilly Java Authors

To be really reusable, each package needs to be independent of any other bigger framework or project. Thus, each package in the Commons project is largely independent, not only from other projects, but mostly of other packages as well. Deviations exist, but mostly to the extent of using well-set APIs. For example, the Betwixt package depends on the use of XML APIs. The primary aim though, is for these packages to work straight out of the box using a set of well-defined interfaces.

The brevity of most packages, however, has led to a brevity of documentation, poor maintenance, and lack of support. Some suffer from incorrect links and very sparse documentation. With most packages, you are left to figure out for yourself how to use them or, in some cases, why to use them. Hopefully this article will answer some of those questions.

Note: Jakarta Commons is different from Apache Commons. The latter is a top-level project of the Apache Software Foundation. The former is a subproject of the top-level Jakarta Project, and the subject of this article. Furthermore, Jakarta Commons is solely based on the Java language. In this article, whenever I say Commons, I am referring to Jakarta Commons.

Components

For organizational reasons, I have divided the 18 production-ready (I have excluded EL, Latka, and Jexl, for now) components of the Commons project into five categories. The table below lists these.

Component CategoryComponents
Web-related FileUpload, HTTPClient, and Net
XML-related Betwixt, Digester, Jelly, and JXPath
Utilities BeanUtils, Logging, DBCP, Pool, and Validator
Packages Codec and Modeler
Trivial CLI, Discovery, Lang, and Collections

Note that this organization is only for the purpose of this article. No such organization actually exists within the Commons project. The boundaries of these categories overlap to a certain degree. In this article, I will cover the Web-related and the Trivial categories; my next article will cover the XML-related and Packages categories. The final article will describe the components in the Utilities category.

Trivial

Source Code

Download commons1src.zip for the example applications.

The reason the CLI, Discovery, Lang, and Collections packages are categorized as Trivial is because they each serve a very small, yet very useful purpose.

1. CLI

Summary: CLI (Command Line Interface) provides a consistent interface for accessing and parsing the command-line parameters from within your Java program.

Where: Main Page, Binaries, Source.

When: When you want to use a consistent way of accessing and specifying command-line parameters.

Example Application: CLIDemo.java; needs commons-cli-1.0.jar in the CLASSPATH.

Description:

How many times have you written a Java application and had had to rewrite a new way of specifying the input parameters to your application? Wouldn't it be nice if there were one single interface to the way you define the input parameters (mandatory vs. numbers vs. Boolean, etc.), parse these parameters (according to a set of rules), interrogate, and decide the path that your application will take? CLI is the answer.

In CLI, each parameter that you want to specify on the command line is an Option. Create an Options object, add individual Option objects to it, then use the CLI-supplied methods to parse the user's input parameters. An Option might require the user to input a value as well; for example, if the name of a file is required. In such a case, the Option must be created where this is explicitly specified.

These are the steps towards using CLI:

  1. Create your Options:

    Options options = new Options();
    options.addOption("t", false, "current time");
  2. Create a Parser, and parse your input:

    CommandLineParser parser = new BasicParser();
    CommandLine cmd;
    try {
    	cmd = parser.parse(options, args); 
    } catch (ParseException pe) {
    	usage(options);
    	return;
    }
  3. Based on what the user has entered, take the relevant actions in your code:

    if (cmd.hasOption("n")) {
    	System.err.println("Nice to meet you: " + cmd.getOptionValue('n'));
    }

That is almost all there is to using CLI. Of course, there are other advanced options that give you control over various formats and parsers, but the basic idea remains the same. Look at the demo application for a complete example.

2. Discovery

Summary: An implementation of the discovery pattern, where you can use a consistent way of locating and instantiating your classes and other resources.

Where: Main Page, Binaries, Source. All code is in pre-release mode.

When: When you want to use best-practice algorithms for locating implementations of Java interfaces in your code.

Example Applications: DiscoveryDemo.java, MyInterface.java, MyImpl1.java, MyImpl2.java, MyInterface. Requires commons-discovery.jar and commons-logging.jar in the CLASSPATH.

Description:

Discovery is an attempt to locate all known implementations of an interface using best-practice algorithms. As a user of services, this is particularly useful in cases where you want to locate all known service providers for the service that you are trying to access.

Consider the case where you write an interface for a certain difficult task. All implementations of this interface would have code written in a unique way to achieve this difficult task. This would give the actual end user a variety of choices for actually doing this task. How would he know what implementations of your interface are available on his system?

The scenario that I have painted is the Service and Service Provider architecture. The Service is promised by your interface. The Service Providers provide the implementations of your service. The end user now needs to discover which Service Providers are actually installed. The Discovery component helps in doing this by a variety of means. Note that Discovery is not just used for discovering implementing classes, but also for locating resources, such as images and other files. In this it follows the rules set out in the Service Provider Architecture specified by Sun.

As such, it is really simple to use Discovery. See the example application, the associated MyInterface, and the Implementing classes MyImpl1 and MyImpl2 for details. You will also need the MyInterface file, which should be in the META-INF/services directory. Note that the name of this file corresponds to the fully qualified name of your interface. If your interface is in a package structure, the name of this file should change accordingly.

  1. Use the supplied ClassLoaders:

    ClassLoaders loaders =
    	ClassLoaders.getAppLoaders(MyInterface.class, getClass(), false);
  2. DiscoverClass is used to find our implementing classes:

    DiscoverClass discover = new DiscoverClass(loaders);
  3. Find the implementing class:

    Class implClass = discover.find(MyInterface.class);
    System.err.println("Implementing Provider: " + implClass.getName());

When you run the above code (DiscoveryDemo.java), you will get the class that you have registered in the MyInterface file, as shown below. Again, note that if your implementation is in a package structure, the name here should reflect that. If you don't have this file in the specified place, or if the name of your implementing class cannot be instantiated or located, you will get a DiscoverException stating that no implementation can be found for MyInterface.

MyImpl2 # Implementation 2

Of course, this is not the only way that you can register your implementing classes, otherwise there would be no use for Discovery! In fact, this way is the last way in which classes are discovered by Discovery's internal class-finding mechanism. Other preferred ways include passing the implementing class names in either the system properties or user-defined properties. For example, get rid of the file in META-INF/services and type the following to run this demo. You will get the same results as before. The system property in this case is the name of our interface, and the value is the provider for this interface.

$ java -DMyInterface=MyImpl1 DiscoveryDemo

Discovery can also be used to create (singleton) instances of your service providers and invoke methods on them. To do so, use the following syntax:

((MyInterface)discover.newInstance(MyInterface.class)).myMethod();

Note that we do not know at this stage which service provider is actually going to implement myMethod, nor do we care. Based on how you run this code and who you have registered as the service provider, you will get different implementations of the above method.

Pages: 1, 2, 3

Next Pagearrow