Jigsaw EA feedback on running an internal app

Peter Levart peter.levart at gmail.com
Fri Sep 11 15:52:04 UTC 2015


Hi,

Excellent work and congratulations on finally bringing modules to Java!

I tried to run a medium-sized stand-alone server application with Jigsaw 
9.0 EA build and it seems to work fine after adding a single 
-XaddExports option. This app uses the following 3rd party 
libraries/components:

Hibernate 4.3
Jasper Reports 4.5
Jetty 6.1.5
JRules 6.7
Spring 4.2
Coherence 3.7
...and lots of other small libraries

All used libraries behave nicely. The 
-XaddExports:java.rmi/sun.rmi.server=ALL-UNNAMED was needed because of 
Oracle Coherence!

Building the app with Maven doesn't work though. Maven compiler module 
doesn't seem to be able to use the javac compiler API correctly. Maven 
folks would have to look at it.

Spotted an error in "The State of the Module System" document at: 
http://openjdk.java.net/projects/jigsaw/spec/sotms/ ... In the "Implied 
readability" section, it writes:

> The java.sql.Driver interface, in particular, declares the public method
>
> public Logger getParentLogger();
>
> where Logger is a type declared in the exported java.util.logging 
> package of the java.logging module.
>
> Suppose that code in the com.foo.app module invokes this method in 
> order to acquire a logger and log a message:
>
> String url = ...;
> Properties props = ...;
> Driver d = DriverManager.getDriver(url);
> Connection c = d.connect(url, props);
> d.getParentLogger().info("Connection acquired");
>
> If the com.foo.app module is declared as above then this will not 
> work: *The getParentLogger method is defined in the Logger class, 
> which is in the java.logging module, which is not read by the 
> com.foo.app module. That class is therefore inaccessible to code in 
> the com.foo.app module and so the invocation of the getParentLogger 
> method will fail, at both compile time and run time.*


The correct text should be, I think: "The getParentLogger method defined 
in Driver class has a signature with a return type of Logger class, 
which is in the java.logging module, which is not read by the 
com.foo.app module. That class is therefore inaccessible to code in the 
com.foo.app module and so the invocation of the .info method will fail, 
at both compile time and run time (in case the modules were successfully 
compiled together using legacy compiler mode and then their artifacts 
split into modules)".

The fact is that getParentLogger method can be invoked even though it's 
return type is in an unreadable module if the return type is not used in 
any way in such invocation. For example:

d.getParentLogger().toString();

(the above assumes that .toString() is declared in Object and not 
overridden in Logger). If it is overriden, it can be invoked 
nevertheless with the following trick:

((Object) d.getParentLogger()).toString().

The important thing is that Logger as a type must not be referenced in 
bytecodes if the bytecodes are in a module that doesn't read 
java.logging module.

Am I correct? I have experimented with the EA and this is my experience. 
Is the prototype working correctly in this respect?

Now just a thought on implied readability. While it is a convenient 
feature, it can be dangerous if not used correctly. I think a module 
should declare it's own dependencies (requires) for all types it 
references explicitly. In above example, it invokes method Loger.info() 
and therefore uses types from module java.logging explicitly.

A case where implied readability would be abused could be described in 
the following example:

- java.sql module uses java.logging internally and also exposes a method 
Driver.getParentLogger() which returns a Logger which is declared in 
java.logging module. Therefore java.sql requires *public* java.logging 
to imply the readability of java.logging for modules that require only 
java.sql and not java.logging explicitly.
- com.foo.internal module requires java.sql and only uses the part of 
it's API that doesn't need types from java.logging
- com.foo.internal also uses Logger internally, but fails to declare 
this. It nevertheless works since java.logging is implicitly readable to 
it through declaration in java.sql
- java.sql module decides to change it's internal use of logging API and 
instead of java.logging starts using org.apache.log4j. It now requires 
*public* org.apache.log4j instead of java.logging - an incompatible 
change, yes, but should not affect com.foo.internal that only uses the 
part of it's API that doesn't use types from java.logging
- com.foo.internal now fails because it still uses java.logging, but 
implicit readability of it is not provided by java.sql any more. If 
com.foo.internal declared explicitly a dependency on java.logging, such 
configuration would still work.

So while convenient, implied readability should be used with care.

Regards, Peter

P.S.

Here's a perl script one can find useful for parsing exception traces 
(from logs for example) and turning them into -XaddExports options that 
allow access to non-public APIs:

#!/bin/perl
my %exports = ();
while (<STDIN>) {
   chomp;
   if (/cannot access \S+ (\S+) \(in module: (.*?)\), (\S+) is not 
exported to (.*)/) {
     my $target_module = ($4 eq "Unnamed Module") ? "ALL-UNNAMED" : $4;
     my $source_module = $2;
     my $source_pkg = $3;
$exports{"-XaddExports:$source_module/$source_pkg=$target_module"}++;
   }
}
print join(" ", keys %exports) . "\n";





More information about the jigsaw-dev mailing list