The case for Java modularity

Two newer JSRs tackle an age-old problem

1 2 3 4 Page 3
Page 3 of 4

Versioning

The Java classloading mechanism has been a thorn in the side of developers who need to deliver updated versions of their components, primarily because of the tight couplings between a class and its consumer. When a new class is loaded by the Java classloading framework, it is simply regarded by the host classloader as an entirely new class, and old versions of the class are retained due to associations with components holding existing references to the old versions. This creates a fragile environment for managing class versions. Java modularity seeks to solve this issue and make versioning a graceful process.

JSR 291

JSR 291 allows you to declare a version or a range of versions to be used as constraints for an import definition or an export definition. The format of a version in JSR 291 is defined as major.minor.micro-qualifier -- 1.6.1-b32 would be an example.

JSR 277

JSR 277 establishes a versioning scheme that is defined in the metadata of the module definition. This version information is used to define the version of the module itself as well as any constraints on its module dependencies. Dependencies can be constrained by a version, a version range, or both. The format of a version in JSR 277 is defined as major.minor.micro.update-qualifier -- 1.6.1.2-b32 would be an example. (This four-digit versioning scheme has been surprisingly controversial.)

Lifecycle

As modules are updated and reloaded by a host container, they must be gracefully stopped, terminated, and flushed from memory. Java modularity seeks to provide a lifecycle structure and provide information and functionality to keep a system robust and error free.

JSR 291

JSR 291 defines a strict lifecycle, with the following states:

  • Installed: The bundle has been installed successfully.
  • Resolved: All classes exposed by the bundle are available and the bundle is ready to be started.
  • Starting: The bundle is being started.
  • Active: The bundle has been started and is ready for use by a bundle client.
  • Stopping: The bundle is being stopped, and all resources related to the bundle will be deactivated, unregistered, and so on.
  • Stopped: The bundle has been stopped.
  • Uninstalled: The bundle has been uninstalled. Resources related to the bundle are made unavailable by the container framework.
  • Updated: The bundle has been modified.

A bundle starts its active life when its bundle activator is called. A bundle activator is an implementation of the BundleActivator interface, containing start and stop methods, and is identified by the Bundle-Activator manifest header.

JSR 277

The JSR 277 lifecycle is more loosely defined and is controlled by the containing repository. A module can be set to one of the following states by a repository:

  • New: The module is constructed but not initialized.
  • Preparing: The module is being prepared.
  • Validating: The module has been prepared and is being validated.
  • Resolved: The module has been prepared and validated, but not has not yet been initialized.
  • Ready: The module has been initialized and is ready to be used.
  • Error: The module is in an error state.

Execution context

A component or module typically executes in context to its host environment. For example, the resources you can access from a module depend on whether that module is executed as a standalone desktop application or as a component in a Web application, where filesystem access is more restricted.

JSR 291

In JSR 291, a bundle recognizes its execution context via a BundleContext object that is passed to the bundle by the containing framework. A JSR 291 framework passes a BundleContext object to a bundle's BundleActivator implementation during activation. The BundleContext object contains information about the framework and the JSR 291 service registry.

JSR 277

A module in JSR 277 is executed in the context of the JVM, just as a regular Java class is. Executable modules (that is, modules with a main class) are instantiated by an application launcher using the module's definition, which is retrieved from the module repository. Modules without a main class are loaded by a module classloader.

Inter-component relationships

The relationships between classes and modules in Java programming is a fundamental concern when considering things like method signatures, ancillary classes, package dependencies, and the like.

JSR 291

In JSR 291 the process of connecting (that is, wiring together) importer bundles and exporter bundles is referred to as resolving. During this wiring process, constraints are checked and enforced. This process is performed before the JVM loads or executes a bundle.

A wire is a link between an exporter bundle and an importer bundle. Bundles interconnected by wires form a graph. A bundle can only be resolved if all mandatory imports are wired and all required bundles are available and their exports wired.

JSR 291's service layer allows bundles to register service objects with the service registry. The service layer provides a mechanism for bundles to publish, find, and bind to each other's services. Bundles use services registered by other bundles by obtaining a service reference from the bundle and then passing this reference to the BundleContext object. The BundleContext then returns a service object to the calling bundle, which in turn becomes dependent upon the service object's lifecycle until the service object is released.

JSR 277

In JSR 277, a module's import declarations define that module's dependencies upon other modules. Imports are specified metadata of the module definition.

Linking a module together with its imports is known as resolving. A resolved module is a module instance that is interconnected with its imported modules. A mechanism known as an import policy is provided to allow each module to interconnect its imported modules. This mechanism relies on the module providing an instance of the ImportPolicy class to the module system.

Registries and repositories

Registration and discovery of components is a fundamental requirement of any system offering dynamic component technologies. It is of vital importance that consumers be able to find the proper component or module -- and the correct version of that component or module -- for a given task. Registries and repositories are typical solutions for this issue.

JSR 291

JSR 291 provides a service layer that exposes a service registry. In the service layer, bundles register service objects with that service registry. Other bundles can then obtain references to the service objects. Bundles can use a service reference to get the properties and call methods on the service.

JSR 277

JSR 277 defines the concept of a repository for storing, discovering, and retrieving module definitions in the module system. Just like the classloading system, a module system has one or more repositories. A bootstrap repository exposes the core platform modules and is the only mandatory repository in the module system; other possible repositories include a global repository, an application repository, or a URL repository.

As with classloading, repositories use a delegation model to locate module definitions. Each repository has a parent repository. When a repository is asked to find a module definition, it will delegate the request to its parent repository first and then handle the request itself if the parent can not.

1 2 3 4 Page 3
Page 3 of 4