Loading of external JavaScript files simplified classloader
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Wed, 26 Jun 2013 08:43:32 +0200
branchclassloader
changeset 1632652760705d6
parent 162 50b9aff26ad9
child 164 7235d50dd452
Loading of external JavaScript files simplified
boot-fx/src/main/java/org/apidesign/html/boot/fx/FXPresenter.java
boot/src/main/java/net/java/html/js/JavaScriptResource.java
boot/src/main/java/org/apidesign/html/boot/impl/FnUtils.java
boot/src/main/java/org/apidesign/html/boot/impl/JsClassLoader.java
boot/src/main/java/org/apidesign/html/boot/spi/Fn.java
boot/src/test/java/org/apidesign/html/boot/impl/FnTest.java
boot/src/test/java/org/apidesign/html/boot/impl/JsClassLoaderBase.java
boot/src/test/java/org/apidesign/html/boot/impl/JsClassLoaderTest.java
boot/src/test/java/org/apidesign/html/boot/impl/JsMethods.java
boot/src/test/resources/org/apidesign/html/boot/impl/jsmethods.js
ko-fx/src/main/java/org/apidesign/html/kofx/Knockout.java
     1.1 --- a/boot-fx/src/main/java/org/apidesign/html/boot/fx/FXPresenter.java	Wed Jun 26 08:41:40 2013 +0200
     1.2 +++ b/boot-fx/src/main/java/org/apidesign/html/boot/fx/FXPresenter.java	Wed Jun 26 08:43:32 2013 +0200
     1.3 @@ -20,6 +20,8 @@
     1.4   */
     1.5  package org.apidesign.html.boot.fx;
     1.6  
     1.7 +import java.io.BufferedReader;
     1.8 +import java.io.Reader;
     1.9  import java.lang.reflect.Method;
    1.10  import java.net.URL;
    1.11  import java.net.URLClassLoader;
    1.12 @@ -95,6 +97,20 @@
    1.13      }
    1.14  
    1.15      @Override
    1.16 +    public void loadScript(Reader code) throws Exception {
    1.17 +        BufferedReader r = new BufferedReader(code);
    1.18 +        StringBuilder sb = new StringBuilder();
    1.19 +        for (;;) {
    1.20 +            String l = r.readLine();
    1.21 +            if (l == null) {
    1.22 +                break;
    1.23 +            }
    1.24 +            sb.append(l).append('\n');
    1.25 +        }
    1.26 +        engine.executeScript(sb.toString());
    1.27 +    }
    1.28 +
    1.29 +    @Override
    1.30      public void displayPage(final URL resource, Runnable onLoad) {
    1.31          engine = FXBrwsr.findEngine(onLoad);
    1.32          Platform.runLater(new Runnable() {
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/boot/src/main/java/net/java/html/js/JavaScriptResource.java	Wed Jun 26 08:43:32 2013 +0200
     2.3 @@ -0,0 +1,42 @@
     2.4 +/**
     2.5 + * HTML via Java(tm) Language Bindings
     2.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     2.7 + *
     2.8 + * This program is free software: you can redistribute it and/or modify
     2.9 + * it under the terms of the GNU General Public License as published by
    2.10 + * the Free Software Foundation, version 2 of the License.
    2.11 + *
    2.12 + * This program is distributed in the hope that it will be useful,
    2.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    2.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    2.15 + * GNU General Public License for more details. apidesign.org
    2.16 + * designates this particular file as subject to the
    2.17 + * "Classpath" exception as provided by apidesign.org
    2.18 + * in the License file that accompanied this code.
    2.19 + *
    2.20 + * You should have received a copy of the GNU General Public License
    2.21 + * along with this program. Look for COPYING file in the top folder.
    2.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
    2.23 + */
    2.24 +package net.java.html.js;
    2.25 +
    2.26 +import java.lang.annotation.ElementType;
    2.27 +import java.lang.annotation.Retention;
    2.28 +import java.lang.annotation.RetentionPolicy;
    2.29 +import java.lang.annotation.Target;
    2.30 +import net.java.html.boot.BrowserBuilder;
    2.31 +
    2.32 +/** When a class annotated by this annotation is loaded into the Java virtual
    2.33 + * machine by {@link BrowserBuilder} classloader, the script referenced by
    2.34 + * this annotation gets loaded into associated JavaScript executor environment.
    2.35 + *
    2.36 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    2.37 + */
    2.38 +@Retention(RetentionPolicy.CLASS)
    2.39 +@Target(ElementType.TYPE)
    2.40 +public @interface JavaScriptResource {
    2.41 +    /** The JavaScript file to load in before associated class can execute.
    2.42 +     * @return relative path with respect to the annotated class
    2.43 +     */
    2.44 +    public String value();
    2.45 +}
     3.1 --- a/boot/src/main/java/org/apidesign/html/boot/impl/FnUtils.java	Wed Jun 26 08:41:40 2013 +0200
     3.2 +++ b/boot/src/main/java/org/apidesign/html/boot/impl/FnUtils.java	Wed Jun 26 08:43:32 2013 +0200
     3.3 @@ -20,6 +20,9 @@
     3.4   */
     3.5  package org.apidesign.html.boot.impl;
     3.6  
     3.7 +import java.io.InputStream;
     3.8 +import java.io.InputStreamReader;
     3.9 +import java.io.Reader;
    3.10  import java.lang.reflect.Method;
    3.11  import java.net.URL;
    3.12  import java.util.ArrayList;
    3.13 @@ -65,6 +68,11 @@
    3.14              protected Fn defineFn(String code, String... names) {
    3.15                  return d.defineFn(code, names);
    3.16              }
    3.17 +
    3.18 +            @Override
    3.19 +            protected void loadScript(Reader code) throws Exception {
    3.20 +                d.loadScript(code);
    3.21 +            }
    3.22          };
    3.23      }
    3.24  
    3.25 @@ -145,5 +153,24 @@
    3.26          }
    3.27          return Class.forName(t.getClassName(), false, loader);
    3.28      }
    3.29 -    
    3.30 +
    3.31 +    static void loadScript(JsClassLoader jcl, String resource) {
    3.32 +        final InputStream script = jcl.getResourceAsStream(resource);
    3.33 +        if (script == null) {
    3.34 +            throw new NullPointerException("Can't find " + resource);
    3.35 +        }
    3.36 +        try {
    3.37 +            Reader isr = null;
    3.38 +            try {
    3.39 +                isr = new InputStreamReader(script, "UTF-8");
    3.40 +                jcl.loadScript(isr);
    3.41 +            } finally {
    3.42 +                if (isr != null) {
    3.43 +                    isr.close();
    3.44 +                }
    3.45 +            }
    3.46 +        } catch (Exception ex) {
    3.47 +            throw new IllegalStateException("Can't execute " + resource, ex);
    3.48 +        } 
    3.49 +    }
    3.50  }
     4.1 --- a/boot/src/main/java/org/apidesign/html/boot/impl/JsClassLoader.java	Wed Jun 26 08:41:40 2013 +0200
     4.2 +++ b/boot/src/main/java/org/apidesign/html/boot/impl/JsClassLoader.java	Wed Jun 26 08:43:32 2013 +0200
     4.3 @@ -23,6 +23,7 @@
     4.4  import org.apidesign.html.boot.spi.Fn;
     4.5  import java.io.IOException;
     4.6  import java.io.InputStream;
     4.7 +import java.io.Reader;
     4.8  import java.net.URL;
     4.9  import java.util.ArrayList;
    4.10  import java.util.Enumeration;
    4.11 @@ -126,7 +127,7 @@
    4.12      }
    4.13      
    4.14      protected abstract Fn defineFn(String code, String... names);
    4.15 -    
    4.16 +    protected abstract void loadScript(Reader code) throws Exception;
    4.17      
    4.18      private final class FindInClass extends ClassVisitor {
    4.19          private String name;
    4.20 @@ -141,7 +142,14 @@
    4.21              this.name = name;
    4.22              super.visit(version, access, name, signature, superName, interfaces);
    4.23          }
    4.24 -        
    4.25 +
    4.26 +        @Override
    4.27 +        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
    4.28 +            if ("Lnet/java/html/js/JavaScriptResource;".equals(desc)) {
    4.29 +                return new LoadResource();
    4.30 +            }
    4.31 +            return super.visitAnnotation(desc, visible);
    4.32 +        }
    4.33          
    4.34  
    4.35          @Override
    4.36 @@ -403,6 +411,24 @@
    4.37                  }
    4.38              }
    4.39          }
    4.40 +        
    4.41 +        private final class LoadResource extends AnnotationVisitor {
    4.42 +            public LoadResource() {
    4.43 +                super(Opcodes.ASM4);
    4.44 +            }
    4.45 +            
    4.46 +            @Override
    4.47 +            public void visit(String attrName, Object value)  {
    4.48 +                String relPath = (String) value;
    4.49 +                if (relPath.startsWith("/")) {
    4.50 +                    FnUtils.loadScript(JsClassLoader.this, relPath);
    4.51 +                } else {
    4.52 +                    int last = name.lastIndexOf('/');
    4.53 +                    String fullPath = name.substring(0, last + 1) + relPath;
    4.54 +                    FnUtils.loadScript(JsClassLoader.this, fullPath);
    4.55 +                }
    4.56 +            }
    4.57 +        }
    4.58      }
    4.59      
    4.60      private class ClassWriterEx extends ClassWriter {
     5.1 --- a/boot/src/main/java/org/apidesign/html/boot/spi/Fn.java	Wed Jun 26 08:41:40 2013 +0200
     5.2 +++ b/boot/src/main/java/org/apidesign/html/boot/spi/Fn.java	Wed Jun 26 08:43:32 2013 +0200
     5.3 @@ -20,8 +20,8 @@
     5.4   */
     5.5  package org.apidesign.html.boot.spi;
     5.6  
     5.7 +import java.io.Reader;
     5.8  import java.net.URL;
     5.9 -import java.util.Collection;
    5.10  
    5.11  /**
    5.12   *
    5.13 @@ -33,5 +33,6 @@
    5.14      public interface Presenter {
    5.15          public Fn defineFn(String code, String... names);
    5.16          public void displayPage(URL page, Runnable onPageLoad);
    5.17 +        public void loadScript(Reader code) throws Exception;
    5.18      }
    5.19  }
     6.1 --- a/boot/src/test/java/org/apidesign/html/boot/impl/FnTest.java	Wed Jun 26 08:41:40 2013 +0200
     6.2 +++ b/boot/src/test/java/org/apidesign/html/boot/impl/FnTest.java	Wed Jun 26 08:43:32 2013 +0200
     6.3 @@ -21,6 +21,7 @@
     6.4  
     6.5  package org.apidesign.html.boot.impl;
     6.6  
     6.7 +import java.io.Reader;
     6.8  import java.net.URL;
     6.9  import java.net.URLClassLoader;
    6.10  import java.util.ArrayList;
    6.11 @@ -102,6 +103,11 @@
    6.12              public void displayPage(URL resource, Runnable r) {
    6.13                  throw new UnsupportedOperationException();
    6.14              }
    6.15 +
    6.16 +            @Override
    6.17 +            public void loadScript(Reader code) throws Exception {
    6.18 +                eng.eval(code);
    6.19 +            }
    6.20          }
    6.21          Impl impl = new Impl();
    6.22          ClassLoader loader = FnUtils.newLoader(impl, impl, parent);
     7.1 --- a/boot/src/test/java/org/apidesign/html/boot/impl/JsClassLoaderBase.java	Wed Jun 26 08:41:40 2013 +0200
     7.2 +++ b/boot/src/test/java/org/apidesign/html/boot/impl/JsClassLoaderBase.java	Wed Jun 26 08:43:32 2013 +0200
     7.3 @@ -127,4 +127,13 @@
     7.4          Method st = methodClass.getMethod("sumArr", int[].class);
     7.5          assertEquals(st.invoke(null, new int[] { 1, 2, 3 }), 6, "1+2+3 is six");
     7.6      }
     7.7 +    
     7.8 +    @Test public void javaScriptResource() throws Throwable {
     7.9 +        try {
    7.10 +            Method st = methodClass.getMethod("useExternalMul", int.class, int.class);
    7.11 +            assertEquals(st.invoke(null, 6, 7), 42, "Meaning of JavaScript?");
    7.12 +        } catch (InvocationTargetException ex) {
    7.13 +            throw ex.getTargetException();
    7.14 +        }
    7.15 +    }
    7.16  }
    7.17 \ No newline at end of file
     8.1 --- a/boot/src/test/java/org/apidesign/html/boot/impl/JsClassLoaderTest.java	Wed Jun 26 08:41:40 2013 +0200
     8.2 +++ b/boot/src/test/java/org/apidesign/html/boot/impl/JsClassLoaderTest.java	Wed Jun 26 08:43:32 2013 +0200
     8.3 @@ -20,6 +20,7 @@
     8.4   */
     8.5  package org.apidesign.html.boot.impl;
     8.6  
     8.7 +import java.io.Reader;
     8.8  import org.apidesign.html.boot.spi.Fn;
     8.9  import java.net.URL;
    8.10  import java.net.URLClassLoader;
    8.11 @@ -94,6 +95,11 @@
    8.12              protected Enumeration<URL> findResources(String name) {
    8.13                  throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    8.14              }
    8.15 +
    8.16 +            @Override
    8.17 +            protected void loadScript(Reader code) throws ScriptException {
    8.18 +                eng.eval(code);
    8.19 +            }
    8.20          };
    8.21          
    8.22          methodClass = loader.loadClass(JsMethods.class.getName());
     9.1 --- a/boot/src/test/java/org/apidesign/html/boot/impl/JsMethods.java	Wed Jun 26 08:41:40 2013 +0200
     9.2 +++ b/boot/src/test/java/org/apidesign/html/boot/impl/JsMethods.java	Wed Jun 26 08:43:32 2013 +0200
     9.3 @@ -21,12 +21,14 @@
     9.4  package org.apidesign.html.boot.impl;
     9.5  
     9.6  import net.java.html.js.JavaScriptBody;
     9.7 +import net.java.html.js.JavaScriptResource;
     9.8  
     9.9  
    9.10  /**
    9.11   *
    9.12   * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    9.13   */
    9.14 +@JavaScriptResource("jsmethods.js")
    9.15  public class JsMethods {
    9.16      @JavaScriptBody(args = {}, body = "return 42;")
    9.17      public static Object fortyTwo() {
    9.18 @@ -67,4 +69,7 @@
    9.19      public static int sumArr(int... arr) {
    9.20          return sumArr(new Arithm(), arr);
    9.21      }
    9.22 +    
    9.23 +    @JavaScriptBody(args = { "x", "y" }, body = "return mul(x, y);")
    9.24 +    public static native int useExternalMul(int x, int y);
    9.25  }
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/boot/src/test/resources/org/apidesign/html/boot/impl/jsmethods.js	Wed Jun 26 08:43:32 2013 +0200
    10.3 @@ -0,0 +1,22 @@
    10.4 +/*
    10.5 + * HTML via Java(tm) Language Bindings
    10.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    10.7 + *
    10.8 + * This program is free software: you can redistribute it and/or modify
    10.9 + * it under the terms of the GNU General Public License as published by
   10.10 + * the Free Software Foundation, version 2 of the License.
   10.11 + *
   10.12 + * This program is distributed in the hope that it will be useful,
   10.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   10.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   10.15 + * GNU General Public License for more details. apidesign.org
   10.16 + * designates this particular file as subject to the
   10.17 + * "Classpath" exception as provided by apidesign.org
   10.18 + * in the License file that accompanied this code.
   10.19 + *
   10.20 + * You should have received a copy of the GNU General Public License
   10.21 + * along with this program. Look for COPYING file in the top folder.
   10.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
   10.23 + */
   10.24 +
   10.25 +function mul(x, y) { return x * y; }
    11.1 --- a/ko-fx/src/main/java/org/apidesign/html/kofx/Knockout.java	Wed Jun 26 08:41:40 2013 +0200
    11.2 +++ b/ko-fx/src/main/java/org/apidesign/html/kofx/Knockout.java	Wed Jun 26 08:43:32 2013 +0200
    11.3 @@ -27,6 +27,7 @@
    11.4  import java.util.logging.Level;
    11.5  import java.util.logging.Logger;
    11.6  import net.java.html.js.JavaScriptBody;
    11.7 +import net.java.html.js.JavaScriptResource;
    11.8  import net.java.html.json.Model;
    11.9  import netscape.javascript.JSObject;
   11.10  import org.apidesign.html.json.spi.FunctionBinding;
   11.11 @@ -41,6 +42,7 @@
   11.12   *
   11.13   * @author Jaroslav Tulach <jtulach@netbeans.org>
   11.14   */
   11.15 +@JavaScriptResource("knockout-2.2.1.js")
   11.16  public final class Knockout {
   11.17      private static final Logger LOG = Logger.getLogger(Knockout.class.getName());
   11.18      /** used by tests */
   11.19 @@ -110,25 +112,6 @@
   11.20          static final JSObject KObject;
   11.21  
   11.22          static {
   11.23 -            final InputStream koScript = Knockout.class.getResourceAsStream("knockout-2.2.1.js");
   11.24 -            assert koScript != null : "Can't load knockout.js";
   11.25 -            BufferedReader r = new BufferedReader(new InputStreamReader(koScript));
   11.26 -            StringBuilder sb = new StringBuilder();
   11.27 -            for (;;) {
   11.28 -                try {
   11.29 -                    String l = r.readLine();
   11.30 -                    if (l == null) {
   11.31 -                        break;
   11.32 -                    }
   11.33 -                    sb.append(l).append('\n');
   11.34 -                } catch (IOException ex) {
   11.35 -                    throw new IllegalStateException(ex);
   11.36 -                }
   11.37 -            }
   11.38 -            exec(sb.toString());
   11.39 -            Object ko = exec("ko");
   11.40 -            assert ko != null : "Knockout library successfully defined 'ko'";
   11.41 -
   11.42              Console.register();
   11.43              KObject = (JSObject) kObj();
   11.44          }