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