I am playing with Jigsaw. I have a simple reproducible code.
package university.harvard;
public class Pilot{
public static void main(final String args[]){
callInNotAExportedPackage();
}
private static void callInNotAExportedPackage(){
try{
final Class<?>classy = Class.forName("javax.swing.JButton");
System.out.println(classy.newInstance());
}catch(final Exception e){
e.printStackTrace();
}
}
}
I have module-info.java like this.
module John{
exports university.harvard;
}
I can compile the module this this command.
C:\Ocp11>javac -d out --module-source-path src -m John
Note: src\John\Pilot.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
I'm getting messages about deprecation but the compilation is successful. At this point I say well the compiler doesn't know I will try to call a class in not a exported package.
And when I run the module
C:\Ocp11>java -p out -m John/university.harvard.Pilot
I can see that the instance is being retrieved by reflection with not a problem.
javax.swing.JButton[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.5,border=javax.
swing.plaf.BorderUIResource$CompoundBorderUIResource@3498ed,flags=296,maximumSiz
e=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,
margin=javax.swing.plaf.InsetsUIResource[top=2,left=14,bottom=2,right=14],paintB
order=true,paintFocus=true,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rollo
verSelectedIcon=,selectedIcon=,text=,defaultCapable=true]
But what is this? I thought that Jigsaw would block me
If I put the fully qualified class name in the code like this.
final Class<?>classy = Class.forName("javax.swing.JButton");
final javax.swing.JButton button = (javax.swing.JButton)classy.newInstance();
System.out.println(button);
And this time Jigsaw reacts correctly.
C:\Ocp11>javac -d out --module-source-path src -m John
src\John\Pilot.java:10: error: package javax.swing is not visible
final javax.swing.JButton button = (javax.swing.JButton)classy.newIn
stance();
^
(package javax.swing is declared in module java.desktop, but module John does
not read it)
src\John\Pilot.java:10: error: package javax.swing is not visible
final javax.swing.JButton button = (javax.swing.JButton)classy.newIn
stance();
^
(package javax.swing is declared in module java.desktop, but module John does
not read it)
Note: src\John\Pilot.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
2 errors
But then I don't put the fully qualified class name I can bypass Jigsaw.
I know that there is not much I can do without a reference to it but I think is some strange that Jigsaw allow me to do so.
I'm using
C:\Ocp11>java --version
java 14 2020-03-17
Java(TM) SE Runtime Environment (build 14+36-1461)
Java HotSpot(TM) 64-Bit Server VM (build 14+36-1461, mixed mode, sharing)
The question's title says
Java 14 Jigsaw reflection to a class in not a Exported package is not denying access
But actually, the class you're trying to access is in an exported package. The class javax.swing.JButton
is in package javax.swing
of module java.desktop
and that package is exported:
// [License & Javadoc]
module java.desktop {
// [other exports and requires]
exports javax.swing;
// [other exports, opens, uses and provides]
}
As the module is available at runtime, Class.forName("javax.swing.JButton")
will not throw an exception (otherwise it would throw a java.lang.ClassNotFoundException
). It probably is available, because it is a root module (see here and here).
Even if the class would not be in an exported package this would work:
Class<?> aClass = Class.forName("sun.java2d.marlin.Curve"); // will compile and run
However, (javax.swing.JButton)classy.newInstance();
will not compile — not because the package is not exported, but because your module, John
, does not read it. For this to work, your module would a requires java.desktop;
like this:
module John {
requires java.desktop;
exports university.harvard;
}
This way the module John
would be able to read all of java.desktop
's exported packages.
Calling classy.newInstance();
(without the type cast) will compile because the compiler does not know the type of the new instance.
Here are some examples what will work at runtime and compile time and why (not):
// (1) will compile and run:
Class<?> curveClass = Class.forName("sun.java2d.marlin.Curve");
// (2) will compile but not run, even if the module `requires java.desktop;`:
Object curveObject = curveClass.newInstance();
// (3) will not compile and not run, even if the module has `requires java.desktop;`:
sun.java2d.marlin.Curve curve = (sun.java2d.marlin.Curve) curveClass.newInstance();
// (4) will compile and run:
Class<?> jButtonClass = Class.forName("javax.swing.JButton");
// (5) will compile and run, even if the module does not have `requires java.desktop;`:
Object jButtonObject = jButtonClass.newInstance();
// (6) will only compile if the module `requires java.desktop;`:
javax.swing.JButton jButton = (javax.swing.JButton) jButtonClass.newInstance();
sun.java2d.marlin.Curve
. At runtime however, the access will be blocked.requires java.desktop;
because the compiler knows the type