blob: 44464015f3450e72320d6ee6c7142025b79fddfb [file] [log] [blame]
/*
* Copyright (c) 2015, 2016, 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 8132734 8144062 8159785
* @summary Test that URL connections to multi-release jars can be runtime versioned
* @library /lib/testlibrary/java/util/jar
* @modules java.compiler
* jdk.compiler
* jdk.httpserver
* jdk.jartool
* @build Compiler JarBuilder CreateMultiReleaseTestJars SimpleHttpServer
* @run testng MultiReleaseJarURLConnection
*/
import java.io.IOException;
import java.io.InputStream;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Enumeration;
import java.util.jar.JarFile;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class MultiReleaseJarURLConnection {
String userdir = System.getProperty("user.dir",".");
String unversioned = userdir + "/unversioned.jar";
String unsigned = userdir + "/multi-release.jar";
String signed = userdir + "/signed-multi-release.jar";
SimpleHttpServer server;
@BeforeClass
public void initialize() throws Exception {
CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars();
creator.compileEntries();
creator.buildUnversionedJar();
creator.buildMultiReleaseJar();
creator.buildSignedMultiReleaseJar();
server = new SimpleHttpServer();
server.start();
}
@AfterClass
public void close() throws IOException {
// Windows requires server to stop before file is deleted
if (server != null)
server.stop();
Files.delete(Paths.get(unversioned));
Files.delete(Paths.get(unsigned));
Files.delete(Paths.get(signed));
}
@DataProvider(name = "data")
public Object[][] createData() {
return new Object[][]{
{"unversioned", unversioned},
{"unsigned", unsigned},
{"signed", signed}
};
}
@Test(dataProvider = "data")
public void testRuntimeVersioning(String style, String file) throws Exception {
String urlFile = "jar:file:" + file + "!/";
String baseUrlEntry = urlFile + "version/Version.java";
String rtreturn = "return " + Runtime.version().major();
Assert.assertTrue(readAndCompare(new URL(baseUrlEntry), "return 8"));
// #runtime is "magic" for a multi-release jar, but not for unversioned jar
Assert.assertTrue(readAndCompare(new URL(baseUrlEntry + "#runtime"),
style.equals("unversioned") ? "return 8" : rtreturn));
// #fragment or any other fragment is not magic
Assert.assertTrue(readAndCompare(new URL(baseUrlEntry + "#fragment"), "return 8"));
// cached entities not affected
Assert.assertTrue(readAndCompare(new URL(baseUrlEntry), "return 8"));
// the following tests will not work with unversioned jars
if (style.equals("unversioned")) return;
// direct access to versioned entry
String versUrlEntry = urlFile + "META-INF/versions/" + Runtime.version().major()
+ "/version/Version.java";
Assert.assertTrue(readAndCompare(new URL(versUrlEntry), rtreturn));
// adding any fragment does not change things
Assert.assertTrue(readAndCompare(new URL(versUrlEntry + "#runtime"), rtreturn));
Assert.assertTrue(readAndCompare(new URL(versUrlEntry + "#fragment"), rtreturn));
// it really doesn't change things
versUrlEntry = urlFile + "META-INF/versions/10/version/Version.java";
Assert.assertTrue(readAndCompare(new URL(versUrlEntry), "return 10"));
Assert.assertTrue(readAndCompare(new URL(versUrlEntry + "#runtime"), "return 10"));
Assert.assertTrue(readAndCompare(new URL(versUrlEntry + "#fragment"), "return 10"));
}
@Test(dataProvider = "data")
public void testCachedJars(String style, String file) throws Exception {
String urlFile = "jar:file:" + file + "!/";
URL rootUrl = new URL(urlFile);
JarURLConnection juc = (JarURLConnection)rootUrl.openConnection();
JarFile rootJar = juc.getJarFile();
Runtime.Version root = rootJar.getVersion();
URL runtimeUrl = new URL(urlFile + "#runtime");
juc = (JarURLConnection)runtimeUrl.openConnection();
JarFile runtimeJar = juc.getJarFile();
Runtime.Version runtime = runtimeJar.getVersion();
if (style.equals("unversioned")) {
Assert.assertEquals(root, runtime);
} else {
Assert.assertNotEquals(root, runtime);
}
juc = (JarURLConnection)rootUrl.openConnection();
JarFile jar = juc.getJarFile();
Assert.assertEquals(jar.getVersion(), root);
Assert.assertEquals(jar, rootJar);
juc = (JarURLConnection)runtimeUrl.openConnection();
jar = juc.getJarFile();
Assert.assertEquals(jar.getVersion(), runtime);
Assert.assertEquals(jar, runtimeJar);
rootJar.close();
runtimeJar.close();
jar.close(); // probably not needed
}
@DataProvider(name = "resourcedata")
public Object[][] createResourceData() throws Exception {
return new Object[][]{
{"unversioned", Paths.get(unversioned).toUri().toURL()},
{"unsigned", Paths.get(unsigned).toUri().toURL()},
{"signed", Paths.get(signed).toUri().toURL()},
{"unversioned", new URL("file:" + unversioned)},
{"unsigned", new URL("file:" + unsigned)},
{"signed", new URL("file:" + signed)},
{"unversioned", new URL("jar:file:" + unversioned + "!/")},
{"unsigned", new URL("jar:file:" + unsigned + "!/")},
{"signed", new URL("jar:file:" + signed + "!/")},
// external jar received via http protocol
{"http", new URL("jar:http://localhost:" + server.getPort() + "/multi-release.jar!/")},
{"http", new URL("http://localhost:" + server.getPort() + "/multi-release.jar")},
};
}
@Test(dataProvider = "resourcedata")
public void testResources(String style, URL url) throws Throwable {
//System.out.println(" testing " + style + " url: " + url);
URL[] urls = {url};
URLClassLoader cldr = new URLClassLoader(urls);
Class<?> vcls = cldr.loadClass("version.Version");
// verify we are loading a runtime versioned class
MethodType mt = MethodType.methodType(int.class);
MethodHandle mh = MethodHandles.lookup().findVirtual(vcls, "getVersion", mt);
Assert.assertEquals((int)mh.invoke(vcls.newInstance()),
style.equals("unversioned") ? 8 : Runtime.version().major());
// now get a resource and verify that we don't have a fragment attached
Enumeration<URL> vclsUrlEnum = cldr.getResources("version/Version.class");
Assert.assertTrue(vclsUrlEnum.hasMoreElements());
URL vclsUrls[] = new URL[] {
vcls.getResource("/version/Version.class"),
vcls.getResource("Version.class"),
cldr.getResource("version/Version.class"),
vclsUrlEnum.nextElement()
};
Assert.assertFalse(vclsUrlEnum.hasMoreElements());
for (URL vclsUrl : vclsUrls) {
String fragment = vclsUrl.getRef();
Assert.assertNull(fragment);
// and verify that the the url is a reified pointer to the runtime entry
String rep = vclsUrl.toString();
//System.out.println(" getResource(\"/version/Version.class\") returned: " + rep);
if (style.equals("http")) {
Assert.assertTrue(rep.startsWith("jar:http:"));
} else {
Assert.assertTrue(rep.startsWith("jar:file:"));
}
String suffix;
if (style.equals("unversioned")) {
suffix = ".jar!/version/Version.class";
} else {
suffix = ".jar!/META-INF/versions/" + Runtime.version().major()
+ "/version/Version.class";
}
Assert.assertTrue(rep.endsWith(suffix));
}
cldr.close();
}
private boolean readAndCompare(URL url, String match) throws Exception {
boolean result;
// necessary to do it this way, instead of openStream(), so we can
// close underlying JarFile, otherwise windows can't delete the file
URLConnection conn = url.openConnection();
try (InputStream is = conn.getInputStream()) {
byte[] bytes = is.readAllBytes();
result = (new String(bytes)).contains(match);
}
if (conn instanceof JarURLConnection) {
((JarURLConnection)conn).getJarFile().close();
}
return result;
}
}