http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot-truffle/src/test/java/net/java/html/boot/truffle/TruffleJavaScriptTest.java ---------------------------------------------------------------------- diff --git a/boot-truffle/src/test/java/net/java/html/boot/truffle/TruffleJavaScriptTest.java b/boot-truffle/src/test/java/net/java/html/boot/truffle/TruffleJavaScriptTest.java new file mode 100644 index 0000000..ac4c4f5 --- /dev/null +++ b/boot-truffle/src/test/java/net/java/html/boot/truffle/TruffleJavaScriptTest.java @@ -0,0 +1,164 @@ +/** + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package net.java.html.boot.truffle; + +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.vm.PolyglotEngine; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executors; +import net.java.html.boot.BrowserBuilder; +import org.netbeans.html.boot.spi.Fn; +import org.netbeans.html.json.tck.JavaScriptTCK; +import org.netbeans.html.json.tck.KOTest; +import org.testng.Assert; +import org.testng.SkipException; +import org.testng.annotations.Factory; +import org.testng.annotations.Test; +import static org.testng.Assert.assertEquals; + +/** + * + * @author Jaroslav Tulach + */ +public class TruffleJavaScriptTest { + private static Class<?> browserClass; + private static Fn.Presenter browserPresenter; + + public TruffleJavaScriptTest() { + } + + @Factory public static Object[] compatibilityTests() throws Exception { + PolyglotEngine engine = PolyglotEngine.newBuilder().build(); + PolyglotEngine.Value result = null; + try { + result = engine.eval(Source.fromText("6 * 7", "test.js").withMimeType("text/javascript")); + } catch (Exception notSupported) { + if (notSupported.getMessage().contains("text/javascript")) { + return new Object[] { new Skip(true, notSupported.getMessage()) }; + } + } + assertEquals(42, result.as(Number.class).intValue(), "Executed OK"); + + final BrowserBuilder bb = BrowserBuilder.newBrowser(TrufflePresenters.create(SingleCase.JS)). + loadClass(TruffleJavaScriptTest.class). + loadPage("empty.html"). + invoke("initialized"); + + Executors.newSingleThreadExecutor().submit(new Runnable() { + @Override + public void run() { + bb.showAndWait(); + } + }); + + List<Object> res = new ArrayList<>(); + Class<? extends Annotation> test = + loadClass().getClassLoader().loadClass(KOTest.class.getName()). + asSubclass(Annotation.class); + + Class[] arr = (Class[]) loadClass().getDeclaredMethod("tests").invoke(null); + for (Class c : arr) { + if (c.getSimpleName().contains("GC")) { + continue; + } + for (Method m : c.getMethods()) { + if (m.getAnnotation(test) != null) { + res.add(new SingleCase(browserPresenter, m)); + } + } + } + return res.toArray(); + } + + static synchronized Class<?> loadClass() throws InterruptedException { + while (browserClass == null) { + TruffleJavaScriptTest.class.wait(); + } + return browserClass; + } + + public static synchronized void ready(Class<?> browserCls) throws Exception { + browserClass = browserCls; + browserPresenter = Fn.activePresenter(); + TruffleJavaScriptTest.class.notifyAll(); + } + + public static void initialized() throws Exception { + Assert.assertSame(TruffleJavaScriptTest.class.getClassLoader(), + ClassLoader.getSystemClassLoader(), + "No special classloaders" + ); + TruffleJavaScriptTest.ready(Tck.class); + } + + public static final class Tck extends JavaScriptTCK { + + public static Class[] tests() { + return testClasses(); + } + } + + public static final class Skip { + private final String message; + private final boolean fail; + + public Skip(String message) { + this(false, message); + } + + Skip(boolean fail, String message) { + this.message = message; + this.fail = fail; + } + + @Test + public void needsGraalVMToExecuteTheTests() { + if (fail) { + throw new SkipException(message); + } + } + } +}
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/pom.xml ---------------------------------------------------------------------- diff --git a/boot/pom.xml b/boot/pom.xml new file mode 100644 index 0000000..e1ec2ff --- /dev/null +++ b/boot/pom.xml @@ -0,0 +1,106 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + + Copyright 2013-2016 Oracle and/or its affiliates. All rights reserved. + + Oracle and Java are registered trademarks of Oracle and/or its affiliates. + Other names may be trademarks of their respective owners. + + The contents of this file are subject to the terms of either the GNU + General Public License Version 2 only ("GPL") or the Common + Development and Distribution License("CDDL") (collectively, the + "License"). You may not use this file except in compliance with the + License. You can obtain a copy of the License at + http://www.netbeans.org/cddl-gplv2.html + or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + specific language governing permissions and limitations under the + License. When distributing the software, include this License Header + Notice in each file and include the License file at + nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + particular file as subject to the "Classpath" exception as provided + by Oracle in the GPL Version 2 section of the License file that + accompanied this code. If applicable, add the following below the + License Header, with the fields enclosed by brackets [] replaced by + your own identifying information: + "Portions Copyrighted [year] [name of copyright owner]" + + Contributor(s): + + The Original Software is NetBeans. The Initial Developer of the Original + Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved. + + If you wish your version of this file to be governed by only the CDDL + or only the GPL Version 2, indicate your decision by adding + "[Contributor] elects to include this software in this distribution + under the [CDDL or GPL Version 2] license." If you do not indicate a + single choice of license, a recipient has the option to distribute + your version of this file under either the CDDL, the GPL Version 2 or + to extend the choice of license to its licensees as provided above. + However, if you add GPL Version 2 code and therefore, elected the GPL + Version 2 license, then the option applies only if the new code is + made subject to such option by the copyright holder. + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.netbeans.html</groupId> + <artifactId>pom</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>org.netbeans.html</groupId> + <artifactId>net.java.html.boot</artifactId> + <version>2.0-SNAPSHOT</version> + <packaging>bundle</packaging> + <name>Browser Bootstrap</name> + <url>http://maven.apache.org</url> + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <publicPackages>net.java.html.js,net.java.html.boot,org.netbeans.html.boot.spi</publicPackages> + </properties> + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <configuration> + <instructions> + <Agent-Class>org.netbeans.html.boot.impl.JsAgent</Agent-Class> + <Premain-Class>org.netbeans.html.boot.impl.JsAgent</Premain-Class> + <Eclipse-BuddyPolicy>dependent</Eclipse-BuddyPolicy> + <Require-Capability>osgi.extender;resolution:=optional;filter:="(osgi.extender=osgi.serviceloader.processor)",osgi.serviceloader;filter:="(osgi.serviceloader=org.netbeans.html.boot.spi.Fn$Presenter)";cardinality:=multiple;resolution:=optional</Require-Capability> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + <dependencies> + <dependency> + <groupId>org.ow2.asm</groupId> + <artifactId>asm</artifactId> + <type>jar</type> + <scope>provided</scope> + <optional>true</optional> + </dependency> + <dependency> + <groupId>org.testng</groupId> + <artifactId>testng</artifactId> + <scope>test</scope> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.netbeans.api</groupId> + <artifactId>org-openide-util-lookup</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.netbeans.html</groupId> + <artifactId>net.java.html</artifactId> + <version>${project.version}</version> + <type>jar</type> + </dependency> + </dependencies> + <description>Builder to launch your Java/HTML based application.</description> +</project> http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/net/java/html/boot/BrowserBuilder.java ---------------------------------------------------------------------- diff --git a/boot/src/main/java/net/java/html/boot/BrowserBuilder.java b/boot/src/main/java/net/java/html/boot/BrowserBuilder.java new file mode 100644 index 0000000..3bb8d52 --- /dev/null +++ b/boot/src/main/java/net/java/html/boot/BrowserBuilder.java @@ -0,0 +1,519 @@ +/** + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package net.java.html.boot; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.net.HttpURLConnection; +import java.net.JarURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Locale; +import java.util.ServiceLoader; +import java.util.concurrent.Executor; +import java.util.logging.Level; +import java.util.logging.Logger; +import net.java.html.BrwsrCtx; +import net.java.html.js.JavaScriptBody; +import org.netbeans.html.boot.spi.Fn; +import org.netbeans.html.boot.spi.Fn.Presenter; +import org.netbeans.html.context.spi.Contexts; +import org.netbeans.html.context.spi.Contexts.Id; +import org.netbeans.html.boot.impl.FindResources; +import org.netbeans.html.boot.impl.FnContext; + +/** Use this builder to launch your Java/HTML based application. Typical + * usage in a main method of your application looks like this: + * <pre> + * + * <b>public static void</b> <em>main</em>(String... args) { + * BrowserBuilder.{@link #newBrowser newBrowser()}. + * {@link #loadClass(java.lang.Class) loadClass(YourMain.class)}. + * {@link #loadPage(java.lang.String) loadPage("index.html")}. + * {@link #locale(java.util.Locale) locale}({@link Locale#getDefault()}). + * {@link #invoke(java.lang.String, java.lang.String[]) invoke("initialized", args)}. + * {@link #showAndWait()}; + * System.exit(0); + * } + * </pre> + * The above will load <code>YourMain</code> class via + * a special classloader, it will locate an <code>index.html</code> (relative + * to <code>YourMain</code> class) and show it in a browser window. When the + * initialization is over, a <b>public static</b> method <em>initialized</em> + * in <code>YourMain</code> will be called with provided string parameters. + * <p> + * This module provides only API for building browsers. To use it properly one + * also needs an implementation on the classpath of one's application. For example + * use: <pre> + * <dependency> + * <groupId>org.netbeans.html</groupId> + * <artifactId>net.java.html.boot.fx</artifactId> + * <scope>runtime</scope> + * </dependency> + * </pre> + * + * @author Jaroslav Tulach + */ +public final class BrowserBuilder { + private static final Logger LOG = Logger.getLogger(BrowserBuilder.class.getName()); + + private String resource; + private Class<?> clazz; + private Runnable onLoad; + private String methodName; + private String[] methodArgs; + private final Object[] context; + private ClassLoader loader; + private Locale locale; + + private BrowserBuilder(Object[] context) { + this.context = context; + } + + /** Entry method to obtain a new browser builder. Follow by calling + * its instance methods like {@link #loadClass(java.lang.Class)} and + * {@link #loadPage(java.lang.String)}. + * Since introduction of {@link Id technology identifiers} the + * provided <code>context</code> objects are also passed to the + * {@link BrwsrCtx context} when it is being + * {@link Contexts#newBuilder(java.lang.Object...) created} + * and can influence the selection + * of available technologies + * (like {@link org.netbeans.html.json.spi.Technology}, + * {@link org.netbeans.html.json.spi.Transfer} or + * {@link org.netbeans.html.json.spi.WSTransfer}) by name. + * + * @param context any instances that should be available to the builder - + * implementation dependant + * @return new browser builder + */ + public static BrowserBuilder newBrowser(Object... context) { + return new BrowserBuilder(context); + } + + /** The class to load when the browser is initialized. This class + * is loaded by a special classloader (that supports {@link JavaScriptBody} + * and co.). + * + * @param mainClass the class to load and resolve when the browser is ready + * @return this builder + */ + public BrowserBuilder loadClass(Class<?> mainClass) { + this.clazz = mainClass; + return this; + } + + /** Allows one to specify a runnable that should be invoked when a load + * of a page is finished. This method may be used in addition or instead + * of {@link #loadClass(java.lang.Class)} and + * {@link #invoke(java.lang.String, java.lang.String...)} methods. + * + * @param r the code to run when the page is loaded + * @return this builder + * @since 0.8.1 + */ + public BrowserBuilder loadFinished(Runnable r) { + this.onLoad = r; + return this; + } + + /** Page to load into the browser. If the <code>page</code> represents + * a {@link URL} known to the Java system, the URL is passed to the browser. + * If system property <code>browser.rootdir</code> is specified, then a + * file <code>page</code> relative to this directory is used as the URL. + * If no such file exists, the system seeks for the + * resource via {@link Class#getResource(java.lang.String)} + * method (relative to the {@link #loadClass(java.lang.Class) specified class}). + * If such resource is not found, a file relative to the location JAR + * that contains the {@link #loadClass(java.lang.Class) main class} is + * searched for. + * <p> + * The search honors provided {@link #locale}, if specified. + * E.g. it will prefer <code>index_cs.html</code> over <code>index.html</code> + * if the locale is set to <code>cs_CZ</code>. + * + * @param page the location (relative, absolute, or URL) of a page to load + * @return this builder + */ + public BrowserBuilder loadPage(String page) { + this.resource = page; + return this; + } + + /** Locale to use when searching for an initial {@link #loadPage(java.lang.String) page to load}. + * Localization is best done by providing different versions of the + * initial page with appropriate suffixes (like <code>index_cs.html</code>). + * Then one can call this method with value of {@link Locale#getDefault()} + * to instruct the builder to use the user's current locale. + * + * @param locale the locale to use or <code>null</code> if no suffix search should be performed + * @return this builder + * @since 1.0 + */ + public BrowserBuilder locale(Locale locale) { + this.locale = locale; + return this; + } + + /** Specifies callback method to notify the application that the browser is ready. + * There should be a <b>public static</b> method in the class specified + * by {@link #loadClass(java.lang.Class)} which takes an array of {@link String} + * argument. The method is called on the browser dispatch thread one + * the browser finishes loading of the {@link #loadPage(java.lang.String) HTML page}. + * + * @param methodName name of a method to seek for + * @param args parameters to pass to the method + * @return this builder + */ + public BrowserBuilder invoke(String methodName, String... args) { + this.methodName = methodName; + this.methodArgs = args; + return this; + } + + /** Loader to use when searching for classes to initialize. + * If specified, this loader is going to be used to load {@link Presenter} + * and {@link Contexts#fillInByProviders(java.lang.Class, org.netbeans.html.context.spi.Contexts.Builder) fill} {@link BrwsrCtx} in. + * Specifying special classloader may be useful in modular systems, + * like OSGi, where one needs to load classes from many otherwise independent + * modules. + * + * @param l the loader to use (or <code>null</code>) + * @return this builder + * @since 0.9 + */ + public BrowserBuilder classloader(ClassLoader l) { + this.loader = l; + return this; + } + + /** Shows the browser, loads specified page in and executes the + * {@link #invoke(java.lang.String, java.lang.String[]) initialization method}. + * The method returns when the browser is closed. + * + * @throws NullPointerException if some of essential parameters (like {@link #loadPage(java.lang.String) page} or + * {@link #loadClass(java.lang.Class) class} have not been specified + */ + public void showAndWait() { + if (resource == null) { + throw new NullPointerException("Need to specify resource via loadPage method"); + } + + final Class<?> myCls; + if (clazz != null) { + myCls = clazz; + } else if (onLoad != null) { + myCls = onLoad.getClass(); + } else { + throw new NullPointerException("loadClass, neither loadFinished was called!"); + } + IOException mal[] = { null }; + URL url = findLocalizedResourceURL(resource, locale, mal, myCls); + + Fn.Presenter dfnr = null; + for (Object o : context) { + if (o instanceof Fn.Presenter) { + dfnr = (Fn.Presenter)o; + break; + } + } + + if (dfnr == null && loader != null) for (Fn.Presenter o : ServiceLoader.load(Fn.Presenter.class, loader)) { + dfnr = o; + break; + } + + if (dfnr == null) for (Fn.Presenter o : ServiceLoader.load(Fn.Presenter.class)) { + dfnr = o; + break; + } + + if (dfnr == null) { + throw new IllegalStateException("Can't find any Fn.Presenter"); + } + + final ClassLoader activeLoader; + if (loader != null) { + final URL res = FnContext.isJavaScriptCapable(loader); + if (res != null) { + throw new IllegalStateException("Loader " + loader + + " cannot resolve @JavaScriptBody, because of " + res + ); + } + activeLoader = loader; + } else { + final URL res = FnContext.isJavaScriptCapable(myCls.getClassLoader()); + if (res == null) { + activeLoader = myCls.getClassLoader(); + } else { + FImpl impl = new FImpl(myCls.getClassLoader()); + activeLoader = FnContext.newLoader(res, impl, dfnr, myCls.getClassLoader().getParent()); + if (activeLoader == null) { + throw new IllegalStateException("Cannot find asm-5.0.jar classes!"); + } + } + } + + final Fn.Presenter dP = dfnr; + + class OnPageLoad implements Runnable { + @Override + public void run() { + try { + final Fn.Presenter aP = Fn.activePresenter(); + final Fn.Presenter currentP = aP != null ? aP : dP; + + Thread.currentThread().setContextClassLoader(activeLoader); + final Class<?> newClazz = onLoad != null ? + myCls : + Class.forName(myCls.getName(), true, activeLoader); + Contexts.Builder cb = Contexts.newBuilder(context); + if (!Contexts.fillInByProviders(newClazz, cb)) { + LOG.log(Level.WARNING, "Using empty technology for {0}", newClazz); + } + if (currentP instanceof Executor) { + cb.register(Executor.class, (Executor)currentP, 1000); + } + cb.register(Fn.Presenter.class, currentP, 1000); + BrwsrCtx c = cb.build(); + + class CallInitMethod implements Runnable { + @Override + public void run() { + Throwable firstError = null; + if (onLoad != null) { + try { + FnContext.currentPresenter(currentP); + onLoad.run(); + } catch (Throwable ex) { + firstError = ex; + } finally { + FnContext.currentPresenter(null); + } + } + INIT: if (methodName != null) { + if (methodArgs.length == 0) { + try { + Method m = newClazz.getMethod(methodName); + FnContext.currentPresenter(currentP); + m.invoke(null); + firstError = null; + break INIT; + } catch (Throwable ex) { + firstError = ex; + } finally { + FnContext.currentPresenter(null); + } + } + try { + Method m = newClazz.getMethod(methodName, String[].class); + FnContext.currentPresenter(currentP); + m.invoke(m, (Object) methodArgs); + firstError = null; + } catch (Throwable ex) { + LOG.log(Level.SEVERE, "Can't call " + methodName + " with args " + Arrays.toString(methodArgs), ex); + } finally { + FnContext.currentPresenter(null); + } + } + if (firstError != null) { + LOG.log(Level.SEVERE, "Can't initialize the view", firstError); + } + } + } + + c.execute(new CallInitMethod()); + } catch (ClassNotFoundException ex) { + LOG.log(Level.SEVERE, "Can't load " + myCls.getName(), ex); + } + } + } + dfnr.displayPage(url, new OnPageLoad()); + } + + private static URL findResourceURL(String resource, String suffix, IOException[] mal, Class<?> relativeTo) { + if (suffix != null) { + int lastDot = resource.lastIndexOf('.'); + if (lastDot != -1) { + resource = resource.substring(0, lastDot) + suffix + resource.substring(lastDot); + } else { + resource = resource + suffix; + } + } + + URL url = null; + try { + String baseURL = System.getProperty("browser.rootdir"); // NOI18N + if (baseURL != null) { + URL u = new File(baseURL, resource).toURI().toURL(); + if (isReal(u)) { + url = u; + } + } + + { + URL u = new URL(resource); + if (suffix == null || isReal(u)) { + url = u; + } + return url; + } + } catch (MalformedURLException ex) { + mal[0] = ex; + } + + if (url == null) { + url = relativeTo.getResource(resource); + } + if (url == null) { + final ProtectionDomain pd = relativeTo.getProtectionDomain(); + if (pd != null && pd.getCodeSource() != null) { + URL jar = pd.getCodeSource().getLocation(); + try { + URL u = new URL(jar, resource); + if (isReal(u)) { + url = u; + } + } catch (MalformedURLException ex) { + ex.initCause(mal[0]); + mal[0] = ex; + } + } + } + if (url == null) { + URL res = BrowserBuilder.class.getResource("html4j.txt"); + LOG.log(Level.FINE, "Found html4j {0}", res); + if (res != null) { + try { + URLConnection c = res.openConnection(); + LOG.log(Level.FINE, "testing : {0}", c); + if (c instanceof JarURLConnection) { + JarURLConnection jc = (JarURLConnection) c; + URL base = jc.getJarFileURL(); + for (int i = 0; i < 50; i++) { + URL u = new URL(base, resource); + if (isReal(u)) { + url = u; + break; + } + base = new URL(base, ".."); + } + } + } catch (IOException ex) { + mal[0] = ex; + } + } + } + return url; + } + + static URL findLocalizedResourceURL(String resource, Locale l, IOException[] mal, Class<?> relativeTo) { + URL url = null; + if (l != null) { + url = findResourceURL(resource, "_" + l.getLanguage() + "_" + l.getCountry(), mal, relativeTo); + if (url != null) { + return url; + } + url = findResourceURL(resource, "_" + l.getLanguage(), mal, relativeTo); + } + if (url != null) { + return url; + } + return findResourceURL(resource, null, mal, relativeTo); + } + + private static boolean isReal(URL u) { + try { + URLConnection conn = u.openConnection(); + if (conn instanceof HttpURLConnection) { + HttpURLConnection hc = (HttpURLConnection) conn; + hc.setReadTimeout(5000); + if (hc.getResponseCode() >= 300) { + throw new IOException("Wrong code: " + hc.getResponseCode()); + } + } + InputStream is = conn.getInputStream(); + is.close(); + LOG.log(Level.FINE, "found real url: {0}", u); + return true; + } catch (IOException ignore) { + LOG.log(Level.FINE, "Cannot open " + u, ignore); + return false; + } + } + + private static final class FImpl implements FindResources { + final ClassLoader l; + + public FImpl(ClassLoader l) { + this.l = l; + } + + @Override + public void findResources(String path, Collection<? super URL> results, boolean oneIsEnough) { + if (oneIsEnough) { + URL u = l.getResource(path); + if (u != null) { + results.add(u); + } + } else { + try { + Enumeration<URL> en = l.getResources(path); + while (en.hasMoreElements()) { + results.add(en.nextElement()); + } + } catch (IOException ex) { + // no results + } + } + } + + } +} http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/net/java/html/boot/package.html ---------------------------------------------------------------------- diff --git a/boot/src/main/java/net/java/html/boot/package.html b/boot/src/main/java/net/java/html/boot/package.html new file mode 100644 index 0000000..aea8c9b --- /dev/null +++ b/boot/src/main/java/net/java/html/boot/package.html @@ -0,0 +1,54 @@ +<!-- + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + + Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + + Oracle and Java are registered trademarks of Oracle and/or its affiliates. + Other names may be trademarks of their respective owners. + + The contents of this file are subject to the terms of either the GNU + General Public License Version 2 only ("GPL") or the Common + Development and Distribution License("CDDL") (collectively, the + "License"). You may not use this file except in compliance with the + License. You can obtain a copy of the License at + http://www.netbeans.org/cddl-gplv2.html + or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + specific language governing permissions and limitations under the + License. When distributing the software, include this License Header + Notice in each file and include the License file at + nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + particular file as subject to the "Classpath" exception as provided + by Oracle in the GPL Version 2 section of the License file that + accompanied this code. If applicable, add the following below the + License Header, with the fields enclosed by brackets [] replaced by + your own identifying information: + "Portions Copyrighted [year] [name of copyright owner]" + + Contributor(s): + + The Original Software is NetBeans. The Initial Developer of the Original + Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved. + + If you wish your version of this file to be governed by only the CDDL + or only the GPL Version 2, indicate your decision by adding + "[Contributor] elects to include this software in this distribution + under the [CDDL or GPL Version 2] license." If you do not indicate a + single choice of license, a recipient has the option to distribute + your version of this file under either the CDDL, the GPL Version 2 or + to extend the choice of license to its licensees as provided above. + However, if you add GPL Version 2 code and therefore, elected the GPL + Version 2 license, then the option applies only if the new code is + made subject to such option by the copyright holder. + +--> +<!DOCTYPE html> +<html> + <body> + <div>{@link net.java.html.boot.BrowserBuilder Builder} class to bootstrap your Java/HTML based application.</div> + See {@link net.java.html.boot.BrowserBuilder} for description how to + launch your application. Look at {@link net.java.html.js.JavaScriptBody} + and its usage in case you want to directly talk between Java and + JavaScript. + </body> +</html> http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/net/java/html/js/JavaScriptBody.java ---------------------------------------------------------------------- diff --git a/boot/src/main/java/net/java/html/js/JavaScriptBody.java b/boot/src/main/java/net/java/html/js/JavaScriptBody.java new file mode 100644 index 0000000..01f4977 --- /dev/null +++ b/boot/src/main/java/net/java/html/js/JavaScriptBody.java @@ -0,0 +1,152 @@ +/** + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package net.java.html.js; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** Put this annotation on a method to provide its special implementation + * in JavaScript. This is a way to define <em>native</em> methods that + * interact with the surrounding JavaScript environment. Check the list + * <a href="package-summary.html">use-cases</a> to see real world + * use of this annotation. + * <p> + * Visit an <a target="_blank" href="http://dew.apidesign.org/dew/#7102188">on-line demo</a> + * to play with {@link JavaScriptBody} annotation for real. + * + * @author Jaroslav Tulach + */ +@Retention(RetentionPolicy.CLASS) +@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR }) +public @interface JavaScriptBody { + /** Names of parameters for the method generated method that can + * be referenced from {@link #body()}. + * + * @return array of the names of parameters for the method + * in JavaScript + */ + public String[] args(); + + /** The actual body of the method in JavaScript. This string will be + * put into generated header (last character is '{') and footer (e.g. '}'). + * The body can reference provided arguments. In case of non-static + * instance method it may reference <code>this</code>. + * + * @return JavaScript body of a function which can access {@link #args()} and possibly + * <code>this</code> + */ + public String body(); + + /** Should a special syntax for calling back into Java object be turned on? + * The syntax begins with <b>{@code @}</b> followed by fully qualified + * package name of the class. Now followed by <b>::</b> and a method in + * the class followed by its parameters enclosed inside <b>(...)</b>. + * This is the syntax one can use to call <code>run()</code> + * method of {@link Runnable}: + * <pre>[email protected]::run()()</pre>. + * One can also call static methods. Just use: + * <pre>var ten = @java.lang.Integer::parseInt(Ljava/lang/String;)("10")</pre> + * + * @return true, if the script should be scanned for special callback + * syntax + */ + public boolean javacall() default false; + + /** Should we wait before the JavaScript snippet execution finishes? + * Or not. + * <p> + * Some implementations that recognize the {@link JavaScriptBody} annotation + * need to reschedule the JavaScript execution into different thread and + * then it is easier for them to perform the execution asynchronously + * and not wait for the result of the execution. This may however be + * unexpected (for example when one awaits a callback into Java) + * and as such it has to be explicitly allowed by specifying + * <code>wait4js = false</code>. Such methods need to return <code>void</code>. + * <p> + * Implementations that execute the JavaScript synchronously may ignore + * this attribute. + * <p> + * Implementations that delay execution of JavaScript need to guarantee + * the order of snippets. Those that were submitted sooner, need to be + * executed sooner. Each snippet need to be executed in a timely manner + * (e.g. by a second, or so) even if there are no other calls made + * in the main program. + * <p> + * + * @since 0.7.6 + * @return <code>false</code> in case one allows asynchronous execution + * of the JavaScript snippet + */ + public boolean wait4js() default true; + + /** Controls garbage collection behavior of method parameters. + * In general JavaScript garbage + * collection system makes it close to impossible to find out whether + * an object is supposed to be still used or not. Some systems have + * an external hooks to find that out (like <em>JavaFX</em> <code>WebView</code>), + * in some systems this information is not important (like the + * <a href="http://bck2brwsr.apidesign.org">Bck2Brwsr</a> VM running + * all in JavaScript), but other execution systems just can't find that + * out. To prevent memory leaks on such systems and help them manage + * memory more effectively, those who define JavaScript interfacing + * methods may indicate whether the non-primitive parameters passed + * in should be hold only for the time of method invocation or + * for the whole application lifetime. + * <p> + * The default value is <code>true</code> as that is compatible with + * previous behavior and also prevents unwanted surprises when something + * garbage collects pre-maturaly. Framework developers are however + * encouraged to use <code>keepAlive=false</code> as much as possible. + * + * @return whether Java objects passed as parameters of the method + * should be made guaranteed to be available JavaScript + * even after the method invocation is over (e.g. prevent them to be + * garbage collected in Java until it is known they are not needed + * from JavaScript at all). + * + * @since 1.1 + */ + public boolean keepAlive() default true; +} http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/net/java/html/js/JavaScriptResource.java ---------------------------------------------------------------------- diff --git a/boot/src/main/java/net/java/html/js/JavaScriptResource.java b/boot/src/main/java/net/java/html/js/JavaScriptResource.java new file mode 100644 index 0000000..c14d10e --- /dev/null +++ b/boot/src/main/java/net/java/html/js/JavaScriptResource.java @@ -0,0 +1,70 @@ +/** + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package net.java.html.js; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** Include JavaScript libraries into your application easily. + * Annotate a Java/JavaScript bridging class with this annotation and + * once one of the class {@code @}{@link JavaScriptBody} annotated methods + * is called, it is guaranteed the JavaScript interpreter pre-load + * the content of here is specified resource. All other + * {@code @}{@link JavaScriptBody} methods can then access objects created + * by precessing this {@link JavaScriptResource#value() java script resource}. + * The list of + * <a href="package-summary.html#library">use-cases</a> includes sample use + * of this annotation. + * + * @author Jaroslav Tulach + */ +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.TYPE) +public @interface JavaScriptResource { + /** The JavaScript file to load in before associated class can execute. + * @return relative path with respect to the annotated class + */ + public String value(); +} http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/net/java/html/js/package.html ---------------------------------------------------------------------- diff --git a/boot/src/main/java/net/java/html/js/package.html b/boot/src/main/java/net/java/html/js/package.html new file mode 100644 index 0000000..f8160c0 --- /dev/null +++ b/boot/src/main/java/net/java/html/js/package.html @@ -0,0 +1,483 @@ +<!-- + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + + Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + + Oracle and Java are registered trademarks of Oracle and/or its affiliates. + Other names may be trademarks of their respective owners. + + The contents of this file are subject to the terms of either the GNU + General Public License Version 2 only ("GPL") or the Common + Development and Distribution License("CDDL") (collectively, the + "License"). You may not use this file except in compliance with the + License. You can obtain a copy of the License at + http://www.netbeans.org/cddl-gplv2.html + or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + specific language governing permissions and limitations under the + License. When distributing the software, include this License Header + Notice in each file and include the License file at + nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + particular file as subject to the "Classpath" exception as provided + by Oracle in the GPL Version 2 section of the License file that + accompanied this code. If applicable, add the following below the + License Header, with the fields enclosed by brackets [] replaced by + your own identifying information: + "Portions Copyrighted [year] [name of copyright owner]" + + Contributor(s): + + The Original Software is NetBeans. The Initial Developer of the Original + Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved. + + If you wish your version of this file to be governed by only the CDDL + or only the GPL Version 2, indicate your decision by adding + "[Contributor] elects to include this software in this distribution + under the [CDDL or GPL Version 2] license." If you do not indicate a + single choice of license, a recipient has the option to distribute + your version of this file under either the CDDL, the GPL Version 2 or + to extend the choice of license to its licensees as provided above. + However, if you add GPL Version 2 code and therefore, elected the GPL + Version 2 license, then the option applies only if the new code is + made subject to such option by the copyright holder. + +--> +<!DOCTYPE html> +<html> + <body> + <div>Essential support for those who write <em>native</em> methods communicating directly with JavaScript.</div> + Mix your Java and JavaScript code seamlessly - perform calls from Java + to JavaScript and back with as much freedom as JavaScript gives you + and as much type safety you can get from Java. Execute your code + in a headless testing environment or in a + <a href="http://wiki.apidesign.org/wiki/FXBrwsr">JavaFX WebView</a>. + When done, deploy to <a href="http://bck2brwsr.apidesign.org">real browsers</a>. + + <h3>Simple Meaning of World</h3> + The whole support is build around @<a href="JavaScriptBody.html">JavaScriptBody</a> + annotation. Use it to create parametrised JavaScript snippet easily + accessible from Java: +<pre> +{@code @}{@link net.java.html.js.JavaScriptBody}(args = {"x", "y"}, body = "return x + y;") +<b>private static native int</b> meaning(<b>int</b> x, <b>int</b> y); +</pre> + The above defines method <em>meaning</em> which sums two JavaScript + objects together (being invoked inside of a JavaScript interpreter). + The <em>meaning</em> method now becomes a properly typed Java + surface to your JavaScript code which can be directly + called from the rest of your Java code: +<pre> +<b>public static void</b> main(String... args) { + <b>assert</b> 42 == meaning(40, 2) : <em>"Meaning of World should be 42!"</em>; +} +</pre> + <em>Real code tip:</em> real classes using this technique are + available online: + <a target="top" href="http://hg.netbeans.org/html4j/file/release-0.7/boot/src/test/java/org/netbeans/html/boot/impl/JsMethods.java">JsMethods</a> and + <a target="top" href="http://hg.netbeans.org/html4j/file/release-0.7/json-tck/src/main/java/net/java/html/js/tests/Bodies.java">Bodies</a>. + <p></p> + <em>Editing hint:</em> one can see the list of arguments of the + <em>meaning</em> is now duplicated - it is once specified in Java, + and once inside of the {@link net.java.html.js.JavaScriptBody} + array of <code>args</code>. This is necessary to keep the names of + arguments accessible during runtime. However don't despair - there + is a code completion for the value of <code>args</code> attribute! + Just type the Java signature first and then press Ctrl+Space and the + right parameter names will be inserted for you. + + <a name="#library"><h3>Including JavaScript Libraries</h3></a> + + Large amount of JavaScript code is easier to be delivered in whole + files rather than {@link net.java.html.js.JavaScriptBody small code snippets} - + that is also possible thanks to {@link net.java.html.js.JavaScriptResource} + annotation. Imagine file <code>mul.js</code> with following content: +<pre> +<b>function</b> <em>mul</em>(x, y) { <b>return</b> x * y; } +</pre> + Place the file next to your class and reference it with + {@link net.java.html.js.JavaScriptResource the annotation}: +<pre> +{@code @}{@link net.java.html.js.JavaScriptResource}("mul.js") <b>class</b> Mul { + + {@code @}{@link net.java.html.js.JavaScriptBody}(args = { "x", "y" }, body = "return <b>mul</b>(x, y);") + <b>public static native int</b> multiply(int x, int y); + + <b>public static void</b> main(String... args) { + <b>assert</b> 42 == multiply(6, 7) : <em>"Meaning of World should be 42!"</em>; + } +} +</pre> + All the Java methods annotated {@link net.java.html.js.JavaScriptBody} + can now reference everything that is in the <code>mul.js</code> file - + e.g. the body of the <code>multiply</code> method can reference the + function <code>mul</code> and use it. + <p></p> + <em>Real code tip:</em> + <a target="top" href="http://hg.netbeans.org/html4j/file/release-0.7/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java">this</a> + is the way + the <a target="top" href="http://knockoutjs.com">knockout.js</a> library + is included in its <em>ko4j</em> library. + + <h3>Callback to Java</h3> + + Often JavaScript code needs to call back into the Java classes. + For example when a button in a browser is pressed and your code would + like to invoke a runnable to handle such situation: +<pre> +{@code @}{@link net.java.html.js.JavaScriptBody}(args = {"id", "r"}, {@link net.java.html.js.JavaScriptBody#javacall() javacall} = true, body = "\n" + +" document.getElementById(id).onclick = function() {\n" + +" r.<em>{@code @}java.lang.Runnable::run()</em>();\n" + +" };\n" + +" ") +<b>public static native void</b> onClick(String id, Runnable r); +</pre> + As can be seen, there is a special syntax (starting with <b>@</b>) to + properly identify the right Java method to call on a Java object passed + into the JavaScript interpreter. The syntax starts with a fully + qualified name of the class, followed by <b>::</b> and name of the + method including signature of its parameters. In case of runnable, + this is just <em>()</em> as the method has no parameters, but the + signature can be more complicated. For example in case of following method +<pre><b>static int</b> compare(<b>int</b> i1, String s1, <b>int</b> i2, String s2) +</pre> + it would be <em>(ILjava/lang/String;ILjava/lang/String;)</em> (btw. the + return type is not included in the signature). The actual parameters + then follows. The JavaScript call to such compare method would then + look like: +<pre>{@code @}the.pkg.Clazz::compare(ILjava/lang/String;ILjava/lang/String;)(1, 'One', 2, 'Two'); +</pre> + This syntax gives enough flexibility, helps to properly select one + of overloaded methods and follows the tradition of previous attempts to + provide JavaScript to Java calling conventions. + <p></p> + Please note that to turn the special Java callback syntax on, one + needs to set the {@link net.java.html.js.JavaScriptBody#javacall()} + attribute to <b>true</b>. The callback syntax consists of + following parts: + <p></p> + <pre>[instance.]@classname::methodname(signature)(arguments)</pre> + <ul> + <li><b>instance</b> - must be present when calling an + instance method and must be absent when calling a + static method</li> + <li><b>classname</b> - fully qualified name of the class in + which the method is declared + </li> + <li><b>signature</b> - internal JVM method signature + (as specified at + <a target="top" href="http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html#wp16432">JNI type Signatures</a>) + without the trailing signature of the method return type</li> + <li><b>arguments</b> - the actual values to pass to the called Java method + </li> + </ul> + + <p>Here is the <a target="top" href="http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html#wp16432">JNI type signatures table</a> + one can use to convert + Java parameters to JVM's internal <em>letter</em> based + representation:</p> + + <table border=1 width='100%'> + <tr> + <td><b>Type Signature</b></td> + <td><b>Java Type</b></td> + </tr> + <tr> + <td>Z</td> + <td>boolean</td> + </tr> + <tr> + <td>B</td> + <td>byte</td> + </tr> + <tr> + <td>C</td> + <td>char</td> + </tr> + <tr> + <td>S</td> + <td>short</td> + </tr> + <tr> + <td>I</td> + <td>int</td> + </tr> + <tr> + <td>J</td> + <td>long</td> + </tr> + <tr> + <td>F</td> + <td>float</td> + </tr> + <tr> + <td>D</td> + <td>double</td> + </tr> + <tr> + <td>L fully-qualified-class ;</td> + <td>fully-qualified-class</td> + </tr> + <tr> + <td>[ type</td> + <td>type[]</td> + </tr> + </tbody> + </table> + <p></p> + <em>Editing hint:</em> The callback syntax may seem complicated at + first, however there is an associated <b>annotation processor</b> + that checks the syntax and verifies the referenced class and + method with the requested signature exist. If it does not, the + <em>compilation fails offering correct alternatives</em>. + Thus don't despair seeing the syntax, make sure you get the + fully qualified name of the callback class right. + You'll get warning and help + if there is a typo in the specified signature then - + during compilation of your code. + + <h3>Overloaded Methods</h3> + + Specifying the actual callback signature is important in case of + overloaded methods. Imagine a class: +<pre> +<b>package</b> x.y.z; +<b>class</b> Handler { + <b>int</b> pulse() { + <b>return</b> 1; + } + <b>int</b> pulse(<b>int</b> howMuch) { + <b>return</b> howMuch; + } + <b>int</b> pulse(<b>long</b> evenMore) { + <b>return</b> (<b>int</b>) (5 + evenMore); + } +}</pre> + you then need to choose in {@link net.java.html.js.JavaScriptBody} + the appropriate method to call: +<pre> +{@code @}{@link net.java.html.js.JavaScriptBody}(args = { "h" }, javacall = <b>true</b>, <em>// you want to process the @ syntax</em> + body = "<b>return</b> [email protected]::pulse()() +" + <em>// the call to no argument method</em> + "[email protected]::pulse(I)(10) +" + <em>// the call to method with integer argument</em> + "[email protected]::pulse(J)(10);" <em>// the call to method with long argument</em> + ) + <b>static native void</b> threePulsesFromJavaScript(Handler h); + <b>static</b> { + <b>assert</b> 26 == threePulsesFromJavaScript(<b>new</b> Handler()); + } +</pre> + <p> + To avoid ambiguity, the specification of the correct signature is + required on every call. However, to simplify the development, + there is an annotation processor to + verify the signature really refers to an existing method. + </p> + + <h3>Arrays by Copy</h3> + + It is possible to exchange arrays between Java and JavaScript. Some + implementations can pass arrays by reference, however in some systems + this is hard to achieve. To choose the least common denominator, + the TCK for behavior of {@link net.java.html.js.JavaScriptBody} requires + the arrays to be always transfered by a copy. As such following code: + <pre> +{@code @}{@link net.java.html.js.JavaScriptBody}(args = {"arr"}, body = "arr[0] = null;") +<b>private static native void</b> uselessModify(String[] arr); +<b>public static void</b> main(String... args) { + String[] hello = { "Hello", "World!" }; + uselessModify(arr); + System.out.println(arr[0] + " " + arr[1]); +} +</pre> + will still print <em>Hello World!</em> in spite the JavaScript code + sets the 0-th array element to <code>null</code>. Because the array + is passed as a copy, such assignment has no effect on the Java array. + <p></p> + In case one needs to modify an array in a JavaScript and use its + values in Java, one has to return the array back as a return value: + <pre> +{@code @}{@link net.java.html.js.JavaScriptBody}(args = {"arr"}, body = "arr[0] = 'Ahoy'; return arr;") +<b>private static native</b> Object[] usefulModify(String[] arr); +<b>public static void</b> main(String... args) { + String[] hello = { "Hello", "World!" }; + Object[] ret = usefulModify(arr); + System.out.println(ret[0] + " " + ret[1]); +} +</pre> + now the program prints <em>Ahoy World!</em> as the modified array + is returned back and converted (by a copy) into a Java <code>Object[]</code> + (but of course the <code>ret != hello</code>). Usually the copy based + passing of arrays works OK. It is however good to keep it in mind to + avoid unwanted surprises. + + <h3>Instance Reference to JavaScript Object</h3> + + When writing wrappers around existing JavaScript libraries, it may be + useful to hold a reference to some JavaScript object from a Java + instance and use it later. +<pre> +<b>class</b> WrapperAroundJsObj { + <b>private final</b> Object js; + + WrapperAroundJsObj() { + js = initValue(); + } + + <b>public void</b> set(int v) { + setValue(js, v); + } + + {@link net.java.html.js.JavaScriptBody @JavaScriptBody}(args = {}, body = "return { value : 0 };") + <b>private static native</b> Object initValue(); + + {@link net.java.html.js.JavaScriptBody @JavaScriptBody}( + args = { "js", "v" }, body = "js.value = v;", wait4js = false + ) + <b>private static native void</b> setValue(Object js, int v); +} +</pre> + The type of the Java reference is {@link java.lang.Object}. + From a Java perspective it has no additional methods or fields, however + its properties can be manipulated from JavaScript. Send the object back + to JavaScript by passing it as a parameter of some method + (like the <code>setValue</code> one) and perform necessary JavaScript + calls or changes on it. + + <h3>undefined === null</h3> + <a name='undefined'></a> + + JavaScript recognizes two <em>empty</em> values: <code>null</code> and + <code>undefined</code>. Java has just <code>null</code>. + + For purposes of simplicity and easier inter-operability, <code>undefined</code> + values returned from {@link net.java.html.js.JavaScriptBody @JavaScriptBody} + annotated methods are converted to <code>null</code>. In the following + example both methods return <code>null</code>: +<pre> + {@link net.java.html.js.JavaScriptBody @JavaScriptBody}( + args = {}, body = "var empty = {}; return empty.x;" + ) + <b>private static native</b> Object returnUndefined(); + {@link net.java.html.js.JavaScriptBody @JavaScriptBody}( + args = {}, body = "var empty = {}; empty.x = null; return empty.x;" + ) + <b>private static native</b> Object returnNull(); +} +</pre> + This is the behavior since version 1.4. + + <h3>Post Process Classes</h3> + <a name="post-process"></a> + + Classes with {@link net.java.html.js.JavaScriptBody} annotated methods need to + be post processed before they can be used - e.g. their <code>native</code> + body needs to be generated to call into JavaScript (btw. the code is performed + via {@link org.netbeans.html.boot.spi.Fn}). There are three ways + such post processing can happen. + <p></p> + <b>Compile time</b> processing - this is the preferred method that + most of the <a href="http://html.java.net">Html Java APIs</a> are using. + Just include following plugin configuration into your <code>pom.xml</code> + and your classes will be ready for execution as soon as <em>process-classes</em> + <a href="http://wiki.apidesign.org/wiki/Maven">Maven</a> phase is over: +<pre> +<plugin> + <groupId>org.netbeans.html</groupId> + <artifactId>html4j-maven-plugin</artifactId> + <version>${net.java.html.version}</version> + <executions> + <execution> + <id>js-classes</id> + <goals> + <goal>process-js-annotations</goal> + </goals> + </execution> + </executions> +</plugin> +</pre> + This plugin works in orchestration with + <a href="http://wiki.apidesign.org/wiki/AnnotationProcessor">annotation + processor</a> associated with {@link net.java.html.js.JavaScriptBody} + and {@link net.java.html.js.JavaScriptResource} - the processor creates + list of files that need post-processing. The + <a href="http://wiki.apidesign.org/wiki/Maven">Maven</a> + plugin reads these files, processes classes mentioned in them and + modifies (and deletes at the end) the files to not include classes + already processed. + <p></p> + <b>Instrumentation Agent</b> - one can do processing in runtime + using JDK's {@link java.lang.instrument.ClassFileTransformer instrumentation} + abilities. The JAR artifact of <code>org.netbeans.html:net.java.html.boot</code> + contains an <code>Agent-Class</code> and <code>Premain-Class</code> + definitions in its manifest. As such one can launch the Java virtual + machine with +<pre> +$ java -javaagent:jarpath=net.java.html.boot-x.y.jar +</pre> + and the runtime will take care of processing bytecode of classes + not yet processed in compile time before they are loaded into the + virtual machine. + <p></p> + <b>Special classloading</b> - when booting your application with + {@link net.java.html.boot.BrowserBuilder} there is a 3rd option of + processing the classes. If there are some classes not yet processed + (remember the files listing them generated by the + <a href="http://wiki.apidesign.org/wiki/AnnotationProcessor">annotation + processor</a>), the {@link net.java.html.boot.BrowserBuilder#showAndWait() launching method} + will create a special classloader to that does the processing before + loading the bytecode into the virtual machine. + <p></p> + The options are rich, however to avoid any troubles (as the runtime + processing needs to also include <code>asm-5.0.jar</code> on application + classpath), it is recommended + to perform the <b>compile time</b> processing. + + <h3>Getting Started</h3> + + There are many ways to start developing + <a href="http://html.java.net">Html for Java</a> application. + However to be sure one chooses the most recent setup, it is recommended + to switch to good old command line and use a + <a href="http://wiki.apidesign.org/wiki/Knockout4Java">Maven archetype</a> + associated with every version of this project. Just type: +<pre> +$ mvn archetype:generate \ + -DarchetypeGroupId=org.apidesign.html \ + -DarchetypeArtifactId=knockout4j-archetype \ + -DarchetypeVersion=x.y +</pre> + Answer few questions (for example choose myfirstbrwsrpage as artifactId) and then you can: +<pre> +$ cd myfirstbrwsrpage +$ mvn process-classes exec:java +</pre> + In a few seconds (or minutes if + <a href="http://wiki.apidesign.org/wiki/Maven">Maven</a> + decides to download the whole Internet of dependencies) you should + see a sample Hello World application. It is basically composed from one + Java and one HTML file: +<pre> +$ ls src/main/java/**/DataModel.java +$ ls src/main/webapp/pages/index.html +</pre> + Play with them, modify them and enjoy + <a href="http://html.java.net">Html for Java</a>! + + <a name="debugging"> + <h3>Mixed Java/JavaScript Debugging</h3> + </a> + + <p> + The following video shows how easy it is to use + NetBeans 8.0, JDK8 to debug an application that intermixes Java + and JavaScript calls. One can put breakpoints into Java part, + as well as JavaScript source code, inspect Java as well + as JavaScript variables and switch between these two + languages without any restrictions. + </p> + + <iframe width="420" height="315" + src="http://www.youtube.com/embed/EvaTejQDRwA" + frameborder="0" allowfullscreen> + </iframe> + </body> +</html> http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/org/netbeans/html/boot/impl/FindResources.java ---------------------------------------------------------------------- diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/FindResources.java b/boot/src/main/java/org/netbeans/html/boot/impl/FindResources.java new file mode 100644 index 0000000..6dd53f2 --- /dev/null +++ b/boot/src/main/java/org/netbeans/html/boot/impl/FindResources.java @@ -0,0 +1,56 @@ +/** + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package org.netbeans.html.boot.impl; + +import java.net.URL; +import java.util.Collection; + +/** + * + * @author Jaroslav Tulach + */ +public interface FindResources { + + public void findResources(String path, Collection<? super URL> results, boolean oneIsEnough); + +} http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/boot/src/main/java/org/netbeans/html/boot/impl/FnContext.java ---------------------------------------------------------------------- diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/FnContext.java b/boot/src/main/java/org/netbeans/html/boot/impl/FnContext.java new file mode 100644 index 0000000..9bb1ebf --- /dev/null +++ b/boot/src/main/java/org/netbeans/html/boot/impl/FnContext.java @@ -0,0 +1,165 @@ +/** + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package org.netbeans.html.boot.impl; + +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.Flushable; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.Method; +import java.net.URL; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.netbeans.html.boot.spi.Fn; + +/** + * + * @author Jaroslav Tulach + */ +public final class FnContext implements Closeable { + private static final Logger LOG = Logger.getLogger(FnContext.class.getName()); + private static final FnContext DUMMY; + static { + DUMMY = new FnContext(null, null); + DUMMY.prev = DUMMY; + } + + public static URL isJavaScriptCapable(ClassLoader l) { + if (l instanceof JsClassLoader) { + return null; + } + return l.getResource("META-INF/net.java.html.js.classes"); + } + + public static ClassLoader newLoader(URL res, FindResources impl, Fn.Presenter p, ClassLoader parent) { + StringWriter w = new StringWriter(); + PrintWriter pw = new PrintWriter(w); + Throwable t; + try { + Method newLoader = Class.forName("org.netbeans.html.boot.impl.FnUtils") // NOI18N + .getMethod("newLoader", FindResources.class, Fn.Presenter.class, ClassLoader.class); + return (ClassLoader) newLoader.invoke(null, impl, p, parent); + } catch (LinkageError ex) { + t = ex; + } catch (Exception ex) { + t = ex; + } + pw.println("When using @JavaScriptBody methods, one needs to either:"); + pw.println(" - include asm-5.0.jar on runtime classpath"); + pw.println(" - post process classes, see http://bits.netbeans.org/html+java/dev/net/java/html/js/package-summary.html#post-process"); + pw.append("However following classes has not been processed from ").println(res); + + try { + BufferedReader r = new BufferedReader(new InputStreamReader(res.openStream())); + for (;;) { + String line = r.readLine(); + if (line == null) { + break; + } + pw.append(" ").println(line); + } + r.close(); + } catch (IOException io) { + pw.append("Cannot read ").println(res); + io.printStackTrace(pw); + } + pw.println("Cannot initialize asm-5.0.jar!"); + pw.flush(); + LOG.log(Level.SEVERE, w.toString(), t); + return null; + } + + private Object prev; + private final Fn.Presenter current; + private FnContext(Fn.Presenter prevP, Fn.Presenter newP) { + this.current = newP; + this.prev = prevP; + } + + @Override + public void close() throws IOException { + if (prev != this) { + currentPresenter((Fn.Presenter)prev); + prev = this; + if (current instanceof Flushable) { + ((Flushable)current).flush(); + } + } + } +/* + @Override + protected void finalize() throws Throwable { + if (prev != null) { + LOG.warning("Unclosed context!"); + } + } +*/ + public static Closeable activate(Fn.Presenter newP) { + final Fn.Presenter oldP = currentPresenter(newP); + if (oldP == newP) { + return DUMMY; + } + return new FnContext(oldP, newP); + } + + + private static final ThreadLocal<Fn.Presenter> CURRENT = new ThreadLocal<Fn.Presenter>(); + + public static Fn.Presenter currentPresenter(Fn.Presenter p) { + Fn.Presenter prev = CURRENT.get(); + CURRENT.set(p); + return prev; + } + + public static Fn.Presenter currentPresenter(boolean fail) { + Fn.Presenter p = CURRENT.get(); + if (p == null && fail) { + throw new IllegalStateException("No current WebView context around!"); + } + return p; + } + +}
