# HG changeset patch # User Jaroslav Tulach # Date 1382294163 -7200 # Node ID a6c71e376889f329b30e87f7f492e4ebdf5c7a49 # Parent bc2319ca9c17ff0992852f675b07856b2956b51f Can load multiple resources of the same name even in testing and development mode diff -r bc2319ca9c17 -r a6c71e376889 launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java Fri Oct 18 12:29:14 2013 +0200 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java Sun Oct 20 20:36:03 2013 +0200 @@ -572,7 +572,10 @@ } class Res { - public InputStream get(String resource) throws IOException { + public InputStream get(String resource, int skip) throws IOException { + if (!resource.endsWith(".class")) { + return getResource(resource, skip); + } URL u = null; for (ClassLoader l : loaders) { Enumeration en = l.getResources(resource); @@ -592,6 +595,23 @@ } throw new IOException("Can't find " + resource); } + private InputStream getResource(String resource, int skip) throws IOException { + URL u = null; + for (ClassLoader l : loaders) { + Enumeration en = l.getResources(resource); + while (en.hasMoreElements()) { + final URL now = en.nextElement(); + if (--skip < 0) { + u = now; + break; + } + } + } + if (u != null) { + return u.openStream(); + } + return null; + } } private static class Page extends HttpHandler { @@ -624,7 +644,7 @@ } OutputStream os = response.getOutputStream(); try { - InputStream is = res.get(r); + InputStream is = res.get(r, 0); copyStream(is, os, request.getRequestURL().toString(), replace); } catch (IOException ex) { response.setDetailMessage(ex.getLocalizedMessage()); @@ -696,7 +716,9 @@ } InputStream is = null; try { - is = loader.get(res); + String skip = request.getParameter("skip"); + int skipCnt = skip == null ? 0 : Integer.parseInt(skip); + is = loader.get(res, skipCnt); response.setContentType("text/javascript"); Writer w = response.getWriter(); w.append("["); diff -r bc2319ca9c17 -r a6c71e376889 launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java --- a/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Fri Oct 18 12:29:14 2013 +0200 +++ b/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Sun Oct 20 20:36:03 2013 +0200 @@ -48,7 +48,7 @@ class R implements Bck2Brwsr.Resources { @Override public InputStream get(String resource) throws IOException { - return loader.get(resource); + return loader.get(resource, 0); } } String b2b = System.getProperty("bck2brwsr.js"); @@ -70,9 +70,9 @@ } sb.append( "(function WrapperVM(global) {" - + " function ldCls(res) {\n" + + " function ldCls(res, skip) {\n" + " var request = new XMLHttpRequest();\n" - + " request.open('GET', '/classes/' + res, false);\n" + + " request.open('GET', '/classes/' + res + '?skip=' + skip, false);\n" + " request.send();\n" + " if (request.status !== 200) return null;\n" + " var arr = eval('(' + request.responseText + ')');\n" diff -r bc2319ca9c17 -r a6c71e376889 launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java --- a/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java Fri Oct 18 12:29:14 2013 +0200 +++ b/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java Sun Oct 20 20:36:03 2013 +0200 @@ -220,11 +220,15 @@ * @return the array of bytes in the given resource * @throws IOException I/O in case something goes wrong */ - public static byte[] read(String name) throws IOException { + public static byte[] read(String name, int skip) throws IOException { URL u = null; - Enumeration en = Console.class.getClassLoader().getResources(name); - while (en.hasMoreElements()) { - u = en.nextElement(); + if (!name.endsWith(".class")) { + u = getResource(name, skip); + } else { + Enumeration en = Console.class.getClassLoader().getResources(name); + while (en.hasMoreElements()) { + u = en.nextElement(); + } } if (u == null) { if (name.endsWith(".class")) { @@ -248,6 +252,19 @@ } } + private static URL getResource(String resource, int skip) throws IOException { + URL u = null; + Enumeration en = Console.class.getClassLoader().getResources(resource); + while (en.hasMoreElements()) { + final URL now = en.nextElement(); + if (--skip < 0) { + u = now; + break; + } + } + return u; + } + @JavaScriptBody(args = {}, body = "vm.desiredAssertionStatus = true;") private static void turnAssetionStatusOn() { } diff -r bc2319ca9c17 -r a6c71e376889 rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ResourcesTest.java --- a/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ResourcesTest.java Fri Oct 18 12:29:14 2013 +0200 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ResourcesTest.java Sun Oct 20 20:36:03 2013 +0200 @@ -17,8 +17,10 @@ */ package org.apidesign.bck2brwsr.tck; +import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.util.Enumeration; import org.apidesign.bck2brwsr.vmtest.Compare; import org.apidesign.bck2brwsr.vmtest.VMTest; import org.testng.annotations.Factory; @@ -28,27 +30,52 @@ * @author Jaroslav Tulach */ public class ResourcesTest { + @Compare public String allManifests() throws Exception { + Enumeration en = ClassLoader.getSystemResources("META-INF/MANIFEST.MF"); + assert en.hasMoreElements() : "Should have at least one manifest"; + String first = readString(en.nextElement().openStream()); + boolean different = false; + int cnt = 1; + while (en.hasMoreElements()) { + URL url = en.nextElement(); + String now = readString(url.openStream()); + if (!first.equals(now)) { + different = true; + } + cnt++; + if (cnt > 500) { + throw new IllegalStateException( + "Giving up. First manifest:\n" + first + + "\nLast manifest:\n" + now + ); + } + } + assert different : "Not all manifests should look like first one:\n" + first; + return "" + cnt; + } @Compare public String readResourceAsStream() throws Exception { InputStream is = getClass().getResourceAsStream("Resources.txt"); - byte[] b = new byte[30]; - int len = is.read(b); + return readString(is); + } + + private String readString(InputStream is) throws IOException { StringBuilder sb = new StringBuilder(); - for (int i = 0; i < len; i++) { - sb.append((char)b[i]); + byte[] b = new byte[512]; + for (;;) { + int len = is.read(b); + if (len == -1) { + return sb.toString(); + } + for (int i = 0; i < len; i++) { + sb.append((char)b[i]); + } } - return sb.toString(); } @Compare public String readResourceAsStreamFromClassLoader() throws Exception { InputStream is = getClass().getClassLoader().getResourceAsStream("org/apidesign/bck2brwsr/tck/Resources.txt"); - byte[] b = new byte[30]; - int len = is.read(b); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < len; i++) { - sb.append((char)b[i]); - } - return sb.toString(); + return readString(is); } @Compare public String toURIFromURL() throws Exception { diff -r bc2319ca9c17 -r a6c71e376889 rt/emul/mini/src/main/java/java/lang/Class.java --- a/rt/emul/mini/src/main/java/java/lang/Class.java Fri Oct 18 12:29:14 2013 +0200 +++ b/rt/emul/mini/src/main/java/java/lang/Class.java Sun Oct 20 20:36:03 2013 +0200 @@ -1365,15 +1365,10 @@ */ public InputStream getResourceAsStream(String name) { name = resolveName(name); - byte[] arr = getResourceAsStream0(name); + byte[] arr = ClassLoader.getResourceAsStream0(name, 0); return arr == null ? null : new ByteArrayInputStream(arr); } - - @JavaScriptBody(args = "name", body = - "return (vm.loadBytes) ? vm.loadBytes(name) : null;" - ) - private static native byte[] getResourceAsStream0(String name); - + /** * Finds a resource with a given name. The rules for searching resources * associated with a given class are implemented by the defining @@ -1409,8 +1404,11 @@ * @since JDK1.1 */ public java.net.URL getResource(String name) { - InputStream is = getResourceAsStream(name); - return is == null ? null : newResourceURL(URL.class, "res:/" + name, is); + return newResourceURL(name, getResourceAsStream(name)); + } + + static URL newResourceURL(String name, InputStream is) { + return is == null ? null : newResourceURL0(URL.class, "res:/" + name, is); } @JavaScriptBody(args = { "url", "spec", "is" }, body = @@ -1418,7 +1416,7 @@ + "u.constructor.cons__VLjava_lang_String_2Ljava_io_InputStream_2.call(u, spec, is);\n" + "return u;" ) - private static native URL newResourceURL(Class url, String spec, InputStream is); + private static native URL newResourceURL0(Class url, String spec, InputStream is); /** * Add a package name prefix if the name is not absolute Remove leading "/" diff -r bc2319ca9c17 -r a6c71e376889 rt/emul/mini/src/main/java/java/lang/ClassLoader.java --- a/rt/emul/mini/src/main/java/java/lang/ClassLoader.java Fri Oct 18 12:29:14 2013 +0200 +++ b/rt/emul/mini/src/main/java/java/lang/ClassLoader.java Sun Oct 20 20:36:03 2013 +0200 @@ -24,6 +24,7 @@ */ package java.lang; +import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.IOException; import java.net.URL; @@ -892,9 +893,45 @@ return Object.class.getResource("/" + name); } + @JavaScriptBody(args = { "name", "skip" }, body + = "return (vm.loadBytes) ? vm.loadBytes(name, skip) : null;" + ) + static native byte[] getResourceAsStream0(String name, int skip); + private static Enumeration getBootstrapResources(String name) { - URL u = Object.class.getResource("/" + name); - return new OneOrZeroEnum(u); + return new ResEnum(name); + } + + private static class ResEnum implements Enumeration { + private final String name; + private URL next; + private int skip; + + public ResEnum(String name) { + this.name = name; + } + + + public boolean hasMoreElements() { + if (next == null && skip >= 0) { + byte[] arr = getResourceAsStream0(name, skip++); + if (arr != null) { + next = Class.newResourceURL(name, new ByteArrayInputStream(arr)); + } else { + skip = -1; + } + } + return next != null; + } + + public URL nextElement() { + URL r = next; + if (r == null) { + throw new NoSuchElementException(); + } + next = null; + return r; + } } private static class OneOrZeroEnum implements Enumeration { diff -r bc2319ca9c17 -r a6c71e376889 rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Fri Oct 18 12:29:14 2013 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Sun Oct 20 20:36:03 2013 +0200 @@ -159,13 +159,13 @@ + " return vm.org_apidesign_vm4brwsr_VMLazy(false).\n" + " reload__Ljava_lang_Object_2Ljava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2_3B(loader, name, args, byteCode);\n" + " };\n" - + " vm.loadBytes = function(name) {\n" + + " vm.loadBytes = function(name, skip) {\n" + " return vm.org_apidesign_vm4brwsr_VMLazy(false).\n" - + " loadBytes___3BLjava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2(loader, name, args);\n" + + " loadBytes___3BLjava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2I(loader, name, args, typeof skip == 'number' ? skip : 0);\n" + " }\n" + " vm.java_lang_reflect_Array(false);\n" + " vm.org_apidesign_vm4brwsr_VMLazy(false).\n" - + " loadBytes___3BLjava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2(loader, null, args);\n" + + " loadBytes___3BLjava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2I(loader, null, args, 0);\n" + " return loader;\n" + " };\n"); out.append("}(this));"); diff -r bc2319ca9c17 -r a6c71e376889 rt/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Fri Oct 18 12:29:14 2013 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Sun Oct 20 20:36:03 2013 +0200 @@ -48,14 +48,14 @@ return new VMLazy(loader, arguments).defineClass(arr, name, false); } - static byte[] loadBytes(Object loader, String name, Object[] arguments) throws Exception { - return Zips.loadFromCp(arguments, name); + static byte[] loadBytes(Object loader, String name, Object[] arguments, int skip) throws Exception { + return Zips.loadFromCp(arguments, name, skip); } private Object load(String name, boolean instance) throws IOException, ClassNotFoundException { String res = name.replace('.', '/') + ".class"; - byte[] arr = Zips.loadFromCp(args, res); + byte[] arr = Zips.loadFromCp(args, res, 0); if (arr == null) { throw new ClassNotFoundException(name); } diff -r bc2319ca9c17 -r a6c71e376889 rt/vm/src/main/java/org/apidesign/vm4brwsr/Zips.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/Zips.java Fri Oct 18 12:29:14 2013 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/Zips.java Sun Oct 20 20:36:03 2013 +0200 @@ -49,7 +49,7 @@ @JavaScriptBody(args = { "arr", "index", "value" }, body = "arr[index] = value; return value;") private static native Object set(Object arr, int index, Object value); - public static byte[] loadFromCp(Object classpath, String res) + public static byte[] loadFromCp(Object classpath, String res, int skip) throws IOException, ClassNotFoundException { for (int i = 0; i < length(classpath); i++) { Object c = at(classpath, i); @@ -75,9 +75,10 @@ if (c instanceof Zips) { checkRes = ((Zips)c).findRes(res); } else { - checkRes = callFunction(c, res); + checkRes = callFunction(c, res, skip); + skip = 0; } - if (checkRes != null) { + if (checkRes != null && --skip < 0) { return checkRes; } } @@ -85,11 +86,11 @@ return null; } - @JavaScriptBody(args = { "fn", "res" }, body = - "if (typeof fn === 'function') return fn(res);\n" + @JavaScriptBody(args = { "fn", "res", "skip" }, body = + "if (typeof fn === 'function') return fn(res, skip);\n" + "return null;" ) - private static native byte[] callFunction(Object fn, String res); + private static native byte[] callFunction(Object fn, String res, int skip); @JavaScriptBody(args = { "msg" }, body = "if (typeof console !== 'undefined') console.log(msg.toString());") private static native void log(String msg);