151 lines
7.3 KiB
Java
151 lines
7.3 KiB
Java
|
/*
|
||
|
* Copyright (c) 2020, 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.
|
||
|
*/
|
||
|
|
||
|
import com.sun.net.httpserver.HttpHandler;
|
||
|
import jdk.internal.misc.Unsafe;
|
||
|
import jdk.test.lib.Asserts;
|
||
|
import java.io.InputStream;
|
||
|
import java.net.URL;
|
||
|
|
||
|
|
||
|
// Test cases:
|
||
|
//
|
||
|
// 1. load HttpExchange in apploader, define HttpExchange in parent (platform) loader,
|
||
|
// then load MyHttpHandler => fail.
|
||
|
// 2. define HttpExchange in parent (platform) loader, load MyHttpHandler,
|
||
|
// then try to define HttpExchange in child (app) loader => fail.
|
||
|
// 3. no LinkageError should be throw when linking a class that does not override/implement any
|
||
|
// methods.
|
||
|
class LoaderConstraintsApp {
|
||
|
static void defineHttpExchangeWithAppLoader() throws Exception {
|
||
|
Unsafe unsafe = Unsafe.getUnsafe();
|
||
|
URL url = new URL("jrt://modules/jdk.httpserver/com/sun/net/httpserver/HttpExchange.class");
|
||
|
byte[] bytes;
|
||
|
try (InputStream is = url.openStream()) {
|
||
|
bytes = is.readAllBytes();
|
||
|
}
|
||
|
Class fakeClass = unsafe.defineClass("com/sun/net/httpserver/HttpExchange", bytes, 0, bytes.length,
|
||
|
LoaderConstraintsApp.class.getClassLoader(),
|
||
|
LoaderConstraintsApp.class.getProtectionDomain());
|
||
|
System.out.println("fake HttpExchange = " + fakeClass.hashCode());
|
||
|
System.out.println("fake HttpExchange (loader) = " + fakeClass.getClassLoader());
|
||
|
}
|
||
|
|
||
|
static void resolveHttpExchangeInParentLoader(ClassLoader loader) throws Exception {
|
||
|
Class realClass = Class.forName("com.sun.net.httpserver.HttpExchange", false, loader);
|
||
|
System.out.println("real HttpExchange = " + realClass.hashCode());
|
||
|
System.out.println("real HttpExchange (loader) = " + realClass.getClassLoader());
|
||
|
}
|
||
|
|
||
|
static void doTest(int k) throws Exception {
|
||
|
ClassLoader appLoader = LoaderConstraintsApp.class.getClassLoader();
|
||
|
ClassLoader platformLoader = appLoader.getParent();
|
||
|
if (k == 1) {
|
||
|
defineHttpExchangeWithAppLoader();
|
||
|
// Resolve HttpExchange in parent loader (platform loader) - should be OK.
|
||
|
resolveHttpExchangeInParentLoader(platformLoader);
|
||
|
try {
|
||
|
// This must fail since the two loaders have resolved different versions of HttpExchange
|
||
|
HttpHandler h1 = new MyHttpHandler();
|
||
|
throw new RuntimeException("Load HttpExchange with platform loader did not fail as expected");
|
||
|
} catch (LinkageError e) {
|
||
|
System.out.println("Expected: " + e);
|
||
|
Asserts.assertTrue(e.getMessage().contains("loader constraint violation in interface itable initialization for class MyHttpHandler"));
|
||
|
e.printStackTrace(System.out);
|
||
|
}
|
||
|
} else if (k == 2) {
|
||
|
// Resolve HttpExchange in parent loader (platform loader) - this should succeed
|
||
|
resolveHttpExchangeInParentLoader(platformLoader);
|
||
|
|
||
|
// Load MyHttpHandler in app loader -- this should succeed, but it should
|
||
|
// create a class loader constraint that app loader must resolve the same HttpExchange
|
||
|
// class as the platform loader
|
||
|
HttpHandler h2 = new MyHttpHandler();
|
||
|
|
||
|
// Try to resolve a different HttpExchange class in the app loader. It must fail
|
||
|
try {
|
||
|
defineHttpExchangeWithAppLoader();
|
||
|
throw new RuntimeException("defineHttpExchangeWithAppLoader() did not fail as expected");
|
||
|
} catch (LinkageError e) {
|
||
|
System.out.println("Expected: " + e);
|
||
|
e.printStackTrace(System.out);
|
||
|
}
|
||
|
} else if (k == 3) {
|
||
|
// Resolve a different HttpExchange in platform and app loaders
|
||
|
resolveHttpExchangeInParentLoader(platformLoader);
|
||
|
defineHttpExchangeWithAppLoader();
|
||
|
|
||
|
// MyHttpHandlerB should still link, as it doesn't override HttpHandler.handle(HttpExchange)
|
||
|
MyHttpHandlerB.touch();
|
||
|
|
||
|
MyClassLoader loader = new MyClassLoader(platformLoader, appLoader);
|
||
|
try {
|
||
|
// MyHttpHandlerC should link, as its loader (MyClassLoader) resolves the same
|
||
|
// HttpExchange as the platform loader.
|
||
|
Class C = loader.loadClass("MyHttpHandlerC");
|
||
|
System.out.println("MyHttpHandlerC = " + C);
|
||
|
System.out.println("MyHttpHandlerC (loader) = " + C.getClassLoader());
|
||
|
|
||
|
HttpHandler handlerC = (HttpHandler)C.newInstance();
|
||
|
try {
|
||
|
// If the following is executed during CDS dynamic dump, a loader constraint is checked when resolving
|
||
|
// the HttpHandler.handle(HttpExchange) method reference inside MyHttpHandlerB.test(). This constraint must
|
||
|
// not be saved into the CDS archive for MyHttpHandlerB, or it would prevent MyHttpHandlerB
|
||
|
// from being linked during runtime.
|
||
|
MyHttpHandlerB.test(handlerC);
|
||
|
throw new RuntimeException("MyHttpHandlerB.test() did not fail as expected");
|
||
|
} catch (LinkageError e) {
|
||
|
System.out.println("Expected: " + e);
|
||
|
Asserts.assertTrue(e.getMessage().matches(".*constraint violation: when resolving interface method .*.HttpHandler.handle.*"));
|
||
|
e.printStackTrace(System.out);
|
||
|
}
|
||
|
} catch (Exception e) {
|
||
|
throw new RuntimeException("Unexpected exception", e);
|
||
|
}
|
||
|
} else {
|
||
|
// should not be other value
|
||
|
throw new RuntimeException("Wrong option specified k = " + k);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static void main(String... args) throws Throwable {
|
||
|
if (args.length < 1) {
|
||
|
// option of {1, 2}
|
||
|
throw new RuntimeException("Wrong number of arguments");
|
||
|
}
|
||
|
|
||
|
if (args.length >= 2 && "loadClassOnly".equals(args[1])) {
|
||
|
System.out.println("Loading: " + MyHttpHandler.class);
|
||
|
System.out.println("Loading: " + MyHttpHandlerB.class);
|
||
|
System.exit(0);
|
||
|
}
|
||
|
|
||
|
int k = Integer.valueOf(args[0]);
|
||
|
if (k < 1 && k > 3) {
|
||
|
throw new RuntimeException("Arg is out of range [1,3] k = " + k);
|
||
|
}
|
||
|
|
||
|
doTest(k);
|
||
|
}
|
||
|
}
|