Friday May 23, 2008

Command-line tools and the Java Module System

At JavaOne 2008, I gave a demonstration of using command line tools with the current JSR 277 implementation. Here I'll share that with the blogosphere's wider audience. (For the record, I also demo'd using VisualVM with JSR 277 modules, but a blog on that will have to wait until we've made that code available...it is not yet ready for prime time.)

The examples below show use of javac, jam, java, jrepo. They're based on the current JSR 277 code base, available via the Modules Mercurial repository. You can download the source code for the examples from jam-cli-demo.zip.

Please realize that JSR 277 and its implementation are a presently a moving target, and the sample code, commands, etc. below are all subject to change.

Source files

A modules-related example is really only interesting if there are two modules, one which imports the other. The example uses driver.Main and a lib.Library classes and puts them into correspondingly-named modules. Here's lib/src/lib/Library.java, which prints hello world and some information about the code's module definition and repository:
package lib;

import java.module.\*;

public class Library {
    public String greet() {
        String info = "";
        try {
            Repository r = Repository.getSystemRepository();
            ModuleDefinition md = r.find("lib");
            info += " from " + md.getName() + "-" + md.getVersion()
                + " in repository '" + r.getName()
                + "' at " + r.getSourceLocation();
        } catch (Throwable t) {
            // should not happen but ignore; it's just a demo anyway
        }
        return "hello, world" + info;
    }
}
Here's the associated lib/src/lib/module_info.java file:
package lib;

import java.lang.ModuleInfo.\*;
import java.module.annotation.\*;

@ImportModules({
    @ImportModule(name="java.se.core")
})
@Version("1.0")
class module_info {
    exports lib$Library;
}
Note the use of annotations to import the core Java platform, and to designate this module as version 1.0.

The "exports lib$Library;" merits explanation. For a class to be visible from one module to another, it must be exported. Our implementation currently requires us to do this "by hand". module_info is a real Java class: so is java.lang.ModuleInfo.exports, and lib$Library is a field of type exports in class lib.module_info. Ultimately, we expect that the set of classes exported by a module will be determined via the normal Java visibility rules, in conjunction with the new "module" keyword, and module developers won't have to explicitly list the exports as shown above. More on this when we discuss the jam tool.

Here's driver/src/driver/Main.java:

package driver;

import java.io.\*;
import lib.Library;

public class Main {
    public static void main(String[] args) {
        Library ly = new Library();
        System.out.println(ly.greet());
    }
}
And finally, the corresponding driver/src/driver/module_info.java:
package driver;

import java.module.annotation.\*;

@Version("1.0")
@ImportModules({
    @ImportModule(name="java.se.core"),
    @ImportModule(name="lib", version="[1.0,2.0)")
})
@MainClass("driver.Main")
class module_info {
}
You can readily see that the driver module depends on the lib module, in any version greater or equal to 1.0 and less than 2.0.

Compiling and creating JAM files

The rest of this discussion assumes you've built the modules sources and the resulting build is at $MJDK. With the current state of the modules source base, javac compilation is no different than what you're already used to. However, creating a JAM file is a little different than creating a JAR file:
(1) mkdir repo
(2) cd lib
(3) mkdir classes
(4) $MJDK/bin/javac -d classes src/lib/\*.java
(5) $MJDK/bin/jam cfsS ../repo/lib.jam lib classes -C classes lib
(6) cd ..
In (1), we create the directory which will serve as the source location for a LocalRepository. In (4) you can see ... nothing new. However line (5) is new, the jam tool. It is based on the jar tool, so the "cf" options are familiar. The new options are:
  • s: Specifies module name, in this case "lib".
  • S: Specifies the location of the module info file, in this case it is in the classes directory. (In case you're wondering about the choice of "s" and "S": In earlier times, we considered using the term "superpackage" instead of "module", and "s" made sense. We've not yet changed this, and as with other things in this blog entry, "s" and "S" are subject to change.)
The resulting lib.jam has this content:
META-INF/
META-INF/MANIFEST.MF
lib/
lib/Library.class
lib/module_info.class
MODULE-INF/
MODULE-INF/MODULE.METADATA
Where did MODULE.METADATA come from? Its content is based on module_info.java. The current implementation creates it by copying module_info.java. Eventually jam will examine its argument files, and build lists of member and exported classes. These lists will and the information in module_info.java will be put into MODULE.METADATA.

Compilation and jam'ing for the driver is similar:

(1) cd driver
(2) mkdir classes
(3) $MJDK/bin/javac -d classes -cp ../lib/classes src/driver/\*.java
(4) $MJDK/bin/jam cfsS ../repo/driver.jam driver classes -C classes driver
(5) cd ..
In (3), notice that we provide the classpath to the library's classes. In the fullness of time, we anticipate allowing something like this:
$MJDK/bin/javac -d classes -repository  ../repo src/driver/\*.java
We expect to more options for locating JAM files during compilation, similar to those allowed by the java launcher.

Running code in Java Modules

By now, you should have a directory named repo containing driver.jam and lib.jam. Lets run the driver:
$MJDK/bin/java -jam repo/driver.jam
which should print something like this:
hello, world from lib-1.0 in repository 'application' at file:/tmp/cli-demo/repo/
Or, you can specify a version constraint for the driver:
$MJDK/bin/java -module "driver:[1.0,2.0)" -repository repo
If no module matches the version constraint, you'll get an error message:
$MJDK/bin/java -module "driver:1.5;[2.0,3.0)" -repository repo
Error: Module not found: driver:1.5;[2.0,3.0)
The Java launcher has other modules-related options; see the Java Launcher section on our tools page for more information.

Using jrepo to work with repositories

The new JDK tool jrepo, described at the jrepo section of our tools page, lets you examine repository contents, as well as install and uninstall modules to/from repositories. This example's prior steps resulted in a directory repo with two JAMs, lib.jam and driver.jam. Here's how jrepo shows the repository's contents:
% $MJDK/bin/jrepo list -r repo
Repository file:/tmp/cli-demo/
Name                 Version             
lib                  1.0                 
driver               1.0  
With -v, jrepo gives more information:
% $MJDK/bin/jrepo list -v -r repo
Repository file:/tmp/cli-demo/repo/
Name                 Version              Platform  Arch    Modified          Filename
lib                  1.0                  generic   generic 5/22/08 1:52 PM   /tmp/cli-demo/repo/lib.jam
driver               1.0                  generic   generic 5/22/08 1:52 PM   /tmp/cli-demo/repo/driver.jam
Adding -p causes jrepo to show all contents of all repositories that are parents of the named repository:
% $MJDK/bin/jrepo list -v -p -r repo
Bootstrap repository
Name                 Version              Platform  Arch    Modified          Filename
java.se.core         1.7                  generic   generic n/a               n/a
corba                3.0                  generic   generic n/a               n/a
javax.xml            1.4                  generic   generic n/a               n/a
javax.xml.bind       2.0                  generic   generic n/a               n/a
javax.xml.ws         2.0                  generic   generic n/a               n/a
javax.xml.soap       1.3                  generic   generic n/a               n/a
javax.tools          1.0                  generic   generic n/a               n/a
javax.annotation.processing 1.0                  generic   generic n/a               n/a
javax.annotation     1.0                  generic   generic n/a               n/a
javax.script         1.0                  generic   generic n/a               n/a
java.se              1.7                  generic   generic n/a               n/a
java.classpath       1.7                  generic   generic n/a               n/a
Repository file:/tmp/cli-demo/repo/
Name                 Version              Platform  Arch    Modified          Filename
lib                  1.0                  generic   generic 5/22/08 1:52 PM   /tmp/cli-demo/repo/lib.jam
driver               1.0                  generic   generic 5/22/08 1:52 PM   /tmp/cli-demo/repo/driver.jam
We can remove a module:
% $MJDK/bin/jrepo uninstall -v -r repo lib
Uninstalled lib                  1.0                  generic   generic 5/22/08 1:52 PM   /tmp/cli-demo/repo/lib.jam
% $MJDK/bin/jrepo list -r repo
Repository file:/tmp/cli-demo/repo/
Name                 Version             
driver               1.0                 
And, if there's another repository into which we want to install a JAM, we can do that as well:
% mkdir repo2
% $MJDK/bin/jrepo install -v -r repo2 repo/driver.jam
Installed repo/driver.jam: driver               1.0                  generic   generic 5/22/08 2:02 PM   /tmp/cli-demo/repo/driver-1.0.jam
There's more work for the JSR 277 development team to do with respect to command line tools. Here's some of what's on our plate:
  • 6673594: (javac) ModuleFileManager for recognizing modules' import dependencies during compilation
  • 6674169: (dependency) Support JSR 277 in javac, javap, javah, and javadoc
  • 6674173: (dependency) Support JSR 277 in jrunscript
  • 6674167: (dependency) Support JSR 277 in rmic
  • 6584410: (jam) generate list of all classes and all public classes in embedded JAR files
  • 6590185: (jrepo) Installing a module via repository management tool should check its dependencies
  • 6593310: (jrepo) "list" command should sort repository contents
  • 6605077: (jrepo) add "dependencies" subcommand
  • 6605083: (jrepo) add "validate" subcommand
  • 6628249: (jrepo) provide information about services and providers that are in modules
Last but not least, please send suggestions if any command line tool functionality seems lacking.
About

daveb

Search

Categories
Archives
« May 2008
SunMonTueWedThuFriSat
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
24
25
26
27
28
29
30
31
       
Today