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().
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Thu, 30 Oct 2014 01:50:21 +0100
changeset 1717f5200d90b730
parent 1716 20ea1a1b73ae
child 1718 ff14ad07bc16
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().
rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ResourcesTest.java
rt/emul/mini/src/main/java/java/lang/Class.java
rt/emul/mini/src/main/java/java/lang/ClassLoader.java
rt/emul/mini/src/main/java/java/net/URL.java
     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;