Having 'invoke' method on the main class is more natural than Class[1] and Runnable callback
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();