blob: 31194bb1cfd5f02a15fd213d860145df225ab4e2 [file] [log] [blame]
/*
* Copyright (c) 2011, 2015, 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 6887710
* @summary Verify the impact of sun.misc.JarIndex.metaInfFilenames on ServiceLoader
* @modules jdk.jartool/sun.tools.jar
* jdk.httpserver
* jdk.compiler
* jdk.zipfs
* @run main/othervm Basic
*/
import java.io.IOException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Iterator;
import java.util.ServiceLoader;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
/**
* Verifies the impact of sun.misc.JarIndex.metaInfFilenames on ServiceLoader
* and on finding resources via Class.getResource.
*
* 1) Compile the test sources:
* jarA:
* META-INF/services/my.happy.land
* com/message/spi/MessageService.java
* a/A.java
* jarB:
* META-INF/JAVA2.DS
* META-INF/services/no.name.service
* b/B.java
* jarC:
* META-INF/fonts.mf
* META-INF/fonts/Company-corporate.ttf
* META-INF/fonts/kidpr.ttf
* META-INF/services/com.message.spi.MessageService
* my/impl/StandardMessageService.java
*
* 2) Build three jar files a.jar, b.jar, c.jar
*
* 3) Create an index in a.jar (jar -i a.jar b.jar c.jar)
* with sun.misc.JarIndex.metaInfFilenames=true
*
* 4) Start a HTTP server serving out the three jars.
*
* The test then tries to locate services/resources within the jars using
* URLClassLoader. Each request to the HTTP server is recorded to ensure
* only the correct amount of requests are being made.
*
*/
public class Basic {
static final String slash = File.separator;
static final String[] testSources = {
"jarA" + slash + "a" + slash + "A.java",
"jarA" + slash + "com" + slash + "message" + slash + "spi" + slash + "MessageService.java",
"jarB" + slash + "b" + slash + "B.java",
"jarC" + slash + "my" + slash + "impl" + slash + "StandardMessageService.java"};
static final String testSrc = System.getProperty("test.src");
static final String testSrcDir = testSrc != null ? testSrc : ".";
static final String testClasses = System.getProperty("test.classes");
static final String testClassesDir = testClasses != null ? testClasses : ".";
static JarHttpServer httpServer;
public static void main(String[] args) throws Exception {
// Set global url cache to false so that we can track every jar request.
(new URL("http://localhost/")).openConnection().setDefaultUseCaches(false);
buildTest();
try {
httpServer = new JarHttpServer(testClassesDir);
httpServer.start();
doTest(httpServer.getAddress());
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
if (httpServer != null) { httpServer.stop(2); }
}
}
static void buildTest() {
/* compile the source that will be used to generate the jars */
for (int i=0; i<testSources.length; i++)
testSources[i] = testSrcDir + slash + testSources[i];
compile("-d" , testClassesDir,
"-sourcepath", testSrcDir,
testSources[0], testSources[1], testSources[2], testSources[3]);
/* build the 3 jar files */
jar("-cf", testClassesDir + slash + "a.jar",
"-C", testClassesDir, "a",
"-C", testClassesDir, "com",
"-C", testSrcDir + slash + "jarA", "META-INF");
jar("-cf", testClassesDir + slash + "b.jar",
"-C", testClassesDir, "b",
"-C", testSrcDir + slash + "jarB", "META-INF");
jar("-cf", testClassesDir + slash + "c.jar",
"-C", testClassesDir, "my",
"-C", testSrcDir + slash + "jarC", "META-INF");
/* Create an index in a.jar for b.jar and c.jar */
createIndex(testClassesDir);
}
/* run jar <args> */
static void jar(String... args) {
debug("Running: jar " + Arrays.toString(args));
sun.tools.jar.Main jar = new sun.tools.jar.Main(System.out, System.err, "jar");
if (!jar.run(args)) {
throw new RuntimeException("jar failed: args=" + Arrays.toString(args));
}
}
/* run javac <args> */
static void compile(String... args) {
debug("Running: javac " + Arrays.toString(args));
if (com.sun.tools.javac.Main.compile(args) != 0) {
throw new RuntimeException("javac failed: args=" + Arrays.toString(args));
}
}
static String jar;
static {
jar = System.getProperty("java.home") + slash+ "bin" + slash + "jar";
}
/* create the index */
static void createIndex(String workingDir) {
// ProcessBuilder is used so that the current directory can be set
// to the directory that directly contains the jars.
debug("Running jar to create the index");
ProcessBuilder pb = new ProcessBuilder(
jar, "-J-Dsun.misc.JarIndex.metaInfFilenames=true", "-i", "a.jar", "b.jar", "c.jar");
pb.directory(new File(workingDir));
//pd.inheritIO();
try {
Process p = pb.start();
if(p.waitFor() != 0)
throw new RuntimeException("jar indexing failed");
if(debug && p != null) {
String line = null;
BufferedReader reader =
new BufferedReader(new InputStreamReader(p.getInputStream()));
while((line = reader.readLine()) != null)
debug(line);
reader = new BufferedReader(new InputStreamReader(p.getErrorStream()));
while((line = reader.readLine()) != null)
debug(line);
}
} catch(InterruptedException ie) { throw new RuntimeException(ie);
} catch(IOException e) { throw new RuntimeException(e); }
}
static final boolean debug = true;
static void debug(Object message) { if (debug) System.out.println(message); }
/* service define in c.jar */
static final String messageService = "com.message.spi.MessageService";
/* a service that is not defined in any of the jars */
static final String unknownService = "java.lang.Object";
static void doTest(InetSocketAddress serverAddress) throws IOException {
URL baseURL = new URL("http://localhost:" + serverAddress.getPort() + "/");
int failed = 0;
// Tests using java.util.SerivceLoader
if (!javaUtilServiceLoaderTest(baseURL, messageService, true, false, true)) {
System.out.println("Test: ServiceLoader looking for " + messageService + ", failed");
failed++;
}
if (!javaUtilServiceLoaderTest(baseURL, unknownService, false, false, false)) {
System.out.println("Test: ServiceLoader looking for " + unknownService + " failed");
failed++;
}
// Tests using java.lang.Class (similar to the FontManager in javafx)
if (!klassLoader(baseURL, "/META-INF/fonts.mf", true, false, true)) {
System.out.println("Test: klassLoader looking for /META-INF/fonts.mf failed");
failed++;
}
if (!klassLoader(baseURL, "/META-INF/unknown.mf", false, false, false)) {
System.out.println("Test: klassLoader looking for /META-INF/unknown.mf failed");
failed++;
}
if (failed > 0)
throw new RuntimeException("Failed: " + failed + " tests");
}
static boolean javaUtilServiceLoaderTest(URL baseURL,
String serviceClass,
boolean expectToFind,
boolean expectbDotJar,
boolean expectcDotJar) throws IOException {
debug("----------------------------------");
debug("Running test with java.util.ServiceLoader looking for " + serviceClass);
URLClassLoader loader = getLoader(baseURL);
httpServer.reset();
Class<?> messageServiceClass = null;
try {
messageServiceClass = loader.loadClass(serviceClass);
} catch (ClassNotFoundException cnfe) {
System.err.println(cnfe);
throw new RuntimeException("Error in test: " + cnfe);
}
Iterator<?> iterator = (ServiceLoader.load(messageServiceClass, loader)).iterator();
if (expectToFind && !iterator.hasNext()) {
debug(messageServiceClass + " NOT found.");
return false;
}
while (iterator.hasNext()) {
debug("found " + iterator.next() + " " + messageService);
}
debug("HttpServer: " + httpServer);
if (!expectbDotJar && httpServer.bDotJar > 0) {
debug("Unexpeced request sent to the httpserver for b.jar");
return false;
}
if (!expectcDotJar && httpServer.cDotJar > 0) {
debug("Unexpeced request sent to the httpserver for c.jar");
return false;
}
return true;
}
/* Tries to find a resource in a similar way to the font manager in javafx
* com.sun.javafx.scene.text.FontManager */
static boolean klassLoader(URL baseURL,
String resource,
boolean expectToFind,
boolean expectbDotJar,
boolean expectcDotJar) throws IOException {
debug("----------------------------------");
debug("Running test looking for " + resource);
URLClassLoader loader = getLoader(baseURL);
httpServer.reset();
Class<?> ADotAKlass = null;
try {
ADotAKlass = loader.loadClass("a.A");
} catch (ClassNotFoundException cnfe) {
System.err.println(cnfe);
throw new RuntimeException("Error in test: " + cnfe);
}
URL u = ADotAKlass.getResource(resource);
if (expectToFind && u == null) {
System.out.println("Expected to find " + resource + " but didn't");
return false;
}
debug("HttpServer: " + httpServer);
if (!expectbDotJar && httpServer.bDotJar > 0) {
debug("Unexpeced request sent to the httpserver for b.jar");
return false;
}
if (!expectcDotJar && httpServer.cDotJar > 0) {
debug("Unexpeced request sent to the httpserver for c.jar");
return false;
}
return true;
}
static URLClassLoader getLoader(URL baseURL) throws IOException {
ClassLoader loader = Basic.class.getClassLoader();
while (loader.getParent() != null)
loader = loader.getParent();
return new URLClassLoader( new URL[]{
new URL(baseURL, "a.jar"),
new URL(baseURL, "b.jar"),
new URL(baseURL, "c.jar")}, loader );
}
/**
* HTTP Server to server the jar files.
*/
static class JarHttpServer implements HttpHandler {
final String docsDir;
final HttpServer httpServer;
int aDotJar, bDotJar, cDotJar;
JarHttpServer(String docsDir) throws IOException {
this.docsDir = docsDir;
httpServer = HttpServer.create(new InetSocketAddress(0), 0);
httpServer.createContext("/", this);
}
void start() throws IOException {
httpServer.start();
}
void stop(int delay) {
httpServer.stop(delay);
}
InetSocketAddress getAddress() {
return httpServer.getAddress();
}
void reset() {
aDotJar = bDotJar = cDotJar = 0;
}
@Override
public String toString() {
return "aDotJar=" + aDotJar + ", bDotJar=" + bDotJar + ", cDotJar=" + cDotJar;
}
public void handle(HttpExchange t) throws IOException {
InputStream is = t.getRequestBody();
Headers map = t.getRequestHeaders();
Headers rmap = t.getResponseHeaders();
URI uri = t.getRequestURI();
debug("Server: received request for " + uri);
String path = uri.getPath();
if (path.endsWith("a.jar"))
aDotJar++;
else if (path.endsWith("b.jar"))
bDotJar++;
else if (path.endsWith("c.jar"))
cDotJar++;
else
System.out.println("Unexpected resource request" + path);
while (is.read() != -1);
is.close();
File file = new File(docsDir, path);
if (!file.exists())
throw new RuntimeException("Error: request for " + file);
long clen = file.length();
t.sendResponseHeaders (200, clen);
OutputStream os = t.getResponseBody();
FileInputStream fis = new FileInputStream(file);
try {
byte[] buf = new byte [16 * 1024];
int len;
while ((len=fis.read(buf)) != -1) {
os.write (buf, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
fis.close();
os.close();
}
}
}