diff --git a/make/test/JtregNativeJdk.gmk b/make/test/JtregNativeJdk.gmk index 89cc94f90bf..2ec0be639f9 100644 --- a/make/test/JtregNativeJdk.gmk +++ b/make/test/JtregNativeJdk.gmk @@ -65,6 +65,7 @@ ifeq ($(call isTargetOs, windows), true) BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeNullCallerClassLoaderTest := jvm.lib BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeNullCallerLookupTest := jvm.lib BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeNullCallerResourceBundle := jvm.lib + BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeNullCallerGetResource := jvm.lib BUILD_JDK_JTREG_EXECUTABLES_LIBS_exerevokeall := advapi32.lib BUILD_JDK_JTREG_LIBRARIES_CFLAGS_libAsyncStackWalk := /EHsc BUILD_JDK_JTREG_LIBRARIES_CFLAGS_libAsyncInvokers := /EHsc @@ -86,6 +87,7 @@ else BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeNullCallerClassLoaderTest := -ljvm BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeNullCallerLookupTest := -ljvm BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeNullCallerResourceBundle := -ljvm + BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeNullCallerGetResource := -ljvm endif ifeq ($(call isTargetOs, macosx), true) diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index a55f4ced309..b9c711b7451 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -3002,9 +3002,9 @@ public final class Class<T> implements java.io.Serializable, if (callerModule != thisModule) { String pn = Resources.toPackageName(name); if (thisModule.getDescriptor().packages().contains(pn)) { - if (callerModule == null && !thisModule.isOpen(pn)) { - // no caller, package not open - return false; + if (callerModule == null) { + // no caller, return true if the package is open to all modules + return thisModule.isOpen(pn); } if (!thisModule.isOpen(pn, callerModule)) { // package not open to caller diff --git a/src/java.base/share/classes/java/lang/Module.java b/src/java.base/share/classes/java/lang/Module.java index 01540fb73e1..2f315b42cfc 100644 --- a/src/java.base/share/classes/java/lang/Module.java +++ b/src/java.base/share/classes/java/lang/Module.java @@ -1653,11 +1653,11 @@ public final class Module implements AnnotatedElement { if (caller != this && caller != Object.class.getModule()) { String pn = Resources.toPackageName(name); if (getPackages().contains(pn)) { - if (caller == null && !isOpen(pn)) { - // no caller, package not open - return null; - } - if (!isOpen(pn, caller)) { + if (caller == null) { + if (!isOpen(pn)) { + return null; + } + } else if (!isOpen(pn, caller)) { // package not open to caller return null; } diff --git a/test/jdk/java/lang/module/exeNullCallerGetResource/NullCallerGetResource.java b/test/jdk/java/lang/module/exeNullCallerGetResource/NullCallerGetResource.java new file mode 100644 index 00000000000..d1acea9d35b --- /dev/null +++ b/test/jdk/java/lang/module/exeNullCallerGetResource/NullCallerGetResource.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/** + * @test + * @bug 8281006 + * @summary Test uses custom launcher that starts VM using JNI that verifies + * Module::getResourceAsStream and Class::getResourceAsStream with + * a null caller class functions properly. + * @library /test/lib + * @modules java.base/jdk.internal.module + * jdk.compiler + * @build NullCallerGetResource + * jdk.test.lib.compiler.CompilerUtils + * @requires os.family != "aix" + * @run main/native NullCallerGetResource + */ + +// Test disabled on AIX since we cannot invoke the JVM on the primordial thread. + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import jdk.test.lib.Platform; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.compiler.CompilerUtils; + +public class NullCallerGetResource { + + private static final String TEST_SRC = System.getProperty("test.src"); + + // the module name of the test module + private static final String TEST_MODULE = "n"; + + private static final Path SRC_DIR = Paths.get(TEST_SRC, "src"); + private static final Path MODS_DIR = Paths.get("mods"); + private static final Path TEST_MOD_DIR = MODS_DIR.resolve(TEST_MODULE); + + /* + * Build the test module called 'n' which opens the package 'open' + * to everyone. There is also a package 'closed' which is neither + * opened or exported. + */ + static void compileTestModule() throws Exception { + // javac -d mods/$TESTMODULE src/$TESTMODULE/** + boolean compiled + = CompilerUtils.compile(SRC_DIR.resolve(TEST_MODULE), TEST_MOD_DIR); + assert (compiled); + var open = TEST_MOD_DIR.resolve("open/test.txt"); + try (var out = Files.newBufferedWriter(open)) { + out.write("open"); + out.flush(); + } + var closed = TEST_MOD_DIR.resolve("closed/test.txt"); + try (var out = Files.newBufferedWriter(closed)) { + out.write("closed"); + out.flush(); + } + } + + public static void main(String[] args) throws Exception { + + // build the module used for the test + compileTestModule(); + + var launcher = Path.of(System.getProperty("test.nativepath"), "NullCallerGetResource"); + var pb = new ProcessBuilder(launcher.toString()); + var env = pb.environment(); + + var libDir = Platform.libDir().toString(); + var vmDir = Platform.jvmLibDir().toString(); + + // set up shared library path + var sharedLibraryPathEnvName = Platform.sharedLibraryPathVariableName(); + env.compute(sharedLibraryPathEnvName, + (k, v) -> (v == null) ? libDir : v + File.pathSeparator + libDir); + env.compute(sharedLibraryPathEnvName, + (k, v) -> (v == null) ? vmDir : v + File.pathSeparator + vmDir); + + // launch the actual test + System.out.println("Launching: " + launcher + " shared library path: " + + env.get(sharedLibraryPathEnvName)); + new OutputAnalyzer(pb.start()) + .outputTo(System.out) + .errorTo(System.err) + .shouldHaveExitValue(0); + + } + +} + diff --git a/test/jdk/java/lang/module/exeNullCallerGetResource/exeNullCallerGetResource.c b/test/jdk/java/lang/module/exeNullCallerGetResource/exeNullCallerGetResource.c new file mode 100644 index 00000000000..f5e77e1f1d5 --- /dev/null +++ b/test/jdk/java/lang/module/exeNullCallerGetResource/exeNullCallerGetResource.c @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include <stdio.h> +#include <stdlib.h> + +#include "jni.h" +#undef NDEBUG +#include "assert.h" +#include "string.h" + +static jclass class_InputStream = NULL; +static jmethodID mid_InputStream_close = NULL; + +// in.close(); +void closeInputStream(JNIEnv *env, jobject in) { + (*env)->CallObjectMethod(env, in, mid_InputStream_close); + if ((*env)->ExceptionOccurred(env) != NULL) { + printf("ERROR: Exception was thrown calling InputStream::close.\n"); + (*env)->ExceptionDescribe(env); + exit(-1); + } +} + +/* + * The java test running this native test creates a test module named 'n' + * which opens the package 'open'. It has a text file resource named + * 'test.txt' in the open package. It also has a class called + * open.OpenResources. One should be able to get the resource through + * either the Class or the Module with getResourceAsStream. + * + * Class c = open.OpenResources.fetchClass(); + * InputStream in1 = c.getResourceAsStream("test.txt"); + * Module n = c.getModule(); + * InputStream in2 = n.getResourceAsStream("open/test.txt"); + * + * The test also checks that closed resources are not available and + * don't throw any exceptions. The test module contains a class + * called closed.ClosedResources and a file 'test.txt' in the package + * 'closed'. + * + * Class closed = closed.ClosedResources.fetchClass(); + * assert(closed.getResourceAsStream("test.txt") == null); + * assert(n.getResourceAsStream("closed/test.txt") == null); + * + */ +int main(int argc, char** args) { + JavaVM *jvm; + JNIEnv *env; + JavaVMInitArgs vm_args; + JavaVMOption options[4]; + jint rc; + + options[0].optionString = "--module-path=mods"; + options[1].optionString = "--add-modules=n"; + + vm_args.version = JNI_VERSION_9; + vm_args.nOptions = 2; + vm_args.options = options; + + if ((rc = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args)) != JNI_OK) { + printf("ERROR: cannot create VM.\n"); + exit(-1); + } + + // initialize for stream close + class_InputStream = (*env)->FindClass(env, "java/io/InputStream"); + assert(class_InputStream != NULL); + mid_InputStream_close = (*env)->GetMethodID(env, class_InputStream, "close", "()V" ); + assert(mid_InputStream_close != NULL); + + // the open and closed classes + jclass class_OpenResources = (*env)->FindClass(env, "open/OpenResources"); + assert(class_OpenResources != NULL); + jclass class_ClosedResources = (*env)->FindClass(env, "closed/ClosedResources"); + assert(class_ClosedResources != NULL); + + // Fetch the Module from one of the classes in the module + jclass class_Class = (*env)->FindClass(env, "java/lang/Class"); + assert(class_Class != NULL); + jmethodID mid_Class_getModule = (*env)->GetMethodID(env, class_Class, "getModule", "()Ljava/lang/Module;" ); + assert(mid_Class_getModule != NULL); + jobject n =(*env)->CallObjectMethod(env, class_OpenResources, mid_Class_getModule); + if ((*env)->ExceptionOccurred(env) != NULL) { + printf("ERROR: Exception was thrown calling Class::getModule.\n"); + (*env)->ExceptionDescribe(env); + exit(-1); + } + assert(n != NULL); + + // Attempt to fetch an open resource from the module. It should return a valid stream. + // InputStream in = n.getResourceAsStream("open/test.txt"); + jclass class_Module = (*env)->FindClass(env, "java/lang/Module"); + assert(class_Module != NULL); + jmethodID mid_Module_getResourceAsStream = + (*env)->GetMethodID(env, class_Module, "getResourceAsStream", "(Ljava/lang/String;)Ljava/io/InputStream;" ); + assert(mid_Module_getResourceAsStream != NULL); + jobject in = (*env)->CallObjectMethod(env, n, mid_Module_getResourceAsStream, + (*env)->NewStringUTF(env, "open/test.txt")); + if ((*env)->ExceptionOccurred(env) != NULL) { + printf("ERROR: Exception was thrown calling Module::getResourceAsStream on 'open/test.txt'.\n"); + (*env)->ExceptionDescribe(env); + exit(-1); + } + if (in == NULL) { + printf("ERROR: Module::getResourceAsStream, expected valid stream for open resource\n"); + exit(-1); + } + + // in.close(); + closeInputStream(env, in); + + // Attempt to fetch closed resource from the module. It should return null. + // in = n.getResourceAsStream("closed/test.txt"); + in = (*env)->CallObjectMethod(env, n, mid_Module_getResourceAsStream, + (*env)->NewStringUTF(env, "closed/test.txt")); + if ((*env)->ExceptionOccurred(env) != NULL) { + printf("ERROR: Exception was thrown calling Module::getResourceAsStream on 'closed/test.txt'.\n"); + (*env)->ExceptionDescribe(env); + exit(-1); + } + if (in != NULL) { + printf("ERROR: Module::getResourceAsStream, expected null value for closed resource\n"); + exit(-1); + } + + // Attempt to fetch open resource from the class. It should return a valid stream. + // in = open.OpenReosurces.class.getResourceAsStream("test.txt"); + jmethodID mid_Class_getResourceAsStream = + (*env)->GetMethodID(env, class_Class, "getResourceAsStream", "(Ljava/lang/String;)Ljava/io/InputStream;" ); + assert(mid_Class_getResourceAsStream != NULL); + in = (*env)->CallObjectMethod(env, class_OpenResources, mid_Class_getResourceAsStream, + (*env)->NewStringUTF(env, "test.txt")); + if ((*env)->ExceptionOccurred(env) != NULL) { + printf("ERROR: Exception was thrown calling Class::getResourceAsStream on 'test.txt'.\n"); + (*env)->ExceptionDescribe(env); + exit(-1); + } + if (in == NULL) { + printf("ERROR: Class::getResourceAsStream, expected valid stream for open resource\n"); + exit(-1); + } + + // in.close(); + closeInputStream(env, in); + + // Attempt to fetch closed resource from the class. It should return null. + // in = closed.ClosedResources.class.getResourceAsStream("test.txt"); + in = (*env)->CallObjectMethod(env, class_ClosedResources, mid_Class_getResourceAsStream, + (*env)->NewStringUTF(env, "test.txt")); + if ((*env)->ExceptionOccurred(env) != NULL) { + printf("ERROR: Exception was thrown calling Class::getResourceAsStream on closed 'test.txt'.\n"); + (*env)->ExceptionDescribe(env); + exit(-1); + } + if (in != NULL) { + printf("ERROR: Class::getResourceAsStream, expected null value for closed resource\n"); + exit(-1); + } + + (*jvm)->DestroyJavaVM(jvm); + return 0; +} + diff --git a/test/jdk/java/lang/module/exeNullCallerGetResource/src/n/closed/ClosedResources.java b/test/jdk/java/lang/module/exeNullCallerGetResource/src/n/closed/ClosedResources.java new file mode 100644 index 00000000000..9ec5cdbc178 --- /dev/null +++ b/test/jdk/java/lang/module/exeNullCallerGetResource/src/n/closed/ClosedResources.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package closed; + +// class to test relative access to a closed resource +// with Class::getResourceAsStream with no caller frame +public class ClosedResources { +} diff --git a/test/jdk/java/lang/module/exeNullCallerGetResource/src/n/module-info.java b/test/jdk/java/lang/module/exeNullCallerGetResource/src/n/module-info.java new file mode 100644 index 00000000000..99170e686a5 --- /dev/null +++ b/test/jdk/java/lang/module/exeNullCallerGetResource/src/n/module-info.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// module to test access to both open and closed resources using +// Module::getResourceAsStream with no caller frame +module n { + opens open; +} diff --git a/test/jdk/java/lang/module/exeNullCallerGetResource/src/n/open/OpenResources.java b/test/jdk/java/lang/module/exeNullCallerGetResource/src/n/open/OpenResources.java new file mode 100644 index 00000000000..e449c0c36ac --- /dev/null +++ b/test/jdk/java/lang/module/exeNullCallerGetResource/src/n/open/OpenResources.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package open; + +// class used to test relative access to an open resource +// with Class::getResourceAsStream with no caller frame +public class OpenResources { +}