The case for Java modularity

Two newer JSRs tackle an age-old problem

1 2 3 4 Page 2
Page 2 of 4

Introducing the Java modularity contenders

OSGi and the Eclipse plug-in frameworks are two of the more recent attempts to solve the problem of efficient module mechanisms in Java. Both of these technologies, however, have been created outside of the standard Java Specification Request (JSR) realm. JSR 277 (the Java Module System) and JSR 291 (Dynamic Component Support for Java SE) were introduced to bring Java modularity to the Java platform proper. Both are vying for inclusion in Java 7.

JSR 277 approaches the modularity problem from the perspective of the Java Runtime Environment (JRE). It defines a solution that provides Java API enhancements and specifies a distribution format, a versioning scheme, a module repository, and a set of support tools. JSR 277 is currently slated to be deployed with version 7 of the JDK, sometime in early 2009.

JSR 291 attempts to exploit the popularity of the OSGi system of modularity, which includes a service layer, a strict component lifecycle, a mechanism for exposing services from modules, and so on (see the Resources section to learn more about OSGi). JSR 291 aims to formally bring OSGi into the Java programming language. The dynamic component model underlying OSGi and JSR 291 supports the assembly of applications from components and implementation detail hiding between components, as well as the management of those components' lifecycle.

Similar concepts underlie JSR 277 and JSR 291, but there are significant differences between the two initiatives. For the remainder of this article, I'll focus on the high-level requirements essential to a modular programming system. You'll have the opportunity to see how each JSR does or does not implement these requirements.

Module definition

Each specification for Java modularity has a slightly different view of what a module actually is and how it should be defined.

JSR 291

JSR 291 defines a unit of modularization and execution as a bundle. A bundle is a JAR file comprised of Java classes, resources, and manifest headers. Among other things, a bundle can specify its own version, the packages it imports and exports, the exports it requires from other bundles, its target execution environments, and more.

JSR 277

Straightforwardly enough, JSR 277 defines a unit of modularization as a module. A module is metadata that is compiled and packaged together with classes and other resources as a deployment artifact known as a Java Module (JAM). A JAM file is a JAR file that contains a file named METADATA.module, which contains information about the module, including its name, version, and dependency imports, along with the classes and resources that the module exports.

Module identification

Identifying a module is fundamental to gaining access to it and its ancillary packages or features. Mechanisms for the identification of components in the Java programming space have always attempted to address problems such as namespace collision, and Java modularity is no exception.

JSR 291

A JSR 291 bundle is identified by an identifier, a location, or a symbolic name. An identifier is a number assigned to the bundle by the container when a bundle is installed. A bundle location is a name assigned to the bundle when it is installed that defines the location of the bundle; this would typically be a URL for the JAR file. A symbolic name is a fully qualified name assigned to the bundle by the developer that adheres to the naming constraints defined in the Java Language Specification.

JSR 277

A JSR 277 module is identified by a fully qualified name assigned to the module by the developer. This name must adhere to the naming constraints defined in the Java Language Specification.

Runtime loading and reloading

The loading and reloading or deployment and redeployment of a software component is an important concern, especially in enterprise systems where the uptime of a host container can be extremely precious. The Java platform has attempted to address this issue since it entered the enterprise space.

The Java classloader framework has continually dealt with restrictive issues centered on the loading and reloading of classes. In particular, Java classloading is a fragile balancing act, with developers needing to consider flexibility (loading new versions of a given class dynamically), class-definition footprint (perm space), and couplings between a class and its consumers.

JSR 291

In JSR 291, when an updated bundle is redeployed, an explicit call must be made to the PackageAdmin.refreshPackages() method to force the container to reconnect existing bundle clients with the newly updated bundle.

JSR 277

In JSR 277, when a module definition is updated and redeployed, the module is reloaded when the Repository.reload() call is invoked. However, support for this method on the part of the repository vendor is optional.

Distribution format

Java applications and components to be distributed in a number of typical ways:

  • In a JAR file, or in a file whose format derives from JAR (WAR, EAR, etc.)
  • As an applet
  • As a JNLP component

Each distribution format has its idiosyncrasies. In particular, building the runtime required to support each format has proved to be a tricky task in itself. The Java modularity specifications seek to address this problem from the outset.

JSR 291

In JSR 291, a bundle JAR file is the only artifact used for distribution and deployment. A bundle is a standard JAR file containing resources, classfiles, a manifest with bundle information, and other ancillary data, such as documentation, icons, and HTML files.

JSR 277

In JSR 277, a JAM file is the artifact used for distribution and deployment. The JAM file format is based on the JAR file format and contains classfiles, resources, and a METADATA.module file under the MODULE-INF path. The metadata includes information about the module, such as its dependencies or a resource export list. Listing 1 is an example of a METADATA.module file.

Listing 1. METADATA.module

METADATA.module:
(
   name=com.example.mymodule
   extensible-metadata=[@Version("2.0")]
   imports=
      [ImportModule(com.example.utils, @VersionConstraint("2.0+")),
       ImportModule(com.example.io, @VersionConstraint("3.0+"))]
   class-exports=[com.example.foo.Class1,
                  com.example.foo.Interface2]
   members=[com.example.foo.Class1,
            com.example.foo.Interface2,
            com.example.foo.Class3]
)

To provide a mechanism for encapsulating multiple packages in a single artifact, JSR 277 introduces the concept of a super package. A super package is a collection of packages declared along with the exported classes and interfaces for each package. Listing 2 demonstrates how to declare a super package in Java code.

Listing 2. Declaring a super package

Superpackage com.example.myservice
{
   // the packages that are part of this superpackage
   member package example.foo.myapp, example.foo.myapp.processing;

   // the list of exported classes and interfaces
   export example.foo.myapp.Main, example.foo.myapp.Helper;
}

A super package is compiled to bytecode. Access control is enforced by the compiler and the runtime JVM, just as it is for as classfiles. Details about a super package can be discovered using reflective API extensions that are slated to be added to the java.lang.Class class when JSR 277 is formally released.

1 2 3 4 Page 2
Page 2 of 4