Add JavaScript platform to jpackage: Draft 1
Summary
jpackage already allows packaging Java applications for several platforms. This proposal adds a new platform: JavaScript.This effort will enable jpackage to convert bytecode from the provided classes into JavaScript, and generate the required HTML to invoke the specified main method when opened in a web browser. These files will be bundled into a WAR file for easy deployment.
Goals
- Enabling JVM languages to build client-side web applications
- Allow easy generation of JavaScript from JVM bytecode
- Allow easy deployment and execution of generated JavaScript in web browsers
- Allow easy deployment of the generated JavaScript in all web server environments
- Java web application container (like Tomcat)
- Static file web servers
- Static file web hosting services
Non-Goals
- Allowing execution of JavaScript server-side. (Java already has numerous options for executing bytecode server-side.)
Motivation
Java was once used to create client-side web applications via applets that could be launched by visiting a web page. Applets could draw on an area of the screen (like HTML5 Canvas) or manipulate the page DOM to create dynamic front-end applications (like JS single-page apps).However, as evident in JEP 398 (https://openjdk.java.net/jeps/398), applets are no longer feasible due to the actions of browser vendors. While browsers have lost the ability to execute Java bytecode or invoke methods from the Java class libraries, they do have mature engines for executing a different sort of code (JavaScript) and an extensive list of useful APIs. By converting class files to JavaScript, and providing mechanisms to invoke browser APIs, Java can again be used to create in-browser applications. TeaVM has demonstrated that this is feasible and has numerous benefits:
- Provides a strongly-typed language for client-side web development
- Provides a wealth of IDEs, build tools, and testing tools for client-side web development
- Allows teams with Java experience to produce apps with familiar technology
- Allows sharing of POJO and business logic classes, simplifying development
- Allows options for porting applet- and JNLP-based systems to present-day browsers
Details
An additional jpackage option for type will be added:js
jpackage will use a JavaScript AOT compiler (TeaVM) to convert the Java code to JavaScript, with the main class compiled to a JavaScript method called 'main()'.
jpackage bundles application code, runtime, and resources into a platform-specific format. For this new JavaScript type, the layout will be either a ZIP file or a standard WAR file. The ZIP format will contain the files ready to be extracted to a static file webserver or HTML hosting service. Generated WARs will have the required structure to be deployable in a Java web application container.
WAR layout
- HelloApp.war
- index.html (Main application page, loads classes.js and invokes main())
- teavm
- classes.js (Class files, templates, and resources compiled to JavaScript)
- css
- (CSS files from application)
- META-INF
- MANIFEST.MF
- WEB-INF
- web.xml
ZIP Layout
- HelloApp.zip
- index.html (Main application page, loads classes.js and invokes main())
- teavm
- classes.js (Class files, templates, and resources compiled to JavaScript)
- css
- (CSS files from application)
Basic usage: Non-modular applications
Command-line usage is similar to jpackage today, except you use the--type js
. For example, if you have your application JARs in a folder called lib
and the JAR with the declared main()
method is main.jar
, you could use this command:
$ jpackage --type js --name myapp --input lib --main-jar main.jarThis will produce
myapp.war
in the current directory. This is a standard WAR file ready for deployment in any web application container (like Tomcat). When myapp/index.html is opened in a browser, the code in main() will be executed, in-browser. A typical Hello World main() method like
public static void main(String args[]) { System.out.println("Hello, Browser!"); }will print the message on the browser developer console.
Processing
Conversion of the input JAR files to the classes.js file will be done by TeaVM. It will- Convert provided class files to JavaScript
- Expose the specified main method as main()
- Provide implementation of selected core Java classes that function in a browser environment
- Bundle resources into the generated JavaScript
- Include images, css, and web.xml in the generated package, if provided
- Provide default index.html if omitted
- Provide default web.xml if omitted and WAR format specified
- Optionally minify the generated JavaScript
js-specific options
-
--minify
: Perform a minification pass after generating JavaScript, renaming classes and methods to short, generated names to reduce download sizes and provide some obfuscation. -
--debug
: Enable generation of source maps. -
--debug-full
: Enable generation of source maps and bundled source files. -
--optimization
: Choose simple, advanced, or full.- simple: Perform only basic optimizations
- advanced: Perform more optimizations. Recommended for production.
- full: Perform aggressive optimizations. Increases compilation time.
-
--timezone-support
: Enables timezone support, at the cost of increased application size -
--locale-list
: Add extra locales via a list, at the cost of increased application size. Format: comma-separated list of locale IDs like "en_US, ru_RU"
Unsupported options for the JavaScript type
These options are unsupported for--type js
-
--file-associations
: Not yet meaningful for a web-based app, though it may be in the future once PWAs support file types: https://github.com/WICG/file-handling -
--app-version, --copyright, --description, --license-file, --vendor
: jpackage will only support --name initially. Users can customize index.html (and the rest of the application) to show branding and metadata as desired. -
--java-options
: Not yet supported, use--arguments
instead.
Caveats
Certain Java classes are not feasible to implement in a browser setting. Socket, for example, is not useful in a browser since JavaScript cannot open arbitrary socket connections. Code using unavailable classes will fail during packaging time with warnings about the missing classes.Testing
Since TeaVM is Java-based, tests will be able to run on any platform.Testing will focus on the new jpackage code and added functionality. Tests will confirm that when valid parameters are provided, that output is generated with the right name and in the right folder. Contents of the generated ZIP and WAR files will be checked for the presence of expected files. Testing generated files in a browser will be done manually.
A thorough test of TeaVM itself is out of scope for the jpackage testing. This is in line with jpackage testing for other platforms, in which the external packaging tool (like Wix on Windows) isn't exhaustively tested.
Dependencies
The jpackagejs
type will require TeaVM binaries to be present.
Implementation options:
- Download TeaVM on-demand and cache it. (This is the likely option.)
- Look for TeaVM in local repositories for popular build tools like Maven and Gradle
- If not found locally, download TeaVM binaries from the read-only central repository and store in the cache folder
- Invoke TeaVM from the local repository or cache
- Require that TeaVM binaries be installed locally
- Provide the path to TeaVM binaries on the command line
- Bundle TeaVM
- Challenging due to incompatible licenses (Apache v2 vs. GPL v2 with CPE)
- Probably unnecessary given the options above. Other jpackage options require pre-installed tools, this will be no different.
High-Level Design
A new bundler will be added to the jpackage Java source code.It will first ensure that TeaVM binaries (JAR files) are available locally, as described in the section above.
The new bundler will use TeaVM's TeaVMRunner (https://github.com/konsoletyper/teavm/blob/master/tools/cli/src/main/java/org/teavm/cli/TeaVMRunner.java), which conveniently accepts options similar to jpackage itself. TeaVMRunner will do the heavy lifting of converting the application JAR files and resources into classes.js
.
The bundler will provide additional files required to make a web application, including an index.html
to launch the main()
method. The bundler will create the final archive (ZIP or WAR) using Java's ZipOutputStream. For the WAR format, the bundler will also add web.xml
and MANIFEST.MF
if not present to create a deployable, standard WAR file.