Repository: incubator-netbeans-html4j Updated Branches: refs/heads/master [created] 226089a5a
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/src/main/javadoc/overview.html ---------------------------------------------------------------------- diff --git a/src/main/javadoc/overview.html b/src/main/javadoc/overview.html new file mode 100644 index 0000000..850c0d7 --- /dev/null +++ b/src/main/javadoc/overview.html @@ -0,0 +1,623 @@ +<!-- + + 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> + <head> + <title>HTML for Java APIs</title> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + </head> + <body> + <p> + Use Java to write application logic; Use HTML5 to render the UI; + {@link net.java.html.json.Model Animate an HTML page from Java} + (see <a target="_blank" href="http://dew.apidesign.org/dew/#7212206">Duke being rotated</a> by CSS); + Use {@link net.java.html.json.OnReceive REST} or + <a href="net/java/html/json/doc-files/websockets.html">WebSockets</a>; + interact with <a href="net/java/html/js/package-summary.html">JavaScript</a>; + Get the best of both worlds! + + The goal of these APIs is to use full featured Java runtime + (like real <a target="_blank" href="http://wiki.apidesign.org/wiki/HotSpot">HotSpot VM</a>), + but still rely on a very lightweight rendering technology + (so it can potentially fit + <a target="_blank" href="http://bck2brwsr.apidesign.org">Bck2Brwsr</a> and definitely + to various types of phones). What can be more lightweight + (from a browser perspective) than + <a target="_blank" href="http://wiki.apidesign.org/wiki/HTML">HTML</a>!? + By default we use {@link net.java.html.boot.fx JavaFX's WebView} + component to display the <a target="_blank" href="http://wiki.apidesign.org/wiki/HTML">HTML</a>. + We eliminate the need to manipulate the DOM directly, + there is a special {@link net.java.html.json Java to Knockout.js binding}. + As a result the <a target="_blank" href="http://knockoutjs.com">HTML uses Knockout.js syntax</a>, + yet the application code can be written in Java. + </p> + + <h3>New features in version 1.4</h3> + + Both values <code>null</code> and <code>undefined</code> are + <a href="net/java/html/js/package-summary.html#undefined">treated as null</a>. + Better behavior under <a target="_blank" href="https://netbeans.org/bugzilla/show_bug.cgi?id=259132"> + multi-threaded load</a>. + Integration with <a href="net/java/html/boot/truffle/package-summary.html">Truffle</a>. + + <h3>Improvements in version 1.3</h3> + + {@link net.java.html.json.Model Model classes} can have + {@link net.java.html.json.Model#instance() per-instance private data}. + {@link net.java.html.json.Model Model classes} can generate + builder-like construction methods if builder + {@link net.java.html.json.Model#builder() prefix} is specified. + {@link net.java.html.json.Property#mutable} can be <code>false</code> + to define a non-mutable (almost constant) property. That + in case of <em>Knockout</em> bindings means: the property is + represented by a plain value rather than an observable in the JavaScript + object. The <em>JavaFX</em> presenter can be executed in headless mode - + just specify <code>-Dfxpresenter.headless=true</code> when launching + its virtual machine and no window will be shown. This is particularly + useful for testing. Configure your <em>surefire</em> or <em>failsafe</em> + plugins like: <pre> +<plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.13</version> + <configuration> + <systemPropertyVariables> + <fxpresenter.headless>true</fxpresenter.headless> + </systemPropertyVariables> + </configuration> +</plugin> +</pre> + OSGi headers are now <a target="_blank" href="https://netbeans.org/bugzilla/show_bug.cgi?id=256696"> + enterprise OSGi ready</a>. + Switched to <a target="_blank" href="https://netbeans.org/bugzilla/show_bug.cgi?id=257130">minified version 3.4.0</a> + of <a target="_blank" href="http://knockoutjs.com">knockout.js</a>. + Better support for <a target="_blank" href="https://netbeans.org/bugzilla/show_bug.cgi?id=257348"> + recursive @Model definitions</a>. + New module <code>org.netbeans.html:xhr4j</code> provides implementation + of {@link org.netbeans.html.json.spi.Transfer} with + {@link org.netbeans.html.context.spi.Contexts.Id technology identifier} + <b>xhr4j</b> - this module can be used to + <a target="_blank" href='https://netbeans.org/bugzilla/show_bug.cgi?id=257849'>workaround limitations + of CORS</a> by handling the {@link net.java.html.json.OnReceive} + connections in Java. + + <h3>What's Been Improved in Version 1.2.3?</h3> + + One can control {@link net.java.html.json.OnReceive#headers() HTTP request headers} + when connecting to server using the {@link net.java.html.json.OnReceive} + annotation. It is possible to have + {@link net.java.html.json.ComputedProperty#write() writable computed properties}. + There is an easy way to enable <a target="_blank" href="http://getfirebug.com/">Firebug</a> in + the JavaFX based Web View - + just run with <code>-Dfirebug.lite=true</code> as + <a target="_blank" href="https://www.youtube.com/watch?v=2rxwY-QJiLo">this video</a> + demonstrates. + Bugfix of issues <a target="_blank" href='https://netbeans.org/bugzilla/show_bug.cgi?id=250503'>250503</a>, + <a target="_blank" href='https://netbeans.org/bugzilla/show_bug.cgi?id=252987'>252987</a>. + + <h3>What's New in Version 1.1?</h3> + + <p> + The content of a {@link net.java.html.BrwsrCtx context} + can be selected by registering implementations under specific + {@link org.netbeans.html.context.spi.Contexts.Id technology identifiers} + and requesting them during + {@link org.netbeans.html.context.spi.Contexts#newBuilder(java.lang.Object...) construction} + of the context. <code>org.netbeans.html:ko4j</code> module's implementation + offers <b>ko4j</b>, <b>xhr</b> and <b>websocket</b> identifiers + for its registered services + (e.g. {@link org.netbeans.html.json.spi.Technology}, + {@link org.netbeans.html.json.spi.Transfer} and + {@link org.netbeans.html.json.spi.WSTransfer}). + <code>org.netbeans.html:ko-ws-tyrus</code> + module registers its + {@link org.netbeans.html.json.spi.Transfer Java based JSON} and + {@link org.netbeans.html.json.spi.WSTransfer WebSocket} implementations + under the name <b>tyrus</b>. + </p> + <p> + A particular DOM subtree + that a <a target="_blank" href="http://knockoutjs.com">knockout.js</a> model gets + applied to can be selected by using + {@link net.java.html.json.Models#applyBindings(java.lang.Object,java.lang.String) + Models.applyBindings(m, id)} with an id of an HTML element. + There is new {@link net.java.html.json.Model#targetId()} attribute + which controls behavior of the generated <code>applyBindings</code> method. + If <em>specified and non-empty</em>, then the generated method + will call {@link net.java.html.json.Models#applyBindings(java.lang.Object,java.lang.String)} + with <code>this</code> and the provided {@link net.java.html.json.Model#targetId() target id}. + If <em>specified, but left empty</em>, then the generated method + calls {@link net.java.html.json.Models#applyBindings(java.lang.Object)}. + <em>If unspecified</em>, the method will <b>not</b> be generated at all + (a change with respect to older versions). However one can + still use {@link net.java.html.json.Models#applyBindings(java.lang.Object)} + or {@link net.java.html.json.Models#applyBindings(java.lang.Object,java.lang.String)} + to perform the association of any model with the page element. + </p> + <p> + Memory model when using Knockout bindings has been improved + (required additions of two new methods: + {@link org.netbeans.html.json.spi.PropertyBinding#weak()} and + {@link org.netbeans.html.json.spi.FunctionBinding#weak()}) and + now the Java {@link net.java.html.json.Model models} can garbage collect, + when no longer used. Library writers that use + {@link net.java.html.js.JavaScriptBody} annotation can also + control garbage collection behavior of method arguments by + setting {@link net.java.html.js.JavaScriptBody#keepAlive() keepAlive=false} + attribute. + </p> + + <h3>What's New in Version 1.0?</h3> + + <p> + {@link net.java.html.json.Property#array() Array properties} are now + mutable from the <a href="http://knockoutjs.com">knockout.js</a> + point of view (required {@link org.netbeans.html.json.spi.Proto.Type#replaceValue one SPI change}). + The page lookup mechanism can use {@link net.java.html.boot.BrowserBuilder#locale(java.util.Locale) locale} + to load localized a page with appropriate suffix. + All SPI were moved under the NetBeans namespace - e.g. + {@link org.netbeans.html.boot.spi}, + {@link org.netbeans.html.context.spi}, + {@link org.netbeans.html.json.spi}, + {@link org.netbeans.html.sound.spi}, and also + {@link org.netbeans.html.json.tck}. Methods annotated + with {@link net.java.html.js.JavaScriptBody} annotation and + without fallback Java code now throw {@link java.lang.IllegalStateException} + with a message suggesting to switch to proper + {@link net.java.html.BrwsrCtx#execute browser context} to + prevent endless debugging when one forgets to do so. + </p> + + <p> + What's new in older versions? Click the + <a href="#" onclick="return showHistoric(true)">link</a> + to view even more + <a href="#" onclick="return showHistoric(true)">historic changes</a> below: + </p> + + <a name="historic.changes"></a> + <div id="historic.changes"> + <script> + function showHistoric(show) { + var e = document.getElementById("historic.changes"); + if (show) { + e.style.display="block"; + } else { + e.style.display="none"; + } + return false; + } + showHistoric(false); + </script> + + <h3>What's New in Version 0.9?</h3> + + <p> + System can run in {@link net.java.html.boot.BrowserBuilder#classloader(java.lang.ClassLoader) Felix OSGi container} (originally only Equinox). + {@link net.java.html.json.ComputedProperty Derived properties} + now deeply check changes in other {@link net.java.html.json.Model model + classes} they depend on and recompute their values accordingly. + <a target="_blank" href="http://knockoutjs.com">Knockout.js</a> library has been updated + to version 3.2.0. + </p> + + <h3>What's New in 0.8.x Versions?</h3> + + <p> + Setters or array properties on classes generated by {@link net.java.html.json.Model} + annotation can be accessed from any thread. {@link org.netbeans.html.sound.spi.AudioEnvironment} + can be registered into {@link net.java.html.BrwsrCtx}. There is + a {@link net.java.html.json.Models#parse(net.java.html.BrwsrCtx, java.lang.Class, java.io.InputStream, java.util.Collection) method} + to parse a JSON array and convert it into + {@link net.java.html.json.Model model classes}. + Improved behavior of <code>enum</code> values in + {@link net.java.html.json.Model knockout bindings}. + </p> + + <p> + Few bugfixes for better portability. + New API for {@link net.java.html.boot.script.Scripts headless execution} + on top of <em>Nashorn</em> - does not run <em>knockout for Java</em> + fully yet + (reported as <a href="https://bugs.openjdk.java.net/browse/JDK-8046013">JDK-8046013</a>), + however even in current state it is quite + {@link net.java.html.boot.script.Scripts useful for testing} + of + {@link net.java.html.js Java/JavaScript interactions}. + </p> + + <p> + {@link net.java.html.boot.fx.FXBrowsers} has been extended + with new helper methods to make it easier to use HTML+Java + API in existing JavaFX applications. + The annotation processor is made + more robust with respect to errors in callback syntax of + {@link net.java.html.js.JavaScriptBody} body parameter. + Javadoc of {@link net.java.html.BrwsrCtx#execute} method + has been improved based on a failure of its usability study. + There can be additional parameters to methods annotated by + {@link net.java.html.json.OnReceive} that allows one to + pass state when a JSON call is made and use it when it finishes. + The mechanism of discovery of sibling HTML page has been + extended to work on systems that don't support + {@link java.lang.Class#getProtectionDomain}. + </p> + + <p> + The first argument of method annotated by + {@link net.java.html.json.OnReceive} annotation has to + be the associated {@link net.java.html.json.Model model class}. + </p> + + <p> + {@link net.java.html.json.OnReceive} annotation now accepts + {@link java.util.List} of data values as second argument + (previously required an array). + </p> + + + <h3>What's New in 0.7.x Versions?</h3> + + <p> + {@link net.java.html.js.JavaScriptBody} annotation has new attribute + {@link net.java.html.js.JavaScriptBody#wait4js()} which allows + asynchronous execution. Libraries using + {@link net.java.html.js.JavaScriptBody} are urged to use this + new attribute as much as possible, as it can speed up execution + in certain environments. + </p> + + <p> + Use {@link net.java.html.BrwsrCtx#execute(java.lang.Runnable)} in + multi-threaded environment to execute your code on the browser thread. + See example + {@link net.java.html.BrwsrCtx#execute(java.lang.Runnable) using Java timer}. + </p> + </div> + + <h3>Interesting Entry Points</h3> + + <p>Learn how to {@link net.java.html.json.Model animate an HTML page from Java} + without referencing single HTML element from the Java code. + </p> + <p>Use {@link net.java.html.json.OnReceive JSON} to communicate + with REST based server API. + </p> + <p>Use <a href="net/java/html/json/doc-files/websockets.html">WebSockets</a> + and JSON. + </p> + <p>Call JavaScript methods from Java and vice versa, via + <a href="net/java/html/js/package-summary.html">JavaScriptBody</a>. + </p> + + <h3>Getting Started</h3> + + This <b>HTML/Java</b> API is used by + <a target="_blank" href="https://platform.netbeans.org/tutorials/nbm-dukescript.html"> + NetBeans Platform</a> as well as other project. The smoothest way + to get started is to follow the + <a target="_blank" href="https://dukescript.com/getting_started.html">getting started</a> + tutorial. In case one wants to stick with a <em>Maven</em> and command + line, one can follow + <a target="_blank" href="https://dukescript.com/update/2015/02/05/New-Version-of-Dukescript.html"> + these instructions<a>, make sure at least + <em>JDK7</em> is your installed Java and type: + <pre> +$ mvn archetype:generate \ + -DarchetypeGroupId=com.dukescript.archetype \ + -DarchetypeArtifactId=knockout4j-archetype \ + -DarchetypeVersion=0.11 <em># or newer version, if available</em> + </pre> + Answer few questions (for example choose <em>myfirstbrwsrpage</em> as artifactId) + and then you can: + <pre> +$ cd myfirstbrwsrpage +$ mvn install +$ mvn -f client/pom.xml process-classes exec:exec + </pre> + In a few seconds (or minutes if + <a target="_blank" 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. + Immediatelly you can be + <a target="_blank" href="https://dukescript.com/best/practices/2015/04/12/no-redeploys.html"> + productive without any redeploys</a> - even more productive than + with plain JavaScript! + <p> + The application is rendered in a + <a target="_blank" href="http://wiki.apidesign.org/wiki/JavaFX">JavaFX</a> + web view component (that of course requires your JDK to come + with <a target="_blank" href="http://wiki.apidesign.org/wiki/JavaFX">JavaFX</a>; + <a target="_blank" href="http://www.oracle.com/technetwork/java/javase/downloads/index.html">JDK7 + and JDK8 from Oracle</a> contain everything that is needed). + The generated application is built around one + Java source (uses the {@link net.java.html.json.Model} annotation to + auto-generate another <code>Data.java</code> class during compilation) + and one HTML file (uses the <a target="_blank" href="http://knockoutjs.com">Knockout</a> + syntax to <code>data-bind</code> the HTML elements to the + generated <code>Data</code> model): + <pre> +$ ls client/src/main/java/**/DataModel.java +$ ls client/src/main/webapp/pages/index.html + </pre> + That is all you need to get started. Play with the sources, + modify them and enjoy + <a target="_blank" href="http://html.java.net">Html for Java</a>! + + <h2>IDE Support</h2> + + <p> + This API is part of <a target="_blank" + href="http://netbeans.org">NetBeans.org</a> project and as such + it works naturally with the <a target="_blank" + href="https://netbeans.org/features/index.html">NetBeans IDE</a>. + On the other hand, the API is using nothing NetBeans specific, + it builds on standard Java6 APIs and as such it shall work fine + in any IDE. + </p> + + <p> + A lot of work is done by + <a href="http://wiki.apidesign.org/wiki/AnnotationProcessor"> + annotation processors</a> + that generate various boiler plate code during compilation. This + is a standard part of Java since JDK6, but for example Eclipse + is known not to deal with processors well and developers using + it need to be careful. IntelliJ users hasn't reported any issues + and of course, NetBeans IDE support for + <a href="http://wiki.apidesign.org/wiki/AnnotationProcessor">processors</a> + is outstanding. + </p> + + <p> + When using {@link net.java.html.js.JavaScriptBody} annotation, it is + useful to do a bit of post processing of classes. There is a + <a href="http://wiki.apidesign.org/wiki/Maven">Maven</a> + plugin for that. + NetBeans IDE will invoke it when doing a build. Other IDEs may + need some hint to do so. + Anyway: If one does not see all (generated) sources or is getting + {@link java.lang.LinkageError}s when executing the application, + switch to command line and do clean build + from there: + </p> + <pre>$ mvn clean install</pre> + <p> + If that succeeds, your IDE of choice will hopefully + pick the generated sources up and present the result of the build + properly. If not, + <a href="https://netbeans.org/downloads/">download NetBeans</a>, + you will be pleasantly + surprised - for example with our excellent + <a href="net/java/html/js/package-summary.html#debugging">Java/JavaScript + debugging</a> support. + </p> + + <a name="deploy"> + <h2>Deploy Your Application</h2> + </a> + + <p> + It is not goal of this documentation to list all possible ways + to package and deploy applications which use this API. However it is + important for new comers to see the benefits of using the + <a href="http://html.java.net">HTML for Java</a> API and as such + let's list at least few bundling options, known to work at the time of writing + this documentation. + </p> + + <p> + First of all, this is a <em>client technology</em>. You write client applications + with it which may, but need not connect to a server. You don't need + Tomcat or WebLogic to deploy + <a href="http://html.java.net">HTML for Java</a> applications. + </p> + + <p> + <img src='https://rawgit.com/jtulach/minesweeper/877b7983b1c1a157ebb85850896bcf04d7f5c7f2/client-web/src/main/webapp/resources/javafx_logo.jpg' width="64" + height="64" align="left"/> + The sample project generated by + <code>org.apidesign.html knockout4j-archetype</code> is configured + to use <a href="http://wiki.apidesign.org/wiki/JavaFX">JavaFX</a> + as the rendering technology. This setup is primarily suitable for + development - it needs no special packaging, starts quickly and + allows you to use classical HotSpot VM debuggers. A final + artifact from the build is also a ZIP file which you can use + and distribute to your users. Good for desktop applications. + </p> + + <p> + <img src='https://rawgit.com/jtulach/minesweeper/877b7983b1c1a157ebb85850896bcf04d7f5c7f2/client-web/src/main/webapp/resources/netbeans_logo.jpg' width="64" + height="64" align="right"/> + <img src='https://rawgit.com/jtulach/minesweeper/877b7983b1c1a157ebb85850896bcf04d7f5c7f2/client-web/src/main/webapp/resources/eclipse_logo.png' width="64" + height="64" align="right"/> + All the <a href="http://html.java.net">HTML for Java</a> libraries + are packaged as <a href="http://wiki.apidesign.org/wiki/OSGi">OSGi</a> + bundles and as such they can easily be run in NetBeans as well as + in Eclipse. As a result one can use + <a href="http://wiki.apidesign.org/wiki/OSGi">OSGi</a> + and have a common module system for both platforms. In addition to that + one can render using + HTML and have a common UI in both platforms. In such case + your application would be packaged as a set of + <a href="http://wiki.apidesign.org/wiki/OSGi">OSGi</a> bundles. + Read + <a href="http://wiki.apidesign.org/wiki/HTML">more</a>... + </p> + + <p> + <img src='https://rawgit.com/jtulach/minesweeper/877b7983b1c1a157ebb85850896bcf04d7f5c7f2/client-web/src/main/webapp/resources/chrome_logo.png' width="64" + height="64" align="left"/> + <img src='https://rawgit.com/jtulach/minesweeper/877b7983b1c1a157ebb85850896bcf04d7f5c7f2/client-web/src/main/webapp/resources/safari_logo.png' width="64" + height="64" align="left"/> + <img src='https://rawgit.com/jtulach/minesweeper/877b7983b1c1a157ebb85850896bcf04d7f5c7f2/client-web/src/main/webapp/resources/ie_logo.png' width="64" + height="64" align="left"/> + <img src='https://rawgit.com/jtulach/minesweeper/877b7983b1c1a157ebb85850896bcf04d7f5c7f2/client-web/src/main/webapp/resources/firefox_logo.png' width="64" + height="64" align="left"/> + + There is more and more attempts to execute Java bytecode + in a browser, without any special Java plugin installed. + The <a href="http://html.java.net">HTML for Java</a> is + carefully designed to produce lightweight, well performing + applications even on such restricted environments. It uses + no reflection calls and that allows to statically pre-compile + the applications into JavaScript. One of such environments + is called <a target="_blank" href="http://wiki.apidesign.org/wiki/Bck2Brwsr">Bck2Brwsr</a>, + another <a target="_blank" href="http://wiki.apidesign.org/wiki/TeaVM">TeaVM</a>. Both support the + {@link net.java.html.js.JavaScriptBody} annotation. Read + <a target="_blank" href="http://wiki.apidesign.org/wiki/Bck2BrwsrViaCLI">more</a> or play + a minesweeper game packaged for your browser + (of course <a target="_blank" + href="https://github.com/jtulach/minesweeper">written</a> in Java): + </p> + + <script type="text/html" id="field"> + <style type="text/css"> + table.field td { + padding: 4px; + width: 18px; + height: 18px; + font-size: 1.5em; + border: 1px solid black; + } + table.field td.UNKNOWN { + background-color: #D6E4E1; + cursor: pointer; + } + table.field td.EXPLOSION { + background-color: #A31E39; + } + table.field td.DISCOVERED { + background-color: #9DB2B1; + } + </style> + <table class="field"> + <tbody> + <!-- ko foreach: rows --> + <tr> + <!-- ko foreach: columns --> + <td data-bind="css: style, click: $parents[1].click" > + <div data-bind='html: html'></div> + </td> + <!-- /ko --> + </tr> + <!-- /ko --> + </tbody> + </table> + </script> + + <center> + <div id="minesweeper" style="background-color: #f0f0f0; align: center"> + <div data-bind="template: { name : 'field', if: fieldShowing }"></div> + </div> + </center> + + <!-- boot bck2brwsr --> + <script type="text/javascript" src="https://rawgit.com/jtulach/minesweeper/877b7983b1c1a157ebb85850896bcf04d7f5c7f2/client-web/src/main/webapp/resources/teavm.js"></script> + <script> + var vm = new VM(); + vm.loadClass('org.apidesign.demo.minesweeper.MainBrwsr'); + </script> + + <p> + <img src='https://rawgit.com/jtulach/minesweeper/877b7983b1c1a157ebb85850896bcf04d7f5c7f2/client-web/src/main/webapp/resources/ios_logo.jpg' width="64" + height="64" align="right"/> + <img src='https://rawgit.com/jtulach/minesweeper/877b7983b1c1a157ebb85850896bcf04d7f5c7f2/client-web/src/main/webapp/resources/android_logo.jpg' width="64" + height="64" align="right"/> + + Now when we have seen that the + <a href="http://html.java.net">HTML for Java</a> applications + can run on any modern browser, we can ask whether they can also + fit into a phone!? Yes, they can and especially to phones + that can execute Java code already! Just by changing your + packaging you can create an APK file and deploy it to your + Android phone. + Read <a target="_blank" href="http://wiki.apidesign.org/wiki/DlvkBrwsr">more</a> + or install <a target="_blank" href="https://play.google.com/store/apps/details?id=org.apidesign.demo.minesweeper"> + Fair Minesweeper for Android</a>... + </p> + <p> + In case you'd like your application to reach out to second biggest + group of smartphone users, don't despair: It + seems the set of devices that can execute + <a href="http://html.java.net">HTML for Java</a> applications + has been extended to <em>iPads</em> and <em>iPhones</em>. Get the + <a target="_blank" href="http://wiki.apidesign.org/wiki/IBrwsr">details here</a> + and play <a target="_blank" href="https://itunes.apple.com/us/app/fair-minesweeper/id903688146"> + Fair Minesweeper on iOS</a>! + </p> + <p> + Convinced it makes sense to use + <a href="http://html.java.net">HTML for Java</a> + APIs for writing applications that are + <em>written once, displayed anywhere</em>? Or do you have an + environment which is not supported? In such case you can bring + <a href="http://html.java.net">HTML for Java</a> + to your environment yourself. Just implement your own + {@link org.netbeans.html.boot.spi.Fn.Presenter}! + </p> + + <h2>Other Resources</h2> + + <img src="net/java/html/json/doc-files/DukeHTML.png" width="256" height="184" alt="Duke and HTML5. Together at last!" align="right"/> + + The javadoc for latest and previous versions is also available + online: + <ul> + <li>Current <a target="_blank" href="http://bits.netbeans.org/html+java/dev/">development</a> version + <li>Version <a target="_blank" href="http://bits.netbeans.org/html+java/1.2.3">1.2.3</a> + <li>Version <a target="_blank" href="http://bits.netbeans.org/html+java/1.1">1.1</a> + <li>Version <a target="_blank" href="http://bits.netbeans.org/html+java/1.0">1.0</a> + <li>Version <a target="_blank" href="http://bits.netbeans.org/html+java/0.9">0.9</a> + and historic ones (<a target="_blank" href="http://bits.netbeans.org/html+java/0.8.3">0.8.3</a>, + <a target="_blank" href="http://bits.netbeans.org/html+java/0.8.2">0.8.2</a>, + <a target="_blank" href="http://bits.netbeans.org/html+java/0.8.1">0.8.1</a>, + <a target="_blank" href="http://bits.netbeans.org/html+java/0.8">0.8</a>, and + <a target="_blank" href="http://bits.netbeans.org/html+java/0.7.5">0.7.5</a>) + </li> + </ul> + </body> +</html> http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/xhr4j/pom.xml ---------------------------------------------------------------------- diff --git a/xhr4j/pom.xml b/xhr4j/pom.xml new file mode 100644 index 0000000..947ef26 --- /dev/null +++ b/xhr4j/pom.xml @@ -0,0 +1,166 @@ +<?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>xhr4j</artifactId> + <version>2.0-SNAPSHOT</version> + <packaging>bundle</packaging> + <name>XHR via Java</name> + <url>http://maven.apache.org</url> + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.netbeans.html</groupId> + <artifactId>html4j-maven-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <configuration> + <skip>false</skip> + </configuration> + </plugin> + </plugins> + </build> + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <bundleSymbolicName>org.netbeans.html.xhr4j</bundleSymbolicName> + </properties> + <dependencies> + <!-- compile only deps --> + <dependency> + <groupId>org.netbeans.api</groupId> + <artifactId>org-openide-util-lookup</artifactId> + <type>jar</type> + <scope>provided</scope> + </dependency> + + <!-- compile + runtime --> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>net.java.html</artifactId> + <version>${project.version}</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>net.java.html.json</artifactId> + <version>${project.version}</version> + <type>jar</type> + </dependency> + <!-- test only deps --> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>net.java.html.boot</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + <type>jar</type> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>net.java.html.json.tck</artifactId> + <version>${project.version}</version> + <scope>test</scope> + <type>jar</type> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>ko4j</artifactId> + <version>${project.version}</version> + <scope>test</scope> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.glassfish.grizzly</groupId> + <artifactId>grizzly-http-server-core</artifactId> + <version>${grizzly.version}</version> + <scope>test</scope> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.glassfish.grizzly</groupId> + <artifactId>grizzly-websockets-server</artifactId> + <version>${grizzly.version}</version> + <scope>test</scope> + <type>jar</type> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>net.java.html.boot.fx</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.glassfish.grizzly</groupId> + <artifactId>grizzly-http-server</artifactId> + <version>${grizzly.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.glassfish.grizzly</groupId> + <artifactId>grizzly-http-servlet</artifactId> + <version>${grizzly.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>javax.servlet-api</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <description>Implementation module with support for XHR via Java. +Use it to workaround CORS limitations.</description> +</project> http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/xhr4j/src/main/java/org/netbeans/html/xhr4j/LoadJSON.java ---------------------------------------------------------------------- diff --git a/xhr4j/src/main/java/org/netbeans/html/xhr4j/LoadJSON.java b/xhr4j/src/main/java/org/netbeans/html/xhr4j/LoadJSON.java new file mode 100644 index 0000000..78ac78a --- /dev/null +++ b/xhr4j/src/main/java/org/netbeans/html/xhr4j/LoadJSON.java @@ -0,0 +1,274 @@ +/** + * 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.xhr4j; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PushbackInputStream; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.logging.Logger; +import net.java.html.js.JavaScriptBody; +import org.netbeans.html.json.spi.JSONCall; + +/** This is an implementation package - just + * include its JAR on classpath and use official {@link Context} API + * to access the functionality. + * + * @author Jaroslav Tulach + */ +final class LoadJSON implements Runnable { + private static final Logger LOG = Logger.getLogger(LoadJSON.class.getName()); + private static final Executor REQ = Executors.newCachedThreadPool(new ThreadFactory() { + @Override + public Thread newThread(Runnable runnable) { + Thread thread = Executors.defaultThreadFactory().newThread(runnable); + thread.setDaemon(true); + thread.setName("xhr4j daemon"); + return thread; + } + }); + + private final JSONCall call; + private final URL base; + + + private LoadJSON(JSONCall call) { + this.call = call; + this.base = null; + } + + public static void loadJSON(JSONCall call) { + assert !"WebSocket".equals(call.getMethod()); + REQ.execute(new LoadJSON((call))); + } + + @Override + public void run() { + final String url; + Throwable error = null; + Object json = null; + + if (call.isJSONP()) { + url = call.composeURL("dummy"); + } else { + url = call.composeURL(null); + } + try { + final URL u = new URL(base, url.replace(" ", "%20")); + URLConnection conn = u.openConnection(); + if (call.isDoOutput()) { + conn.setDoOutput(true); + } + String h = call.getHeaders(); + if (h != null) { + int pos = 0; + while (pos < h.length()) { + int tagEnd = h.indexOf(':', pos); + if (tagEnd == -1) { + break; + } + int r = h.indexOf('\r', tagEnd); + int n = h.indexOf('\n', tagEnd); + if (r == -1) { + r = h.length(); + } + if (n == -1) { + n = h.length(); + } + String key = h.substring(pos, tagEnd).trim(); + String val = h.substring(tagEnd + 1, Math.min(r, n)).trim(); + conn.setRequestProperty(key, val);; + pos = Math.max(r, n); + } + } + if (call.getMethod() != null && conn instanceof HttpURLConnection) { + ((HttpURLConnection) conn).setRequestMethod(call.getMethod()); + } + if (call.isDoOutput()) { + final OutputStream os = conn.getOutputStream(); + call.writeData(os); + os.flush(); + } + final PushbackInputStream is = new PushbackInputStream( + conn.getInputStream(), 1 + ); + boolean[] arrayOrString = { false, false }; + detectJSONType(call.isJSONP(), is, arrayOrString); + String response = readStream(is); + if (call.isJSONP()) { + response = '(' + response; + } + json = new Result(response, arrayOrString[0], arrayOrString[1]); + } catch (IOException ex) { + error = ex; + } finally { + if (error != null) { + call.notifyError(error); + } else { + call.notifySuccess(json); + } + } + } + + private static final class Result implements Callable<Object> { + private final String response; + private final boolean array; + private final boolean plain; + + Result(String response, boolean array, boolean plain) { + this.response = response; + this.array = array; + this.plain = plain; + } + + @Override + public Object call() throws Exception { + if (plain) { + return response; + } else { + if (array) { + Object r = parse(response); + Object[] arr = r instanceof Object[] ? (Object[])r : new Object[] { r }; + for (int i = 0; i < arr.length; i++) { + arr[i] = new JSObjToStr(response, arr[i]); + } + return arr; + } else { + return new JSObjToStr(response, parse(response)); + } + } + } + } + private static final class JSObjToStr { + final String str; + final Object obj; + + public JSObjToStr(Object str, Object obj) { + this.str = str == null ? "" : str.toString(); + this.obj = obj; + } + + @Override + public String toString() { + return str; + } + } + + static String readStream(InputStream is) throws IOException, UnsupportedEncodingException { + Reader r = new InputStreamReader(is, "UTF-8"); + StringBuilder sb = new StringBuilder(); + char[] arr = new char[4096]; + for (;;) { + int len = r.read(arr); + if (len == -1) { + break; + } + sb.append(arr, 0, len); + } + return sb.toString(); + } + + private static void detectJSONType(boolean skipAnything, final PushbackInputStream is, boolean[] arrayOrString) throws IOException { + for (;;) { + int ch = is.read(); + if (ch == -1) { + arrayOrString[1] = true; + break; + } + if (Character.isWhitespace(ch)) { + continue; + } + + if (ch == '[') { + is.unread(ch); + arrayOrString[0] = true; + break; + } + if (ch == '{') { + is.unread(ch); + break; + } + if (!skipAnything) { + is.unread(ch); + arrayOrString[1] = true; + break; + } + } + } + + @JavaScriptBody(args = {"object", "property"}, body = + "var ret;\n" + + "if (property === null) ret = object;\n" + + "else if (object === null) ret = null;\n" + + "else ret = object[property];\n" + + "return ret ? (typeof ko === 'undefined' ? ret : ko.utils.unwrapObservable(ret)) : null;" + ) + private static Object getProperty(Object object, String property) { + return null; + } + + @JavaScriptBody(args = {"s"}, body = "return eval('(' + s + ')');") + static Object parse(String s) { + throw new IllegalStateException("No parser context for " + s); + } + + static void extractJSON(Object js, String[] props, Object[] values) { + if (js instanceof JSObjToStr) { + js = ((JSObjToStr)js).obj; + } + for (int i = 0; i < props.length; i++) { + values[i] = getProperty(js, props[i]); + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/xhr4j/src/main/java/org/netbeans/html/xhr4j/XmlHttpResourceContext.java ---------------------------------------------------------------------- diff --git a/xhr4j/src/main/java/org/netbeans/html/xhr4j/XmlHttpResourceContext.java b/xhr4j/src/main/java/org/netbeans/html/xhr4j/XmlHttpResourceContext.java new file mode 100644 index 0000000..4b3f02f --- /dev/null +++ b/xhr4j/src/main/java/org/netbeans/html/xhr4j/XmlHttpResourceContext.java @@ -0,0 +1,88 @@ +/** + * 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.xhr4j; + +import java.io.IOException; +import java.io.InputStream; +import net.java.html.json.OnReceive; +import org.netbeans.html.context.spi.Contexts; +import org.netbeans.html.json.spi.JSONCall; +import org.netbeans.html.json.spi.Transfer; +import org.openide.util.lookup.ServiceProvider; + +/** Implementation module with support for XHR via Java. + * Handles {@link OnReceive} requests by using Java to connect to given + * URL and then parsing it via JavaScript. Use this module if you have + * problems with CORS - as the Java connection isn't restricted by CORS + * rules. + * + * Registers {@link Transfer} technology at position <code>50</code>. + * The {@link Contexts.Id} of the technology is <b>xhr4j</b>. + * + * @author Jaroslav Tulach + * @since 1.3 + */ [email protected]("xhr4j") +@ServiceProvider(service = Contexts.Provider.class) +public final class XmlHttpResourceContext +implements Contexts.Provider, Transfer { + @Override + public void fillContext(Contexts.Builder context, Class<?> requestor) { + context.register(Transfer.class, this, 50); + } + + @Override + public void extract(Object obj, String[] props, Object[] values) { + LoadJSON.extractJSON(obj, props, values); + } + + @Override + public Object toJSON(InputStream is) throws IOException { + return LoadJSON.parse(LoadJSON.readStream(is)); + } + + @Override + public void loadJSON(JSONCall call) { + LoadJSON.loadJSON(call); + } +} http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonDynamicHTTP.java ---------------------------------------------------------------------- diff --git a/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonDynamicHTTP.java b/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonDynamicHTTP.java new file mode 100644 index 0000000..d114468 --- /dev/null +++ b/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonDynamicHTTP.java @@ -0,0 +1,262 @@ +/** + * 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.xhr4j; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.glassfish.grizzly.PortRange; +import org.glassfish.grizzly.http.server.HttpHandler; +import org.glassfish.grizzly.http.server.HttpServer; +import org.glassfish.grizzly.http.server.NetworkListener; +import org.glassfish.grizzly.http.server.Request; +import org.glassfish.grizzly.http.server.Response; +import org.glassfish.grizzly.http.server.ServerConfiguration; +import org.glassfish.grizzly.websockets.WebSocket; +import org.glassfish.grizzly.websockets.WebSocketAddOn; +import org.glassfish.grizzly.websockets.WebSocketApplication; +import org.glassfish.grizzly.websockets.WebSocketEngine; + +/** + * + * @author Jaroslav Tulach + */ +final class JsonDynamicHTTP extends HttpHandler { + private static int resourcesCount; + private static List<Resource> resources; + private static ServerConfiguration conf; + private static HttpServer server; + + private JsonDynamicHTTP() { + } + + static URI initServer() throws Exception { + server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535)); + final WebSocketAddOn addon = new WebSocketAddOn(); + for (NetworkListener listener : server.getListeners()) { + listener.registerAddOn(addon); + } + resources = new ArrayList<Resource>(); + + conf = server.getServerConfiguration(); + final JsonDynamicHTTP dh = new JsonDynamicHTTP(); + + conf.addHttpHandler(dh, "/"); + + server.start(); + + return pageURL("http", server, "/test.html"); + } + + @Override + public void service(Request request, Response response) throws Exception { + if ("/test.html".equals(request.getRequestURI())) { + response.setContentType("text/html"); + final InputStream is = JsonDynamicHTTP.class.getResourceAsStream("test.html"); + copyStream(is, response.getOutputStream(), null); + return; + } + if ("/dynamic".equals(request.getRequestURI())) { + String mimeType = request.getParameter("mimeType"); + List<String> params = new ArrayList<String>(); + boolean webSocket = false; + for (int i = 0;; i++) { + String p = request.getParameter("param" + i); + if (p == null) { + break; + } + if ("protocol:ws".equals(p)) { + webSocket = true; + continue; + } + params.add(p); + } + final String cnt = request.getParameter("content"); + String mangle = cnt.replace("%20", " ").replace("%0A", "\n"); + ByteArrayInputStream is = new ByteArrayInputStream(mangle.getBytes("UTF-8")); + URI url; + final Resource res = new Resource(is, mimeType, "/dynamic/res" + ++resourcesCount, params.toArray(new String[params.size()])); + if (webSocket) { + url = registerWebSocket(res); + } else { + url = registerResource(res); + } + response.getWriter().write(url.toString()); + response.getWriter().write("\n"); + return; + } + + for (Resource r : resources) { + if (r.httpPath.equals(request.getRequestURI())) { + response.setContentType(r.httpType); + r.httpContent.reset(); + String[] params = null; + if (r.parameters.length != 0) { + params = new String[r.parameters.length]; + for (int i = 0; i < r.parameters.length; i++) { + params[i] = request.getParameter(r.parameters[i]); + if (params[i] == null) { + if ("http.method".equals(r.parameters[i])) { + params[i] = request.getMethod().toString(); + } else if ("http.requestBody".equals(r.parameters[i])) { + Reader rdr = request.getReader(); + StringBuilder sb = new StringBuilder(); + for (;;) { + int ch = rdr.read(); + if (ch == -1) { + break; + } + sb.append((char) ch); + } + params[i] = sb.toString(); + } else if (r.parameters[i].startsWith("http.header.")) { + params[i] = request.getHeader(r.parameters[i].substring(12)); + } + } + if (params[i] == null) { + params[i] = "null"; + } + } + } + + copyStream(r.httpContent, response.getOutputStream(), null, params); + } + } + } + + private URI registerWebSocket(Resource r) { + WebSocketEngine.getEngine().register("", r.httpPath, new WS(r)); + return pageURL("ws", server, r.httpPath); + } + + private URI registerResource(Resource r) { + if (!resources.contains(r)) { + resources.add(r); + conf.addHttpHandler(this, r.httpPath); + } + return pageURL("http", server, r.httpPath); + } + + private static URI pageURL(String proto, HttpServer server, final String page) { + NetworkListener listener = server.getListeners().iterator().next(); + int port = listener.getPort(); + try { + return new URI(proto + "://localhost:" + port + page); + } catch (URISyntaxException ex) { + throw new IllegalStateException(ex); + } + } + + static final class Resource { + + final InputStream httpContent; + final String httpType; + final String httpPath; + final String[] parameters; + + Resource(InputStream httpContent, String httpType, String httpPath, + String[] parameters) { + httpContent.mark(Integer.MAX_VALUE); + this.httpContent = httpContent; + this.httpType = httpType; + this.httpPath = httpPath; + this.parameters = parameters; + } + } + + static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException { + for (;;) { + int ch = is.read(); + if (ch == -1) { + break; + } + if (ch == '$' && params.length > 0) { + int cnt = is.read() - '0'; + if (baseURL != null && cnt == 'U' - '0') { + os.write(baseURL.getBytes("UTF-8")); + } else { + if (cnt >= 0 && cnt < params.length) { + os.write(params[cnt].getBytes("UTF-8")); + } else { + os.write('$'); + os.write(cnt + '0'); + } + } + } else { + os.write(ch); + } + } + } + + private static class WS extends WebSocketApplication { + private final Resource r; + + private WS(Resource r) { + this.r = r; + } + + @Override + public void onMessage(WebSocket socket, String text) { + try { + r.httpContent.reset(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + copyStream(r.httpContent, out, null, text); + String s = new String(out.toByteArray(), "UTF-8"); + socket.send(s); + } catch (IOException ex) { + LOG.log(Level.WARNING, null, ex); + } + } + private static final Logger LOG = Logger.getLogger(WS.class.getName()); + + } +} http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonFX.java ---------------------------------------------------------------------- diff --git a/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonFX.java b/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonFX.java new file mode 100644 index 0000000..5b301e2 --- /dev/null +++ b/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonFX.java @@ -0,0 +1,125 @@ +/** + * 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.xhr4j; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import javafx.application.Platform; +import org.netbeans.html.boot.impl.FnContext; +import org.netbeans.html.boot.spi.Fn; +import org.testng.ITest; +import org.testng.annotations.Test; + +/** + * + * @author Jaroslav Tulach + */ +public final class JsonFX implements ITest, Runnable { + private final Fn.Presenter p; + private final Method m; + private Object result; + private Object inst; + private int count; + + JsonFX(Fn.Presenter p, Method m) { + this.p = p; + this.m = m; + } + + @Override + public String getTestName() { + return m.getName(); + } + + @Test + public synchronized void executeTest() throws Exception { + if (result == null) { + Platform.runLater(this); + wait(); + } + if (result instanceof Exception) { + throw (Exception)result; + } + if (result instanceof Error) { + throw (Error)result; + } + } + + @Override + public synchronized void run() { + boolean notify = true; + try { + FnContext.currentPresenter(p); + if (inst == null) { + inst = m.getDeclaringClass().newInstance(); + } + result = m.invoke(inst); + if (result == null) { + result = this; + } + } catch (InvocationTargetException ex) { + Throwable r = ex.getTargetException(); + if (r instanceof InterruptedException) { + if (count++ < 100) { + notify = false; + try { + Thread.sleep(100); + } catch (Exception ex1) { + // ignore and continue + } + Platform.runLater(this); + return; + } + } + result = r; + } catch (Exception ex) { + result = ex; + } finally { + if (notify) { + notifyAll(); + } + FnContext.currentPresenter(null); + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonKnockoutTest.java ---------------------------------------------------------------------- diff --git a/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonKnockoutTest.java b/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonKnockoutTest.java new file mode 100644 index 0000000..b84be22 --- /dev/null +++ b/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonKnockoutTest.java @@ -0,0 +1,217 @@ +/** + * 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.xhr4j; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import net.java.html.BrwsrCtx; +import net.java.html.boot.BrowserBuilder; +import net.java.html.js.JavaScriptBody; +import org.netbeans.html.boot.spi.Fn; +import org.netbeans.html.context.spi.Contexts; +import org.netbeans.html.json.spi.Technology; +import org.netbeans.html.json.tck.KOTest; +import org.netbeans.html.json.tck.KnockoutTCK; +import org.netbeans.html.ko4j.KO4J; +import org.openide.util.lookup.ServiceProvider; +import org.testng.Assert; +import static org.testng.Assert.*; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +@ServiceProvider(service = KnockoutTCK.class) +public final class JsonKnockoutTest extends KnockoutTCK { + private static Class<?> browserClass; + private static Fn.Presenter browserContext; + + public JsonKnockoutTest() { + } + + @Factory public static Object[] compatibilityTests() throws Exception { + Class[] arr = testClasses(); + for (int i = 0; i < arr.length; i++) { + assertEquals(arr[i].getClassLoader(), + JsonKnockoutTest.class.getClassLoader(), + "All classes loaded by the same classloader" + ); + } + + URI uri = JsonDynamicHTTP.initServer(); + + final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(JsonKnockoutTest.class). + loadPage(uri.toString()). + invoke("initialized"); + + Executors.newSingleThreadExecutor().submit(new Runnable() { + @Override + public void run() { + bb.showAndWait(); + } + }); + + ClassLoader l = getClassLoader(); + List<Object> res = new ArrayList<Object>(); + for (int i = 0; i < arr.length; i++) { + Class<?> c = Class.forName(arr[i].getName(), true, l); + Class<? extends Annotation> koTest = + c.getClassLoader().loadClass(KOTest.class.getName()). + asSubclass(Annotation.class); + for (Method m : c.getMethods()) { + if (m.getAnnotation(koTest) != null) { + res.add(new JsonFX(browserContext, m)); + } + } + } + return res.toArray(); + } + + static synchronized ClassLoader getClassLoader() throws InterruptedException { + while (browserClass == null) { + JsonKnockoutTest.class.wait(); + } + return browserClass.getClassLoader(); + } + + public static synchronized void initialized(Class<?> browserCls) throws Exception { + browserClass = browserCls; + browserContext = Fn.activePresenter(); + JsonKnockoutTest.class.notifyAll(); + } + + public static void initialized() throws Exception { + Assert.assertSame(JsonKnockoutTest.class.getClassLoader(), + ClassLoader.getSystemClassLoader(), + "No special classloaders" + ); + JsonKnockoutTest.initialized(JsonKnockoutTest.class); + } + + @Override + public boolean canFailWebSocketTest() { + return true; + } + + @Override + public BrwsrCtx createContext() { + KO4J ko = new KO4J(browserContext); + XmlHttpResourceContext tc = new XmlHttpResourceContext(); + Contexts.Builder cb = Contexts.newBuilder(). + register(Technology.class, ko.knockout(), 10). + register(Executor.class, (Executor)browserContext, 10). + register(Fn.Presenter.class, (Fn.Presenter)browserContext, 10); + tc.fillContext(cb, browserClass); + return cb.build(); + } + + @Override + public Object createJSON(Map<String, Object> values) { + Object json = createJSON(); + for (Map.Entry<String, Object> entry : values.entrySet()) { + setProperty(json, entry.getKey(), entry.getValue()); + } + return json; + } + + @JavaScriptBody(args = {}, body = "return new Object();") + private static native Object createJSON(); + + @JavaScriptBody(args = {"json", "key", "value"}, body = "json[key] = value;") + private static native void setProperty(Object json, String key, Object value); + + @Override + @JavaScriptBody(args = { "s", "args" }, body = "" + + "var f = new Function(s); " + + "return f.apply(null, args);" + ) + public native Object executeScript(String script, Object[] arguments); + + @JavaScriptBody(args = { }, body = + "var h;" + + "if (!!window && !!window.location && !!window.location.href)\n" + + " h = window.location.href;\n" + + "else " + + " h = null;" + + "return h;\n" + ) + private static native String findBaseURL(); + + @Override + public URI prepareURL(String content, String mimeType, String[] parameters) { + try { + final URL baseURL = new URL(findBaseURL()); + StringBuilder sb = new StringBuilder(); + sb.append("/dynamic?mimeType=").append(mimeType); + for (int i = 0; i < parameters.length; i++) { + sb.append("¶m" + i).append("=").append(parameters[i]); + } + String mangle = content.replace("\n", "%0a") + .replace("\"", "\\\"").replace(" ", "%20"); + sb.append("&content=").append(mangle); + + URL query = new URL(baseURL, sb.toString()); + URLConnection c = query.openConnection(); + BufferedReader br = new BufferedReader(new InputStreamReader(c.getInputStream())); + URI connectTo = new URI(br.readLine()); + return connectTo; + } catch (IOException ex) { + throw new IllegalStateException(ex); + } catch (URISyntaxException ex) { + throw new IllegalStateException(ex); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/xhr4j/src/test/resources/org/netbeans/html/xhr4j/test.html ---------------------------------------------------------------------- diff --git a/xhr4j/src/test/resources/org/netbeans/html/xhr4j/test.html b/xhr4j/src/test/resources/org/netbeans/html/xhr4j/test.html new file mode 100644 index 0000000..3951417 --- /dev/null +++ b/xhr4j/src/test/resources/org/netbeans/html/xhr4j/test.html @@ -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. + +--> +<!DOCTYPE html> +<html> + <head> + <title>XHR via Java Harness</title> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <meta name="viewport" content="width=device-width"> + </head> + <body> + <h1>XHR via Java Harness</h1> + </body> + <script></script> +</html>
