Having 'invoke' method on the main class is more natural than Class[1] and Runnable callback classloader
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Tue, 25 Jun 2013 10:20:42 +0200
branchclassloader
changeset 1565a1c6513604d
parent 155 7391d9437a03
child 157 b2e448adfa59
Having 'invoke' method on the main class is more natural than Class[1] and Runnable callback
boot/src/main/java/net/java/html/boot/BrowserBuilder.java
ko-archetype/src/main/resources/archetype-resources/src/main/java/Main.java
ko-archetype/src/main/resources/archetype-resources/src/main/java/TwitterClient.java
ko-fx/src/test/java/org/apidesign/html/kofx/KnockoutFXTest.java
     1.1 --- a/boot/src/main/java/net/java/html/boot/BrowserBuilder.java	Tue Jun 25 09:31:26 2013 +0200
     1.2 +++ b/boot/src/main/java/net/java/html/boot/BrowserBuilder.java	Tue Jun 25 10:20:42 2013 +0200
     1.3 @@ -21,11 +21,15 @@
     1.4  package net.java.html.boot;
     1.5  
     1.6  import java.io.IOException;
     1.7 +import java.lang.reflect.Method;
     1.8  import java.net.MalformedURLException;
     1.9  import java.net.URL;
    1.10 +import java.util.Arrays;
    1.11  import java.util.Collection;
    1.12  import java.util.Enumeration;
    1.13  import java.util.ServiceLoader;
    1.14 +import java.util.logging.Level;
    1.15 +import java.util.logging.Logger;
    1.16  import org.apidesign.html.boot.impl.FnUtils;
    1.17  import org.apidesign.html.boot.spi.Fn;
    1.18  import org.apidesign.html.boot.impl.FindResources;
    1.19 @@ -35,10 +39,15 @@
    1.20   * @author Jaroslav Tulach <jtulach@netbeans.org>
    1.21   */
    1.22  public final class BrowserBuilder {
    1.23 +    private static final Logger LOG = Logger.getLogger(BrowserBuilder.class.getName());
    1.24 +    
    1.25      private String resource;
    1.26      private Class<?> clazz;
    1.27      private Class[] browserClass;
    1.28      private Runnable onLoad;
    1.29 +    private String methodName;
    1.30 +    private String[] methodArgs;
    1.31 +    
    1.32      private BrowserBuilder() {
    1.33      }
    1.34      
    1.35 @@ -55,6 +64,13 @@
    1.36          this.clazz = mainClass;
    1.37          return this;
    1.38      }
    1.39 +
    1.40 +    // invoked on browser thread
    1.41 +    public BrowserBuilder invoke(String methodName, String... args) {
    1.42 +        this.methodName = methodName;
    1.43 +        this.methodArgs = args;
    1.44 +        return this;
    1.45 +    }
    1.46      
    1.47      public void showAndWait() {
    1.48          if (resource == null) {
    1.49 @@ -94,8 +110,29 @@
    1.50                          if (onLoad != null) {
    1.51                              onLoad.run();
    1.52                          }
    1.53 +                        INIT: if (methodName != null) {
    1.54 +                            Exception firstError = null;
    1.55 +                            if (methodArgs.length == 0) {
    1.56 +                                try {
    1.57 +                                    Method m = newClazz.getMethod(methodName);
    1.58 +                                    m.invoke(null);
    1.59 +                                    break INIT;
    1.60 +                                } catch (Exception ex) {
    1.61 +                                    firstError = ex;
    1.62 +                                }
    1.63 +                            }
    1.64 +                            try {
    1.65 +                                Method m = newClazz.getMethod(methodName, String[].class);
    1.66 +                                m.invoke(m, (Object) methodArgs);
    1.67 +                            } catch (Exception ex) {
    1.68 +                                if (firstError != null) {
    1.69 +                                    LOG.log(Level.SEVERE, "Can't call " + methodName, firstError);
    1.70 +                                }
    1.71 +                                LOG.log(Level.SEVERE, "Can't call " + methodName + " with args " + Arrays.toString(methodArgs), ex);
    1.72 +                            }
    1.73 +                        }
    1.74                      } catch (ClassNotFoundException ex) {
    1.75 -                        throw new IllegalStateException(ex);
    1.76 +                        LOG.log(Level.SEVERE, "Can't load " + clazz.getName(), ex);
    1.77                      }
    1.78                  }
    1.79              }
    1.80 @@ -105,20 +142,6 @@
    1.81          throw new IllegalStateException("Can't find any Fn.Definer");
    1.82      }
    1.83  
    1.84 -    public BrowserBuilder onClassReady(Class[] browserClass) {
    1.85 -        if (browserClass.length != 1) {
    1.86 -            throw new IllegalStateException("Expecting array of size one");
    1.87 -        }
    1.88 -        this.browserClass = browserClass;
    1.89 -        return this;
    1.90 -    }
    1.91 -
    1.92 -    // callback happens on browser thread
    1.93 -    public BrowserBuilder onLoad(Runnable r) {
    1.94 -        this.onLoad = r;
    1.95 -        return this;
    1.96 -    }
    1.97 -    
    1.98      private static final class FImpl implements FindResources {
    1.99          final ClassLoader l;
   1.100  
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/ko-archetype/src/main/resources/archetype-resources/src/main/java/Main.java	Tue Jun 25 10:20:42 2013 +0200
     2.3 @@ -0,0 +1,16 @@
     2.4 +package ${package};
     2.5 +
     2.6 +import net.java.html.boot.BrowserBuilder;
     2.7 +
     2.8 +public final class Main {
     2.9 +    private Main() {
    2.10 +    }
    2.11 +    
    2.12 +    public static void main(String... args) throws Exception {
    2.13 +        BrowserBuilder.newBrowser().
    2.14 +            loadPage("index.html").
    2.15 +            loadClass(TwitterClient.class).
    2.16 +            invoke("initialize", args).
    2.17 +            showAndWait();
    2.18 +    }
    2.19 +}
     3.1 --- a/ko-archetype/src/main/resources/archetype-resources/src/main/java/TwitterClient.java	Tue Jun 25 09:31:26 2013 +0200
     3.2 +++ b/ko-archetype/src/main/resources/archetype-resources/src/main/java/TwitterClient.java	Tue Jun 25 10:20:42 2013 +0200
     3.3 @@ -90,7 +90,7 @@
     3.4          model.queryTweets("http://search.twitter.com", sb.toString());
     3.5      }
     3.6      
     3.7 -    static {
     3.8 +    public static void initialize(String... args) {
     3.9          final TwitterModel model = new TwitterModel();
    3.10          final List<Tweeters> svdLst = model.getSavedLists();
    3.11          svdLst.add(newTweeters("API Design", "JaroslavTulach"));
     4.1 --- a/ko-fx/src/test/java/org/apidesign/html/kofx/KnockoutFXTest.java	Tue Jun 25 09:31:26 2013 +0200
     4.2 +++ b/ko-fx/src/test/java/org/apidesign/html/kofx/KnockoutFXTest.java	Tue Jun 25 10:20:42 2013 +0200
     4.3 @@ -53,47 +53,35 @@
     4.4   */
     4.5  @ServiceProvider(service = KnockoutTCK.class)
     4.6  public final class KnockoutFXTest extends KnockoutTCK {
     4.7 +    private static Class<?> browserClass;
     4.8 +    
     4.9      public KnockoutFXTest() {
    4.10      }
    4.11      
    4.12      @Factory public static Object[] compatibilityTests() throws Exception {
    4.13          Class[] arr = testClasses();
    4.14 -        ClassLoader l = KnockoutFXTest.class.getClassLoader();
    4.15          for (int i = 0; i < arr.length; i++) {
    4.16 -            assertEquals(arr[i].getClassLoader(), l, "All classes loaded by the same classloader");
    4.17 +            assertEquals(
    4.18 +                arr[i].getClassLoader(),
    4.19 +                KnockoutFXTest.class.getClassLoader(),
    4.20 +                "All classes loaded by the same classloader"
    4.21 +            );
    4.22          }
    4.23 -        assertNotNull(l, "At least one class provided");
    4.24 -        
    4.25 -        class R implements Runnable {
    4.26 -            final Class[] browserClass = { null };
    4.27 -            @Override
    4.28 -            public synchronized void run() {
    4.29 -                notifyAll();
    4.30 -            }
    4.31 -            
    4.32 -            synchronized ClassLoader getClassLoader() throws InterruptedException {
    4.33 -                while (browserClass[0] == null) {
    4.34 -                    wait();
    4.35 -                }
    4.36 -                return browserClass[0].getClassLoader();
    4.37 -            }
    4.38 -        }
    4.39 -        R r = new R();
    4.40          
    4.41          URI uri = DynamicHTTP.initServer();
    4.42 -        
    4.43 +    
    4.44          final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(KnockoutFXTest.class).
    4.45              loadPage(uri.toString()).
    4.46 -            onClassReady(r.browserClass).
    4.47 -            onLoad(r);
    4.48 +            invoke("initialized");
    4.49 +        
    4.50          Executors.newSingleThreadExecutor().submit(new Runnable() {
    4.51              @Override
    4.52              public void run() {
    4.53                  bb.showAndWait();
    4.54              }
    4.55          });
    4.56 -
    4.57 -        l = r.getClassLoader();
    4.58 +        
    4.59 +        ClassLoader l = getClassLoader();
    4.60          List<Object> res = new ArrayList<Object>();
    4.61          for (int i = 0; i < arr.length; i++) {
    4.62              Class<?> c = Class.forName(arr[i].getName(), true, l);
    4.63 @@ -109,6 +97,24 @@
    4.64          return res.toArray();
    4.65      }
    4.66  
    4.67 +    static synchronized ClassLoader getClassLoader() throws InterruptedException {
    4.68 +        while (browserClass == null) {
    4.69 +            KnockoutFXTest.class.wait();
    4.70 +        }
    4.71 +        return browserClass.getClassLoader();
    4.72 +    }
    4.73 +    
    4.74 +    public static synchronized void initialized(Class<?> browserCls) throws Exception {
    4.75 +        browserClass = browserCls;
    4.76 +        KnockoutFXTest.class.notifyAll();
    4.77 +    }
    4.78 +    
    4.79 +    public static void initialized() throws Exception {
    4.80 +        Class<?> classpathClass = ClassLoader.getSystemClassLoader().loadClass(KnockoutFXTest.class.getName());
    4.81 +        Method m = classpathClass.getMethod("initialized", Class.class);
    4.82 +        m.invoke(null, KnockoutFXTest.class);
    4.83 +    }
    4.84 +    
    4.85      @Override
    4.86      public BrwsrCtx createContext() {
    4.87          FXContext fx = new FXContext();