1 /* 2 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 6887710 27 * @summary Verify the impact of sun.misc.JarIndex.metaInfFilenames on ServiceLoader 28 * @run main/othervm Basic 29 */ 30 31 import java.io.IOException; 32 import java.io.BufferedReader; 33 import java.io.File; 34 import java.io.FileInputStream; 35 import java.io.InputStream; 36 import java.io.InputStreamReader; 37 import java.io.OutputStream; 38 import java.net.InetSocketAddress; 39 import java.net.URI; 40 import java.net.URL; 41 import java.net.URLClassLoader; 42 import java.util.Arrays; 43 import java.util.Iterator; 44 import java.util.ServiceLoader; 45 import com.sun.net.httpserver.Headers; 46 import com.sun.net.httpserver.HttpExchange; 47 import com.sun.net.httpserver.HttpHandler; 48 import com.sun.net.httpserver.HttpServer; 49 50 /** 51 * Verifies the impact of sun.misc.JarIndex.metaInfFilenames on ServiceLoader 52 * and on finding resources via Class.getResource. 53 * 54 * 1) Compile the test sources: 55 * jarA: 56 * META-INF/services/my.happy.land 57 * com/message/spi/MessageService.java 58 * a/A.java 59 * jarB: 60 * META-INF/JAVA2.DS 61 * META-INF/services/no.name.service 62 * b/B.java 63 * jarC: 64 * META-INF/fonts.mf 65 * META-INF/fonts/Company-corporate.ttf 66 * META-INF/fonts/kidpr.ttf 67 * META-INF/services/com.message.spi.MessageService 68 * my/impl/StandardMessageService.java 69 * 70 * 2) Build three jar files a.jar, b.jar, c.jar 71 * 72 * 3) Create an index in a.jar (jar -i a.jar b.jar c.jar) 73 * with sun.misc.JarIndex.metaInfFilenames=true 74 * 75 * 4) Start a HTTP server serving out the three jars. 76 * 77 * The test then tries to locate services/resources within the jars using 78 * URLClassLoader. Each request to the HTTP server is recorded to ensure 79 * only the correct amount of requests are being made. 80 * 81 * Note: Needs jdk/lib/tools.jar in the classpath to compile and run. 82 */ 83 84 public class Basic { 85 static final String slash = File.separator; 86 static final String[] testSources = { 87 "jarA" + slash + "a" + slash + "A.java", 88 "jarA" + slash + "com" + slash + "message" + slash + "spi" + slash + "MessageService.java", 89 "jarB" + slash + "b" + slash + "B.java", 90 "jarC" + slash + "my" + slash + "impl" + slash + "StandardMessageService.java"}; 91 92 static final String testSrc = System.getProperty("test.src"); 93 static final String testSrcDir = testSrc != null ? testSrc : "."; 94 static final String testClasses = System.getProperty("test.classes"); 95 static final String testClassesDir = testClasses != null ? testClasses : "."; 96 97 static JarHttpServer httpServer; 98 99 public static void main(String[] args) throws Exception { 100 101 // Set global url cache to false so that we can track every jar request. 102 (new URL("http://localhost/")).openConnection().setDefaultUseCaches(false); 103 104 buildTest(); 105 106 try { 107 httpServer = new JarHttpServer(testClassesDir); 108 httpServer.start(); 109 110 doTest(httpServer.getAddress()); 111 112 } catch (IOException ioe) { 113 ioe.printStackTrace(); 114 } finally { 115 if (httpServer != null) { httpServer.stop(2); } 116 } 117 } 118 119 static void buildTest() { 120 /* compile the source that will be used to generate the jars */ 121 for (int i=0; i<testSources.length; i++) 122 testSources[i] = testSrcDir + slash + testSources[i]; 123 124 compile("-d" , testClassesDir, 125 "-sourcepath", testSrcDir, 126 testSources[0], testSources[1], testSources[2], testSources[3]); 127 128 /* build the 3 jar files */ 129 jar("-cf", testClassesDir + slash + "a.jar", 130 "-C", testClassesDir, "a", 131 "-C", testClassesDir, "com", 132 "-C", testSrcDir + slash + "jarA", "META-INF"); 133 jar("-cf", testClassesDir + slash + "b.jar", 134 "-C", testClassesDir, "b", 135 "-C", testSrcDir + slash + "jarB", "META-INF"); 136 jar("-cf", testClassesDir + slash + "c.jar", 137 "-C", testClassesDir, "my", 138 "-C", testSrcDir + slash + "jarC", "META-INF"); 139 140 /* Create an index in a.jar for b.jar and c.jar */ 141 createIndex(testClassesDir); 142 } 143 144 /* run jar <args> */ 145 static void jar(String... args) { 146 debug("Running: jar " + Arrays.toString(args)); 147 sun.tools.jar.Main jar = new sun.tools.jar.Main(System.out, System.err, "jar"); 148 if (!jar.run(args)) { 149 throw new RuntimeException("jar failed: args=" + Arrays.toString(args)); 150 } 151 } 152 153 /* run javac <args> */ 154 static void compile(String... args) { 155 debug("Running: javac " + Arrays.toString(args)); 156 if (com.sun.tools.javac.Main.compile(args) != 0) { 157 throw new RuntimeException("javac failed: args=" + Arrays.toString(args)); 158 } 159 } 160 161 static String jar; 162 static { 163 String javaHome = System.getProperty("java.home"); 164 if (javaHome.endsWith("jre")) { 165 int index = javaHome.lastIndexOf(slash); 166 if (index != -1) 167 javaHome = javaHome.substring(0, index); 168 } 169 170 jar = javaHome + slash+ "bin" + slash + "jar"; 171 } 172 173 /* create the index */ 174 static void createIndex(String workingDir) { 175 // ProcessBuilder is used so that the current directory can be set 176 // to the directory that directly contains the jars. 177 debug("Running jar to create the index"); 178 ProcessBuilder pb = new ProcessBuilder( 179 jar, "-J-Dsun.misc.JarIndex.metaInfFilenames=true", "-i", "a.jar", "b.jar", "c.jar"); 180 pb.directory(new File(workingDir)); 181 //pd.inheritIO(); 182 try { 183 Process p = pb.start(); 184 if(p.waitFor() != 0) 185 throw new RuntimeException("jar indexing failed"); 186 187 if(debug && p != null) { 188 String line = null; 189 BufferedReader reader = 190 new BufferedReader(new InputStreamReader(p.getInputStream())); 191 while((line = reader.readLine()) != null) 192 debug(line); 193 reader = new BufferedReader(new InputStreamReader(p.getErrorStream())); 194 while((line = reader.readLine()) != null) 195 debug(line); 196 } 197 } catch(InterruptedException ie) { throw new RuntimeException(ie); 198 } catch(IOException e) { throw new RuntimeException(e); } 199 } 200 201 static final boolean debug = true; 202 203 static void debug(Object message) { if (debug) System.out.println(message); } 204 205 /* service define in c.jar */ 206 static final String messageService = "com.message.spi.MessageService"; 207 208 /* a service that is not defined in any of the jars */ 209 static final String unknownService = "java.lang.Object"; 210 211 static void doTest(InetSocketAddress serverAddress) throws IOException { 212 URL baseURL = new URL("http://localhost:" + serverAddress.getPort() + "/"); 213 214 int failed = 0; 215 216 // Tests using java.util.SerivceLoader 217 if (!javaUtilServiceLoaderTest(baseURL, messageService, true, false, true)) { 218 System.out.println("Test: ServiceLoader looking for " + messageService + ", failed"); 219 failed++; 220 } 221 if (!javaUtilServiceLoaderTest(baseURL, unknownService, false, false, false)) { 222 System.out.println("Test: ServiceLoader looking for " + unknownService + " failed"); 223 failed++; 224 } 225 226 // Tests using java.lang.Class (similar to the FontManager in javafx) 227 if (!klassLoader(baseURL, "/META-INF/fonts.mf", true, false, true)) { 228 System.out.println("Test: klassLoader looking for /META-INF/fonts.mf failed"); 229 failed++; 230 } 231 if (!klassLoader(baseURL, "/META-INF/unknown.mf", false, false, false)) { 232 System.out.println("Test: klassLoader looking for /META-INF/unknown.mf failed"); 233 failed++; 234 } 235 236 if (failed > 0) 237 throw new RuntimeException("Failed: " + failed + " tests"); 238 } 239 240 static boolean javaUtilServiceLoaderTest(URL baseURL, 241 String serviceClass, 242 boolean expectToFind, 243 boolean expectbDotJar, 244 boolean expectcDotJar) throws IOException { 245 debug("----------------------------------"); 246 debug("Running test with java.util.ServiceLoader looking for " + serviceClass); 247 URLClassLoader loader = getLoader(baseURL); 248 httpServer.reset(); 249 250 Class<?> messageServiceClass = null; 251 try { 252 messageServiceClass = loader.loadClass(serviceClass); 253 } catch (ClassNotFoundException cnfe) { 254 System.err.println(cnfe); 255 throw new RuntimeException("Error in test: " + cnfe); 256 } 257 258 Iterator<?> iterator = (ServiceLoader.load(messageServiceClass, loader)).iterator(); 259 if (expectToFind && !iterator.hasNext()) { 260 debug(messageServiceClass + " NOT found."); 261 return false; 262 } 263 264 while (iterator.hasNext()) { 265 debug("found " + iterator.next() + " " + messageService); 266 } 267 268 debug("HttpServer: " + httpServer); 269 270 if (!expectbDotJar && httpServer.bDotJar > 0) { 271 debug("Unexpeced request sent to the httpserver for b.jar"); 272 return false; 273 } 274 if (!expectcDotJar && httpServer.cDotJar > 0) { 275 debug("Unexpeced request sent to the httpserver for c.jar"); 276 return false; 277 } 278 279 return true; 280 } 281 282 /* Tries to find a resource in a similar way to the font manager in javafx 283 * com.sun.javafx.scene.text.FontManager */ 284 static boolean klassLoader(URL baseURL, 285 String resource, 286 boolean expectToFind, 287 boolean expectbDotJar, 288 boolean expectcDotJar) throws IOException { 289 debug("----------------------------------"); 290 debug("Running test looking for " + resource); 291 URLClassLoader loader = getLoader(baseURL); 292 httpServer.reset(); 293 294 Class<?> ADotAKlass = null; 295 try { 296 ADotAKlass = loader.loadClass("a.A"); 297 } catch (ClassNotFoundException cnfe) { 298 System.err.println(cnfe); 299 throw new RuntimeException("Error in test: " + cnfe); 300 } 301 302 URL u = ADotAKlass.getResource(resource); 303 if (expectToFind && u == null) { 304 System.out.println("Expected to find " + resource + " but didn't"); 305 return false; 306 } 307 308 debug("HttpServer: " + httpServer); 309 310 if (!expectbDotJar && httpServer.bDotJar > 0) { 311 debug("Unexpeced request sent to the httpserver for b.jar"); 312 return false; 313 } 314 if (!expectcDotJar && httpServer.cDotJar > 0) { 315 debug("Unexpeced request sent to the httpserver for c.jar"); 316 return false; 317 } 318 319 return true; 320 } 321 322 static URLClassLoader getLoader(URL baseURL) throws IOException { 323 ClassLoader loader = Basic.class.getClassLoader(); 324 325 while (loader.getParent() != null) 326 loader = loader.getParent(); 327 328 return new URLClassLoader( new URL[]{ 329 new URL(baseURL, "a.jar"), 330 new URL(baseURL, "b.jar"), 331 new URL(baseURL, "c.jar")}, loader ); 332 } 333 334 /** 335 * HTTP Server to server the jar files. 336 */ 337 static class JarHttpServer implements HttpHandler { 338 final String docsDir; 339 final HttpServer httpServer; 340 int aDotJar, bDotJar, cDotJar; 341 342 JarHttpServer(String docsDir) throws IOException { 343 this.docsDir = docsDir; 344 345 httpServer = HttpServer.create(new InetSocketAddress(0), 0); 346 httpServer.createContext("/", this); 347 } 348 349 void start() throws IOException { 350 httpServer.start(); 351 } 352 353 void stop(int delay) { 354 httpServer.stop(delay); 355 } 356 357 InetSocketAddress getAddress() { 358 return httpServer.getAddress(); 359 } 360 361 void reset() { 362 aDotJar = bDotJar = cDotJar = 0; 363 } 364 365 @Override 366 public String toString() { 367 return "aDotJar=" + aDotJar + ", bDotJar=" + bDotJar + ", cDotJar=" + cDotJar; 368 } 369 370 public void handle(HttpExchange t) throws IOException { 371 InputStream is = t.getRequestBody(); 372 Headers map = t.getRequestHeaders(); 373 Headers rmap = t.getResponseHeaders(); 374 URI uri = t.getRequestURI(); 375 376 debug("Server: received request for " + uri); 377 String path = uri.getPath(); 378 if (path.endsWith("a.jar")) 379 aDotJar++; 380 else if (path.endsWith("b.jar")) 381 bDotJar++; 382 else if (path.endsWith("c.jar")) 383 cDotJar++; 384 else 385 System.out.println("Unexpected resource request" + path); 386 387 while (is.read() != -1); 388 is.close(); 389 390 File file = new File(docsDir, path); 391 if (!file.exists()) 392 throw new RuntimeException("Error: request for " + file); 393 long clen = file.length(); 394 t.sendResponseHeaders (200, clen); 395 OutputStream os = t.getResponseBody(); 396 FileInputStream fis = new FileInputStream(file); 397 try { 398 byte[] buf = new byte [16 * 1024]; 399 int len; 400 while ((len=fis.read(buf)) != -1) { 401 os.write (buf, 0, len); 402 } 403 } catch (IOException e) { 404 e.printStackTrace(); 405 } 406 fis.close(); 407 os.close(); 408 } 409 } 410 }