-
Bug
-
Resolution: Not an Issue
-
P4
-
None
-
9, 10
-
x86_64
-
linux_ubuntu
FULL PRODUCT VERSION :
java version "9.0.4"
Java(TM) SE Runtime Environment (build 9.0.4+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.4+11, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Linux renato 4.4.0-112-generic #135-Ubuntu SMP Fri Jan 19 11:48:36 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
A DESCRIPTION OF THE PROBLEM :
The java.lang.ClassLoader class has the methods getResource(String) and getResourceAsStream(String) to load resources from a module.
These methods load resources from the module's path according to rules specified in the Javadocs, which work as expected when the method call is made from code within the module the Class object is loaded in, but not when the code is run by code in a different module, even though the instance of ClassLoader is the same in both cases.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Create 2 simple modules in a file structure as follows (the contents of the files are in the "Source code" field):
.
├── mod1
│ ├── mod1
│ │ └── A.java
│ └── module-info.java
├── mod2
│ ├── mod2
│ │ └── Main.java
│ └── module-info.java
└── out2
└── mod2
└── index.html
Compile, jar, then run the code by running these commands:
javac -d out1 mod1/mod1/A.java mod1/module-info.java
jar --create -f mod1.jar -C out1 .
javac -p mod1.jar -d out2 mod2/mod2/Main.java mod2/module-info.java
jar --create -f mod2.jar -C out2 .
java -p mod1.jar:mod2.jar -m mod2/mod2.Main
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
This code should print the following (because using the same ClassLoader to call the same method, the result should always be the same regardless of where the method is called from):
Byte count: 6
Byte count: 6
ACTUAL -
The code prints the following:
NOT FOUND
Byte count: 6
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
===== mod1/module-info.java =====
module mod1 {
exports mod1;
}
=============================
==== mod1/mod1/A.java ====
package mod1;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
public class A {
public static Optional<byte[]> readResource( ClassLoader owner, String path )
throws IOException {
InputStream stream = owner.getResourceAsStream( path );
if ( stream == null ) {
return Optional.empty();
}
return Optional.of( stream.readAllBytes() );
}
}
=============================
==== mod2/module-info.java ====
module mod2 {
requires mod1;
}
=============================
==== mod2/mod2/Main.java ====
package mod2;
import mod1.A;
import java.io.IOException;
import java.util.Optional;
public class Main {
public static void main( String[] args ) throws IOException {
print( A.readResource( Main.class.getClassLoader(), "index.html" ) );
print( Optional.ofNullable( Main.class.getResourceAsStream( "index.html" ) )
.map( s -> {
try {
return s.readAllBytes();
} catch ( IOException e ) {
throw new RuntimeException( e );
}
} ) );
}
private static void print( Optional<byte[]> x ) {
System.out.println( x.map( b -> "Byte count: " + b.length ).orElse( "NOT FOUND" ) );
}
}
=============================
==== out2/mod2/index.html ====
hello
=============================
---------- END SOURCE ----------
java version "9.0.4"
Java(TM) SE Runtime Environment (build 9.0.4+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.4+11, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Linux renato 4.4.0-112-generic #135-Ubuntu SMP Fri Jan 19 11:48:36 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
A DESCRIPTION OF THE PROBLEM :
The java.lang.ClassLoader class has the methods getResource(String) and getResourceAsStream(String) to load resources from a module.
These methods load resources from the module's path according to rules specified in the Javadocs, which work as expected when the method call is made from code within the module the Class object is loaded in, but not when the code is run by code in a different module, even though the instance of ClassLoader is the same in both cases.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Create 2 simple modules in a file structure as follows (the contents of the files are in the "Source code" field):
.
├── mod1
│ ├── mod1
│ │ └── A.java
│ └── module-info.java
├── mod2
│ ├── mod2
│ │ └── Main.java
│ └── module-info.java
└── out2
└── mod2
└── index.html
Compile, jar, then run the code by running these commands:
javac -d out1 mod1/mod1/A.java mod1/module-info.java
jar --create -f mod1.jar -C out1 .
javac -p mod1.jar -d out2 mod2/mod2/Main.java mod2/module-info.java
jar --create -f mod2.jar -C out2 .
java -p mod1.jar:mod2.jar -m mod2/mod2.Main
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
This code should print the following (because using the same ClassLoader to call the same method, the result should always be the same regardless of where the method is called from):
Byte count: 6
Byte count: 6
ACTUAL -
The code prints the following:
NOT FOUND
Byte count: 6
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
===== mod1/module-info.java =====
module mod1 {
exports mod1;
}
=============================
==== mod1/mod1/A.java ====
package mod1;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
public class A {
public static Optional<byte[]> readResource( ClassLoader owner, String path )
throws IOException {
InputStream stream = owner.getResourceAsStream( path );
if ( stream == null ) {
return Optional.empty();
}
return Optional.of( stream.readAllBytes() );
}
}
=============================
==== mod2/module-info.java ====
module mod2 {
requires mod1;
}
=============================
==== mod2/mod2/Main.java ====
package mod2;
import mod1.A;
import java.io.IOException;
import java.util.Optional;
public class Main {
public static void main( String[] args ) throws IOException {
print( A.readResource( Main.class.getClassLoader(), "index.html" ) );
print( Optional.ofNullable( Main.class.getResourceAsStream( "index.html" ) )
.map( s -> {
try {
return s.readAllBytes();
} catch ( IOException e ) {
throw new RuntimeException( e );
}
} ) );
}
private static void print( Optional<byte[]> x ) {
System.out.println( x.map( b -> "Byte count: " + b.length ).orElse( "NOT FOUND" ) );
}
}
=============================
==== out2/mod2/index.html ====
hello
=============================
---------- END SOURCE ----------