Load global scripts when first method in the class is called
diff --git a/boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersOnResourceTest.java b/boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersOnResourceTest.java
new file mode 100644
index 0000000..cb0dfd5
--- /dev/null
+++ b/boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersOnResourceTest.java
@@ -0,0 +1,180 @@
+/**
+ * HTML via Java(tm) Language Bindings
+ * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details. apidesign.org
+ * designates this particular file as subject to the
+ * "Classpath" exception as provided by apidesign.org
+ * in the License file that accompanied this code.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. Look for COPYING file in the top folder.
+ * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
+ */
+package net.java.html.boot.fx;
+
+import java.net.URL;
+import java.util.concurrent.CountDownLatch;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.scene.Scene;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.web.WebView;
+import javafx.stage.Stage;
+import net.java.html.js.JavaScriptBody;
+import net.java.html.js.JavaScriptResource;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNotSame;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
+ */
+public class FXBrowsersOnResourceTest {
+
+ public FXBrowsersOnResourceTest() {
+ }
+
+ @BeforeClass public void initFX() throws Throwable {
+ new Thread("initFX") {
+ @Override
+ public void run() {
+ App.launch(App.class);
+ }
+ }.start();
+ App.CDL.await();
+ }
+
+ @Test
+ public void behaviorOfTwoWebViewsAtOnce() throws Throwable {
+ class R implements Runnable {
+ CountDownLatch DONE = new CountDownLatch(1);
+ Throwable t;
+
+ @Override
+ public void run() {
+ try {
+ doTest();
+ } catch (Throwable ex) {
+ t = ex;
+ } finally {
+ DONE.countDown();
+ }
+ }
+
+ private void doTest() throws Throwable {
+ URL u = FXBrowsersOnResourceTest.class.getResource("/org/apidesign/html/boot/fx/empty.html");
+ assertNotNull(u, "URL found");
+ FXBrowsers.load(App.getV1(), u, OnPages.class, "first");
+
+ }
+ }
+ R run = new R();
+ Platform.runLater(run);
+ run.DONE.await();
+ for (int i = 0; i < 100; i++) {
+ if (run.t != null) {
+ throw run.t;
+ }
+ if (System.getProperty("finalSecond") == null) {
+ Thread.sleep(100);
+ }
+ }
+
+
+
+ assertEquals(Integer.getInteger("finalFirst"), Integer.valueOf(3), "Three times in view one");
+ assertEquals(Integer.getInteger("finalSecond"), Integer.valueOf(2), "Two times in view one");
+ }
+
+ @JavaScriptResource("wnd.js")
+ public static class OnPages {
+ static Class<?> first;
+ static Object firstWindow;
+
+ public static void first() {
+ first = OnPages.class;
+ firstWindow = window();
+ assertNotNull(firstWindow, "First window found");
+
+ assertEquals(increment(), 1, "Now it is one");
+
+ URL u = FXBrowsersOnResourceTest.class.getResource("/org/apidesign/html/boot/fx/empty.html");
+ assertNotNull(u, "URL found");
+ FXBrowsers.load(App.getV2(), u, OnPages.class, "second", "Hello");
+
+ assertEquals(increment(), 2, "Now it is two and not influenced by second view");
+ System.setProperty("finalFirst", "" + increment());
+ }
+
+ public static void second(String... args) {
+ assertEquals(args.length, 1, "One string argument");
+ assertEquals(args[0], "Hello", "It is hello");
+ assertEquals(first, OnPages.class, "Both views share the same classloader");
+
+ Object window = window();
+ assertNotNull(window, "Some window found");
+ assertNotNull(firstWindow, "First window is known");
+ assertNotSame(firstWindow, window, "The window objects should be different");
+
+ assertEquals(increment(), 1, "Counting starts from zero");
+ System.setProperty("finalSecond", "" + increment());
+ }
+
+ @JavaScriptBody(args = {}, body = "return wnd;")
+ private static native Object window();
+
+ @JavaScriptBody(args = {}, body = ""
+ + "if (wnd.cnt) return ++wnd.cnt;"
+ + "return wnd.cnt = 1;"
+ )
+ private static native int increment();
+ }
+
+ public static class App extends Application {
+ static final CountDownLatch CDL = new CountDownLatch(1);
+ private static BorderPane pane;
+
+ /**
+ * @return the v1
+ */
+ static WebView getV1() {
+ return (WebView)System.getProperties().get("v1");
+ }
+
+ /**
+ * @return the v2
+ */
+ static WebView getV2() {
+ return (WebView)System.getProperties().get("v2");
+ }
+
+ @Override
+ public void start(Stage stage) throws Exception {
+ pane= new BorderPane();
+ Scene scene = new Scene(pane, 800, 600);
+ stage.setScene(scene);
+
+ System.getProperties().put("v1", new WebView());
+ System.getProperties().put("v2", new WebView());
+
+ pane.setCenter(getV1());
+ pane.setBottom(getV2());
+
+ stage.show();
+ CDL.countDown();
+ }
+
+
+ }
+}
diff --git a/boot-fx/src/test/resources/net/java/html/boot/fx/wnd.js b/boot-fx/src/test/resources/net/java/html/boot/fx/wnd.js
new file mode 100644
index 0000000..fabd851
--- /dev/null
+++ b/boot-fx/src/test/resources/net/java/html/boot/fx/wnd.js
@@ -0,0 +1,27 @@
+/*
+ * HTML via Java(tm) Language Bindings
+ * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details. apidesign.org
+ * designates this particular file as subject to the
+ * "Classpath" exception as provided by apidesign.org
+ * in the License file that accompanied this code.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. Look for COPYING file in the top folder.
+ * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
+ */
+if (typeof wnd !== 'undefined') {
+ throw 'Window should not be defined yet: ' + wnd;
+}
+
+wnd = {
+};
+
diff --git a/boot/src/main/java/org/apidesign/html/boot/impl/FnUtils.java b/boot/src/main/java/org/apidesign/html/boot/impl/FnUtils.java
index 0f5da49..367a641 100644
--- a/boot/src/main/java/org/apidesign/html/boot/impl/FnUtils.java
+++ b/boot/src/main/java/org/apidesign/html/boot/impl/FnUtils.java
@@ -156,6 +156,7 @@
private String name;
private int found;
private ClassLoader loader;
+ private String resource;
public FindInClass(ClassLoader l, ClassVisitor cv) {
super(Opcodes.ASM4, cv);
@@ -263,6 +264,14 @@
"org/apidesign/html/boot/spi/Fn", "define",
"(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/String;)Lorg/apidesign/html/boot/spi/Fn;"
);
+ if (resource != null) {
+ super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
+ super.visitLdcInsn(resource);
+ super.visitMethodInsn(Opcodes.INVOKESTATIC,
+ "org/apidesign/html/boot/spi/Fn", "preload",
+ "(Lorg/apidesign/html/boot/spi/Fn;Ljava/lang/Class;Ljava/lang/String;)Lorg/apidesign/html/boot/spi/Fn;"
+ );
+ }
super.visitInsn(Opcodes.DUP);
super.visitFieldInsn(
Opcodes.PUTSTATIC, FindInClass.this.name,
@@ -487,11 +496,11 @@
public void visit(String attrName, Object value) {
String relPath = (String) value;
if (relPath.startsWith("/")) {
- loadScript(loader, relPath);
+ resource = relPath;
} else {
int last = name.lastIndexOf('/');
String fullPath = name.substring(0, last + 1) + relPath;
- loadScript(loader, fullPath);
+ resource = fullPath;
}
}
}
diff --git a/boot/src/main/java/org/apidesign/html/boot/spi/Fn.java b/boot/src/main/java/org/apidesign/html/boot/spi/Fn.java
index 4de2960..a0cea41 100644
--- a/boot/src/main/java/org/apidesign/html/boot/spi/Fn.java
+++ b/boot/src/main/java/org/apidesign/html/boot/spi/Fn.java
@@ -21,8 +21,15 @@
package org.apidesign.html.boot.spi;
import java.io.Closeable;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
import net.java.html.js.JavaScriptBody;
import org.apidesign.html.boot.impl.FnContext;
@@ -87,6 +94,31 @@
return FnContext.currentPresenter().defineFn(code, names);
}
+ private static final Map<String,Set<Presenter>> LOADED = new HashMap<String, Set<Presenter>>();
+ public static Fn preload(final Fn fn, final Class<?> caller, final String resource) {
+ return new Fn() {
+ @Override
+ public Object invoke(Object thiz, Object... args) throws Exception {
+ final Presenter p = FnContext.currentPresenter();
+ Set<Presenter> there = LOADED.get(resource);
+ if (there == null) {
+ there = new HashSet<Presenter>();
+ LOADED.put(resource, there);
+ }
+ if (there.add(p)) {
+ InputStream is = caller.getClassLoader().getResourceAsStream(resource);
+ try {
+ InputStreamReader r = new InputStreamReader(is, "UTF-8");
+ p.loadScript(r);
+ } finally {
+ is.close();
+ }
+ }
+ return fn.invoke(thiz, args);
+ }
+ };
+ }
+
/** The currently active presenter.
*
* @return the currently active presenter or <code>null</code>