getClass().getResource(...).openConnection().getURL() returns a URL which is recognized by the browser and can be used to load resources with XHR or <img src='...'>. When no longer needed, convert connection to Closeable and use connection.close().
1.1 --- a/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ResourcesTest.java Thu Oct 30 01:49:57 2014 +0100
1.2 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ResourcesTest.java Thu Oct 30 01:50:21 2014 +0100
1.3 @@ -17,10 +17,15 @@
1.4 */
1.5 package org.apidesign.bck2brwsr.tck;
1.6
1.7 +import java.io.ByteArrayInputStream;
1.8 +import java.io.Closeable;
1.9 import java.io.IOException;
1.10 import java.io.InputStream;
1.11 import java.net.URL;
1.12 +import java.net.URLConnection;
1.13 import java.util.Enumeration;
1.14 +import net.java.html.js.JavaScriptBody;
1.15 +import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
1.16 import org.apidesign.bck2brwsr.vmtest.Compare;
1.17 import org.apidesign.bck2brwsr.vmtest.VMTest;
1.18 import org.testng.annotations.Factory;
1.19 @@ -63,8 +68,78 @@
1.20 return readString(is);
1.21 }
1.22
1.23 + @Compare public String readResourceViaXMLHttpRequest() throws Exception {
1.24 + return readResourceViaXHR("Resources.txt", null);
1.25 + }
1.26 +
1.27 + @BrwsrTest public void xhrTestedInBrowser() throws Exception {
1.28 + boolean[] run = { false };
1.29 + readResourceViaXHR("Resources.txt", run);
1.30 + assert run[0] : "XHR really used in browser";
1.31 + }
1.32 +
1.33 + @Compare public String readBinaryResourceViaXMLHttpRequest() throws Exception {
1.34 + return readResourceViaXHR("0xfe", null);
1.35 + }
1.36 +
1.37 + private String readResourceViaXHR(final String res, boolean[] exec) throws IOException {
1.38 + URL url = getClass().getResource(res);
1.39 + URLConnection conn = url.openConnection();
1.40 + String java = readBytes(url.openStream());
1.41 + String java2 = readBytes(conn.getInputStream());
1.42 + assert java.equals(java2) : "Java:\n" + java + "\nConn:\n" + java2;
1.43 +
1.44 + URL url2 = conn.getURL();
1.45 + String java3 = readBytes(url.openStream());
1.46 + assert java.equals(java3) : "Java:\n" + java + "\nConnURL:\n" + java3;
1.47 +
1.48 +
1.49 + byte[] xhr = readXHR(url2.toExternalForm());
1.50 + if (xhr != null) {
1.51 + if (exec != null) {
1.52 + exec[0] = true;
1.53 + }
1.54 + String s = readBytes(new ByteArrayInputStream(xhr));
1.55 + assert java.equals(s) : "Java:\n" + java + "\nXHR:\n" + s;
1.56 +
1.57 + assert conn instanceof Closeable : "Can be closed";
1.58 +
1.59 + Closeable c = (Closeable) conn;
1.60 + c.close();
1.61 +
1.62 + byte[] xhr2 = null;
1.63 + try {
1.64 + xhr2 = readXHR(url2.toExternalForm());
1.65 + } catch (Throwable t) {
1.66 + // OK, expecting error
1.67 + }
1.68 + assert xhr2 == null : "Cannot read the URL anymore";
1.69 + }
1.70 + return java;
1.71 + }
1.72 +
1.73 + @JavaScriptBody(args = { "url" }, body =
1.74 + "if (typeof XMLHttpRequest === 'undefined') return null;\n" +
1.75 + "var xhr = new XMLHttpRequest();\n" +
1.76 + "xhr.overrideMimeType('text\\/plain; charset=x-user-defined');\n" +
1.77 + "xhr.open('GET', url, false);\n" +
1.78 + "xhr.send();\n" +
1.79 + "var ret = []\n" +
1.80 + "for (var i = 0; i < xhr.responseText.length; i++) {\n" +
1.81 + " ret.push(xhr.responseText.charCodeAt(i) & 0xff);\n" +
1.82 + "}\n" +
1.83 + "return ret;\n"
1.84 + )
1.85 + private static byte[] readXHR(String url) {
1.86 + return null;
1.87 + }
1.88 +
1.89 @Compare public String readResourceViaConnection() throws Exception {
1.90 - InputStream is = getClass().getResource("Resources.txt").openConnection().getInputStream();
1.91 + final URL url = getClass().getResource("Resources.txt");
1.92 + String str = url.toExternalForm();
1.93 + int idx = str.indexOf("org/apidesign/bck2brwsr/tck");
1.94 + assert idx >= 0 : "Package name found in the URL name: " + str;
1.95 + InputStream is = url.openConnection().getInputStream();
1.96 return readString(is);
1.97 }
1.98
1.99 @@ -82,6 +157,20 @@
1.100 }
1.101 }
1.102
1.103 + private String readBytes(InputStream is) throws IOException {
1.104 + StringBuilder sb = new StringBuilder();
1.105 + byte[] b = new byte[512];
1.106 + for (;;) {
1.107 + int len = is.read(b);
1.108 + if (len == -1) {
1.109 + return sb.toString();
1.110 + }
1.111 + for (int i = 0; i < len; i++) {
1.112 + sb.append((int)b[i]).append(" ");
1.113 + }
1.114 + }
1.115 + }
1.116 +
1.117 @Compare public String readResourceAsStreamFromClassLoader() throws Exception {
1.118 InputStream is = getClass().getClassLoader().getResourceAsStream("org/apidesign/bck2brwsr/tck/Resources.txt");
1.119 return readString(is);
2.1 --- a/rt/emul/mini/src/main/java/java/lang/Class.java Thu Oct 30 01:49:57 2014 +0100
2.2 +++ b/rt/emul/mini/src/main/java/java/lang/Class.java Thu Oct 30 01:50:21 2014 +0100
2.3 @@ -1436,19 +1436,21 @@
2.4 * @since JDK1.1
2.5 */
2.6 public java.net.URL getResource(String name) {
2.7 - return newResourceURL(name, getResourceAsStream(name));
2.8 + name = resolveName(name);
2.9 + byte[] arr = ClassLoader.getResourceAsStream0(name, 0);
2.10 + return arr == null ? null : newResourceURL(name, arr);
2.11 }
2.12
2.13 - static URL newResourceURL(String name, InputStream is) {
2.14 - return is == null ? null : newResourceURL0(URL.class, "res:/" + name, is);
2.15 + static URL newResourceURL(String name, byte[] arr) {
2.16 + return newResourceURL0(URL.class, "res:/" + name, arr);
2.17 }
2.18
2.19 - @JavaScriptBody(args = { "url", "spec", "is" }, body =
2.20 + @JavaScriptBody(args = { "url", "spec", "arr" }, body =
2.21 "var u = url.cnstr(true);\n"
2.22 - + "u.constructor.cons__VLjava_lang_String_2Ljava_io_InputStream_2.call(u, spec, is);\n"
2.23 + + "u.constructor.cons__VLjava_lang_String_2_3B.call(u, spec, arr);\n"
2.24 + "return u;"
2.25 )
2.26 - private static native URL newResourceURL0(Class<URL> url, String spec, InputStream is);
2.27 + private static native URL newResourceURL0(Class<URL> url, String spec, byte[] arr);
2.28
2.29 /**
2.30 * Add a package name prefix if the name is not absolute Remove leading "/"
3.1 --- a/rt/emul/mini/src/main/java/java/lang/ClassLoader.java Thu Oct 30 01:49:57 2014 +0100
3.2 +++ b/rt/emul/mini/src/main/java/java/lang/ClassLoader.java Thu Oct 30 01:50:21 2014 +0100
3.3 @@ -916,7 +916,7 @@
3.4 if (next == null && skip >= 0) {
3.5 byte[] arr = getResourceAsStream0(name, skip++);
3.6 if (arr != null) {
3.7 - next = Class.newResourceURL(name, new ByteArrayInputStream(arr));
3.8 + next = Class.newResourceURL(name, arr);
3.9 } else {
3.10 skip = -1;
3.11 }
4.1 --- a/rt/emul/mini/src/main/java/java/net/URL.java Thu Oct 30 01:49:57 2014 +0100
4.2 +++ b/rt/emul/mini/src/main/java/java/net/URL.java Thu Oct 30 01:50:21 2014 +0100
4.3 @@ -26,6 +26,7 @@
4.4 package java.net;
4.5
4.6 import java.io.ByteArrayInputStream;
4.7 +import java.io.Closeable;
4.8 import java.io.IOException;
4.9 import java.io.InputStream;
4.10 import org.apidesign.bck2brwsr.core.JavaScriptBody;
4.11 @@ -216,7 +217,9 @@
4.12 private int hashCode = -1;
4.13
4.14 /** input stream associated with the URL */
4.15 - private InputStream is;
4.16 + private byte[] arr;
4.17 + /** blob URL, if any */
4.18 + private URL blob;
4.19
4.20 /**
4.21 * Creates a <code>URL</code> object from the specified
4.22 @@ -427,9 +430,9 @@
4.23 this(null, spec);
4.24 }
4.25
4.26 - private URL(String spec, InputStream is) throws MalformedURLException {
4.27 + private URL(String spec, byte[] arr) throws MalformedURLException {
4.28 this(null, spec);
4.29 - this.is = is;
4.30 + this.arr = arr;
4.31 }
4.32
4.33 /**
4.34 @@ -984,8 +987,8 @@
4.35 * @see java.net.URLConnection#getInputStream()
4.36 */
4.37 public final InputStream openStream() throws java.io.IOException {
4.38 - if (is != null) {
4.39 - return is;
4.40 + if (arr != null) {
4.41 + return new ByteArrayInputStream(arr);
4.42 }
4.43 byte[] arr = (byte[]) getContent(new Class[] { byte[].class });
4.44 if (arr == null) {
4.45 @@ -1057,13 +1060,49 @@
4.46 }
4.47 return null;
4.48 }
4.49 +
4.50 + @JavaScriptBody(args = "data", body =
4.51 + "if (typeof Blob !== 'undefined' && typeof Uint8Array !== 'undefined' && typeof URL !== 'undefined' && typeof URL.createObjectURL != 'undefined') {\n" +
4.52 + " var s = new Uint8Array(data);\n" +
4.53 + " var b = new Blob([ s ]);\n" +
4.54 + " return URL.createObjectURL(b);\n" +
4.55 + "} else {\n" +
4.56 + " return null;\n" +
4.57 + "}"
4.58 + )
4.59 + static native String toBlobURL(byte[] data);
4.60 +
4.61 + @JavaScriptBody(args = "url", body = "URL.revokeObjectURL(url);")
4.62 + static native void closeBlob(String url);
4.63
4.64 static URLStreamHandler getURLStreamHandler(final String protocol) {
4.65 URLStreamHandler universal = new URLStreamHandler() {
4.66 @Override
4.67 protected URLConnection openConnection(URL u) throws IOException {
4.68 - return new URLConnection(u) {
4.69 - Object stream = url.is;
4.70 + final ByteArrayInputStream is;
4.71 + if (u.arr != null) {
4.72 + is = new ByteArrayInputStream(u.arr);
4.73 + if (u.blob != null) {
4.74 + u = u.blob;
4.75 + } else {
4.76 + String blob = toBlobURL(u.arr);
4.77 + if (blob != null) {
4.78 + URL blobURL = new URL(null, blob, false);
4.79 + blobURL.blob = blobURL;
4.80 + blobURL.arr = u.arr;
4.81 + u = blobURL;
4.82 + }
4.83 + }
4.84 + } else {
4.85 + is = null;
4.86 + }
4.87 +
4.88 + class ResourceConnection extends URLConnection implements Closeable {
4.89 + public ResourceConnection(URL url) {
4.90 + super(url);
4.91 + }
4.92 +
4.93 + Object stream = is;
4.94
4.95 @Override
4.96 public void connect() throws IOException {
4.97 @@ -1086,9 +1125,15 @@
4.98 }
4.99 return (InputStream)stream;
4.100 }
4.101 -
4.102 -
4.103 - };
4.104 +
4.105 + @Override
4.106 + public void close() throws IOException {
4.107 + if (url.blob != null) {
4.108 + closeBlob(url.blob.toExternalForm());
4.109 + }
4.110 + }
4.111 + }
4.112 + return new ResourceConnection(u);
4.113 }
4.114 };
4.115 return universal;