The Source for Java Technology Collaboration
User: Password:



Mandy Chung's Blog

July 2008 Archives


Support for the module keyword

Posted by mandychung on July 25, 2008 at 12:10 PM | Permalink | Comments (0)

Very soon, in a week or two, the Java compiler (javac), packaging tool (jam), and the Java module system implementation in the OpenJDK Modules project will support the new module keyword (the language support for JSR 277).

The engineering team have built a special workaround to enable ourselves to build the Java module system while waiting for the language support. With the javac and jam tool support (thanks to Jonathan Gibbons and Kumar Srinivasan), you can now experience developing, building and packaging of a JAM module as JSR 277 spec defines.

The language support for JSR 277 consists of two parts: module membership and module accessibility. The module accessibility support requires both the javac and JVM support which will go in next (stay tuned!). This blog is to help you get started to build and play with JAM modules using the JDK built from the Modules project with the module membership support.

Module Membership Declaration

There are two different ways to use the module keyword to declare module membership:

1) To declare module membership in the individual source file of a class (recommended):


// hello/HelloWorld.java
module hello;
package hello;

public class HelloWorld {
    ....
}

2) To declare module membership in package-info.java:


// hello/package-info.java
module hello;
package hello;

The module hello; declaration in a source file of a class applies the module membership to that particular class whereas the same module declaration in package-info.java will apply the module membership to all classes in the "hello" package.

Compile a module

Since the module keyword is a new feature, you will need to run javac with -source 7 option:

$ javac -source 7 -target 7 hello/*.java

Module Compilation Unit

A file called module-info.java is used to express the version, import dependency as well as other metadata about the module using annotations (java.module.annotation.*). Following the convention of package-info.java, this module-info.java could be stored in a directory corresponding to the module name.

This module-info.java will be used by the jam tool to build a JAM module. For example, my hello.HelloWorld application depends on two libraries (com.foo.OrderProcessingService and com.bar.PrintService) that will be expressed in the module-info.java file as follows:


// hello/module-info.java
@Version("1.0")
@ImportModules({
    @ImportModule(name="java.se"),
    @ImportModule(name="com.foo.OrderProcessingService"),
    @ImportModule(name="com.bar.PrintService")
})
@MainClass("hello.HelloWorld")
module hello;

The @MainClass annotation specifies the main entry point of the application.

You can download this sample aplication containing the hello, com.foo.OrderProcessingService, and com.bar.PrintService modules and try it out.

Package a JAM Module

The JDK provides a new JAM module packaging tool called "jam". To build the hello module:

$ jam cf hello.jam hello/module-info.class hello/HelloWorld.class

The jam command is similar to the jar command. The jam tool analyzes the list of input files and the module-info.class and generates the JAM module metadata (META-INF/MANIFEST.MF entry in the jam file). This metadata will be used by the Java Module System at runtime to enforce the versioning constraint and resolve the import dependency.

To build the other two JAM modules provided in the sample application,

$ jam cf com.foo.OrderProcessingService-1.0.jam com/foo/*.class
$ jam cf com.bar.PrintService-1.0.jam com/bar/*.class

Launch a Module Application

Once you build the JAM modules, you can run the module application as follows:

$ java -jam hello.jam

The -jam option is similar to the -jar option but launching a JAM module. Another way to launch the application:

$ java -repository . -module hello

The -repository option specifies the source location of the JAM modules and the -module option specifies the module name containing the main entry point.

Exercises

There are few things you can try out:


  • Modify com.bar.PrintService to access FooOrderImpl. It should fail. FooOrderImpl is a package-private class which is not exported in the com.foo.OrderProcessingService module and thus is not accessible outside its own module.

  • Add a new isPaid() method in the com.foo.Order interface. Modify the sample application to use the new version as follows:

    1. Modify com.bar.PrintService to call the new Order.isPaid() method and print the value

    2. Modify com.foo.FooOrderImpl to implement the new Order.isPaid() method

    3. Modify com/foo/module-info.java to a new version "2.0"

    4. Modify com/bar/module-info.java to a new version "2.0" and import com.foo.OrderProcessingService with version="2.0+"

    5. Build the JAM files com.foo.OrderProcessingService-2.0.jam and com.bar.PrintService-2.0.jam


    Running java -jam hello.jam should continue to work with the new 2.0 version of those two libraries (i.e. printing out the value returned from the new isPaid() method).

  • Modify hello/module-info.java to specify the version of the imported modules as follows:

    // hello/module-info.java
    @Version("1.1")
    @ImportModules({
        @ImportModule(name="java.se"),
        @ImportModule(name="com.foo.OrderProcessingService", version="1.0"),
        @ImportModule(name="com.bar.PrintService", version="1.0")
    })
    @MainClass("hello.HelloWorld")
    module hello;

    You can build a jam file called "hello-1.1.jam" and run it as:

    $ java -repository . -module hello:1.1

    This should use the 1.0 version of the OrderProcessingService and the PrintService.


Let us know what you think.

Feedback and Questions

Please send your feedback and questions to modules-dev@openjdk.java.net. We are working to post a JDK binary built from the Modules project once the module membership change is integrated (stay tuned).



OSGi Repository for Java Module System

Posted by mandychung on July 24, 2008 at 08:57 AM | Permalink | Comments (0)

The draft specification for supporting OSGi bundles in the Java Module System is currently under discussion in the JSR 277 Expert Group.

To enable the EG and the community to provide feedback, we have included an OSGi repository prototype in the OpenJDK Modules project. The prototype currently supports only Apache Felix OSGi implementation for proof-of-concept. There are open issues to be resolved. The out-of-the-box support is yet to be designed and implemented. Supporting other OSGi implementations is to be investigated. We hope that this prototype enables the community to begin participating and provide feedback for OSGi interoperability support.

We are working on posting a JDK binary built from the modules-dev project to make it ready for you to try out and experiment the JAM modules development as well as the OSGi support. In the meantime, you can clone the source from the Modules mercurial repository and build the JDK.

The OSGi repository for the Java Module System

The OSGi repository is packaged as <JAVA_HOME>/lib/osgi-repo.jar. To build the JDK with the OSGi repository, do the following (refer to the OpenJDK build instructions for details):


gnumake OSGI_FRAMEWORK_JAR=<pathname of the jar file for the OSGi core API> \
FELIX_JAR=<pathname of felix.jar> all images

Note: We will remove the build dependency of the jar files for the OSGi core API and Felix in a future implementation.

Setting up the OSGi repository

The modules-dev implementation provides a repository configuration file (<JAVA_HOME>/lib/module/repository.properties) to set up the repositories created at VM startup. To set up the OSGi repository:

  1. Download the Apache Felix OSGi implementation.
  2. Uncomment the following lines in the repository.properties and configure the "osgi.container" property to the pathname of felix.jar and the "osgi.source" property to the desired source location for OSGi bundles:
       osgi.parent=user
       osgi.container=file:///<URI of felix.jar>
       osgi.source=file:///<source location where OSGi bundles are loaded from>
    

You can either update the <JAVA_HOME>/lib/module/repository.properties or override the repository properties file by specifying this system property in the command line:


-Djava.module.repository.properties.file=<pathname of the repositories.properties file>

Known bug: The VM does not exit when the Felix container has been started unless System.exit() is explicitly. The OSGi repository starts the Felix container during initialization which started a non-daemon thread ("FelixDispatchQueue" thread). Since the FelixDispatchQueue thread is still running, the program will not terminate.

OSGi Repository Initialization

When the OSGi repository is being initialized during the VM initialization, it will first load the configuration properties file that is "conf/config.properties" under the parent directory of the directory where felix.jar is located; if it doesn't exist, the default configuration properties will be loaded. Then it will load and start the Felix OSGi framework specified in the "osgi.container" property. All bundles in the source location of the OSGi repository will be automatically installed and started as the OSGi repository initialization.

To start Felix, the bundle cache profile name or directory is required to be specified to indicate where the installed bundles will be cached. If there is no config.properties set up, you can set this system property at VM startup:


-Djava.module.osgi.repository.cache=<bundle cache profile directory name>

Develop a JAM module to use an OSGi bundle

A JAM module that imports an OSGi bundle expresses its dependency in the same way as importing a JAM module. For example, each JAM module has a module-info.java file to express its module name, version, and its import dependency.

// hello/module-info.java
@Version("2.0")
@ImportModules({
   @ImportModule(name="java.se"),
   @ImportModule(name="com.foo.xml", version="[1.0, 3.0)"),
   @ImportModule(name="com.bar.parser", version="2.0+")
})
@MainClass("hello.Main");
module hello;

This "hello" module imports two modules, "com.foo.xml" and "com.bar.parser" and they are specified in the @ImportModule annotation regardless of whether they are OSGi bundles or JAM modules.

Say "com.foo.xml" is an OSGi bundle whereas "com.bar.parser" is a JAM module. To successfully resolve the imports, we will need to configure the OSGi repository as an ancestor of the repository for the "hello" module. The source location of the OSGi repository is where the "com.foo.xml" and other OSGi bundles are located.

To launch this "hello" module application, it can be as simple as follows:


<JAVA_HOME>/bin/java -Djava.module.repository.properties.file=<repository.properties with osgi set up> \
-Djava.module.osgi.repository.cache=cache \
-jam hello.jam

You can refer to the documentation in the Modules project for how to create and launch a JAM module in details.

Questions and Feedback

Please send your questions and feedback to modules-dev@openjdk.java.net.

[Comment is closed for this blog because I will not be able to respond to any blog comment the next few weeks (on vacation).]





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds