CheerpJ 3.0: a JVM replacement in HTML5 and WASM to run Java on modern browsers
leaningtech.com
leaningtech.com
All we need now is to run ActiveX. I think BottledWine can run Windows executables in the browser, how hard would it be to simulate the ActiveX bindings?
https://www.destroyallsoftware.com/talks/the-birth-and-death...
That’s maybe enough layers of indirection to make ActiveX safe :-)
This reminds me of the time I was running Linux on my desktop PC back in high school. I'd just received a very suspicious .exe email attachment, and to demonstrate Linux's inherent safety (despite not running any antivirus software!) to a friend, I double-clicked it – and Wine, invoked through binfmt_misc, perfectly emulated the virus it contained, writing garbage to my home directory (fortunately mostly the emulated Windows directories in it!).
Edit: the song is likely it https://youtu.be/JwZwkk7q25I
The only thing I'm missing is the ability to find the easter eggs by hitting the tab key like you could do with Flash...
The first, real uses cases for XMLHttpRequests was sending chunks of HTML over the wire and updating the DOM via JavaScript - so “Dynamic HTML”
Kotlin has a great compat with Kotlin/JS[0]. they also have Compose For Web which is more WASM based[1] though its marked as experimental
[0]: https://kotlinlang.org/docs/js-overview.html
[1]: https://github.com/Kotlin/kotlin-wasm-examples/tree/main/com...
In all seriousness, anything that spits out canvas for a simple textbox is completely misguided about what the web is.
I get the joke. It is sad though how Java didn't end up being the ubiquitous runtime for browsers... It started off so promising... Compiling source to byte code is really fast in Java (and modern browsers generally do much more work compiling JavaScript). Even if the browser downloaded source code (and why not? --byte code is so much more compact and faster to parse), you wouldn't even notice the compiling down to bytecode step.
It's amazing how badly the Java ball got dropped.
It still boggles my mind that Sun Microsystems fully owned the tech for the only ubiquitous browser plugin for full fledged applications with > 95% deployment and somehow screwed it up and went out of business a few years later. Despite all their engineering prowess it seems like they just couldn't make Java work seamlessly and smoothly. I'd like to think it was just a hard engineering challenge, but as someone who went all in on Java WebStart for deployment at one point and then watched them take it from a highly attractive platform to actively ruining it within a few years, I have to attribute it more to incompetence.
They never really figured out how to be in the software business if it wasn't in service of selling hardware. Such an amazing fail.
On top of it all, Java integration was really just an excuse to plop a grossly out of place app UI in the browser window. Would have been much better if we could have had deeper behind the scene integration with the DOM layer.
The problem was that the browsers were busy loading up on every feature they could (including JavaScript) while also providing Java runtimes that would load after the fact (and the Java load times were often much better than the browser load times, but since the Java runtime load was deferred, it was a much more painful experience).
You could keep browsers lightweight by doing much of the stuff browsers were doing into Java, but the problem with that is that the browsers were in a race to differentiate from each other, and the whole point of Java was that the experience was uniform. So instead we got a rush of poorly considered browser capabilities that we were stuck with for an extremely long time.
I can't blame the browser makers... Sun never came up with a business proposition for them that encouraged focusing on a better Java experience.
I suspect the goal is a way to bring Java desktop applications into the browser without rewriting the application from scratch.
Personally, as a fan of Blazor, I thought this was going to be a Java-based analog of Blazor; or a modernized replacement for GWT.
(One flavor of Blazor compiles C# to WASM, and uses a C# templating engine to generate HTML.)
I really dislike this stuff. It's easy-ish to write, but it's like the authors of it hate everything about the web.
SPAs do a lot of annoying stuff, and blazor encourages all of it.
I really hope things swing back away from this grotesque tight binding between the server and the client.
You can get there the old-fashioned way if all your assets are cached in the browser with long timeouts; but that implies that you're doing something like putting hashes in the asset URL and hashing them as part of your build process. (This, is why loading JavaScript and CSS from CDNs helps with performance.)
Otherwise, every time you navigate to a new page on a non-SPA, the browser still needs to send an IF-CHANGED-SINCE request to the server.
As a developer, though, the BIG drawback of SPAs is that they require building much stricter APIs for everything. In a traditional server-side HTML page, you can quickly prototype something where the code that's putting together the HTML has access to privileged data that you can't expose through an API. (IE, if you just need to prototype something, server-side HTML rendering code can directly talk to the database.)
But that solves the problem, doesn't it? And it seems a lot simpler than implementing an SPA.
Django has this feature built-in. You just have to enable ManifestStaticFilesStorage: https://docs.djangoproject.com/en/4.2/ref/contrib/staticfile...
Depends on your design goals.
BTW, I'm not pro/against SPAs. It's just a tool, and like all tools, they have their advantages and disadvantages.
In general, the fact that you can update the page without a server-side round trip is a major advantage. Granted, you can also do that with server-side rendering; but then your rendering logic is defined in two places.
TeaVM seems to be the same except it compiles Java to JavaScript which means has fast startup times.
Web browsers and their fundamental development foundations are best described as a set of mostly well meaning compromises, and so even modern browsers have been updated to support all the other disagreeable things you have mentioned, even having the ability to navigate the browser history within an SPA
You can write libraries and either approach can use the same library.
Server-side Blazor keeps a websocket open to send messages back and forth. If you can tolerate the latency, it allows your UI code to be able to handle data that needs to remain secret on the server.
Blazor WebAssemly requires you make explicity Ajax-like Calls to external web services in order to interact with a server and data. There is no server side framework in WebAssembly for maintaining state. State is entirely client side.
Blazor Server basically uses Web Sockets (with fallbacks) to communicate changes between client and server, this is far more efficient than using http request calls. State here is server side. But differs from all other frameworks in that state is trasmitted over sockets not http requests.
I'm not convinced you have actually used any kind blazor tbh
>I'm not convinced you have actually used any kind blazor tbh
good for you, buddy.
A Java option for modern web apps exists, and it's called Flavour: https://flavour.sourceforge.io/
* Build SPAs in Java with HTML templates and components
* Fast build times and batteries-included build framework
* Easy calls to Java web services, just invoke a method and Flavour handles marshalling and unmarshalling, you just see/use Java objects.
* Full-stack refactoring
* Built on TeaVM, so you get the all the benefits of a mature, performant framework with support for threads and multiple JVM languages (bytecode-based transpilation).
TeaVM vs Blazor comparison chart: https://frequal.com/java/TeaVmVsBlazorChart.html
TeaVM longer comparison with Blazor: https://frequal.com/java/TeaVmVsBlazorWasm1-0.html
Java Magazine article on Flavour and TeaVM: https://blogs.oracle.com/javamagazine/post/java-in-the-brows...
Blazor has the full task API. Asynchronous tasks run just like Javascript promises.
Does TeaVM have true threading, or is it emulating threads via some higher-level concept? (IE, if I have a multicore CPU, will TeaVM take advantage of multiple cores?)
Unless you're trying to do true CPU-intense parallel computing inside the browser, where you need the power of real CPU-level concurrency, "real threads" in the browser has no gain over the task API.
It's just a question of time before the containerized crowd discover that the exact version of Chrome they test their website with can be compiled to the WASM+canvas platform, bundled with the website, and solve all browser issues forever...
That sounds like a horrible idea. I hope it never comes to fruition.
https://docs.oracle.com/javase/tutorial/deployment/applet/ma...
Now we are doing it all over again with WASM when it already existed and just wasn't used.
Frameworks, HTML5 or even DHTML/AJAX didn't exist at the time. If you were already paying the cost of invoking Java/Flash/ActiveX, why would you access the DOM and pay extra rendering price - for an interface suited for documents, not applications?
Edit: Repliers live in an alternate reality where good applets were made and HTML didn't win.
EDIT: Java applets failed because of Java's poor integration and (at the time) performance and sandbox security. That had little to do with the DOM interface. Using that would have made things worse.
Not bad at all.
https://github.com/oracle/graal/tree/master/wasm
But the point of JNI is to provide language bindings. WASM doesn't automatically fix the need for those.
True. I was imprecise. I'm fine with needing the language bindings / JNI. My main beef is with the platform specific native binaries that JNI binds too, and the complications (size or platform targeting) that they introduce to the build and distribution process.
And then you also have the problem that WASM/WASI is basically a new OS which is incompatible with every other, so it requires ports.
On the other hand, if you had something like ELF, PE or Mach-O but designed for cross-platform distribution and which had a nice and convenient cross-building toolchain and a portable dynamic linker, then it'd be a lot easier to compile and distribute once but use on every OS. Sort of like MachO fat binaries but more fine grained and not Apple specific.
Graal also has some sort of capacity for that in the way it can execute LLVM bitcode. You can do it either inside a sandbox that looks like a virtualized Linux (syscalls are recompiled into upcalls into the JVM), or it can be allowed to access native code. There is a toolchain that you simply point your PATH at and it produces the necessary files. But, it's not well known, LLVM bitcode still requires JIT compilation before it can execute on the CPU, and the toolchain doesn't really cross-compile in the way I mean above. Plus bitcode isn't a stable format.
I mean... .NET VM and JVM kind of achieve this, but I think I get what you mean, you would either have to build the environment that these binaries run on for each OS, and recipients would have to install it, or you hope the OS vendors adopt your standard. I would love to see such a project though.
Bonus points if it can be similar to DMG files where you drag it to your "Applications" folder to install, delete it to uninstall.
The problem is that you can't solve the problem of developers not adopting new hardware features by abstracting the hardware to a lowest common denominator, which is what WASM does and what it will always do (because the web guys have no interest in letting people write ARM or Intel only web pages, let alone NEON only web pages). You can see this problem in the writeup where the use case is posited to be SIMD, but that's only one of many possible features CPU vendors could add. What about all the others? Now instead of waiting for Android devs to adopt new features, you have to wait for WASM to get it and users to update their OS and Android devs to then adopt the new features as well. This doesn't sound faster.
So I'd guess that a better investment would be in better developer toolchains and emulators.
There are other problems with that approach:
1. How can devs measure performance if it's the Play Store that compiles the app? Upload it, download it and measure that? You never know what you're gonna get and the app may even be recompiled behind your back without you even doing a release.
2. An example of a painful transition was 32->64 bits, said to be hard due to the need for doubled up testing. Well, WASM doesn't fix that and it's not obvious how it could. It has a 32 and 64 bit variant and the need for testing both versions is driven by the way C/C++ work, not the way native code is expressed.
3. Even just abstracting SIMD isn't all that easy. The Java guys have spent years designing a SIMD abstraction that covers up the differences between AVX and NEON. C++ doesn't have any such abstraction, you literally code against intrinsics for the specific instructions. So it would only work if you assume a really awesome auto-vectorization compiler module, but the JVM guys also spent years trying that and eventually gave up. The JVM can auto-vectorize some things, but to exploit the full power of SIMD units automatically is too hard.
Indeed, however it was mostly caused by relying on their own LLVM bitcode fork to achieve the stability that LLVM bitcode doesn't support, and eventually getting fed up of wasting development resources keeping it up to date with upstream.
As for WASM/NDK as replacement for JNI, I don't think it is a good idea, mostly due to how bad the overall NDK experience happens to be, and this won't make it better anyway.
Regarding 3, .NET does much better in this regard, with system numerics and processor specific intrisics.
Usually people tend to forget CLR was designed for C++ workloads as well, and it is reflected on its bytecodes.
WASM in general, I think its place is on the browser, anything else is just yet another take on bytecode deployments since the dawn of computing.
Maybe someone should write a P-Code to WASM compiler, to bring UCSD Pascal back into modern times, and make Pascal cool again.
I too love the idea of embedding wasm modules to be used across platforms.
Then graal has wasm support, but you need graal, not just any JVM (you can also run in stock JVM, but only in interpreted mode with som graal libs, but this mode is not supported).
And both of these are ok-ish for pure functions. If you need callbacks to your host (typically needed for plugins) you are screwed.
The main pain is that it's really really slow to load on firefox.
I remember trying bcc’s Java compiler years ago, and it was significantly slower than standard JITted Java, but maybe better systems exist.
I'd personally always bet on AOT performance to beat JIT - to convince me otherwise is what I would require evidence for.
As a counter-point to your argument, consider a highly dynamic language such as JavaScript, in particular the '+' operator. An AOT compiler would have no choice but dispatch to a generic heavy implementation that could handle any possible combination of inputs. On the other hand a JIT compiler can observe the runtime values used and specialize using Inline Caches, potentially down to just a few instructions.
The time spent optimizing is more than enough when you can focus the resources for the actually relevant parts.
It's some evidence. Perhaps the HotSpot thing outperforms AOT on its own, it could be true.
Somewhat equivalent to HotSpot but in AOT would be GraalVM's profile guided optimization. If runtime information is what brings JIT its advantage, then PGO could bring the same advantage to AOT. I don't have any hard data though.
From GraalVM team themselves, https://youtu.be/sI-zXYLKzfk
For what its worth, there is a vendor that uses LLVM as a JIT compiler (Falcon), but that really is not the bottleneck in most java applications.
Learning the limitations, workarounds and build processes for using anything other than Javascript is going to take longer than just learning Javascript. I get it for moving large C++ applications onto the web, I get it for occasional fiddly bits and pieces like maybe a codec. But new web front end code in anything other than Javascript? Nah.
And I am no lisp enthusiast, just find the abstraction very minimal there, plus good interop with the host language.
The thing with these kinds of ports is that they do some 'neat stuff' but ultimately, they don't come close to the real thing, often, not close enough to be a material substitute.
The JVM is a very sophisticated and nuanced thing, 'duplicating it' and the standard libs is a very big deal.
Theses are cool projects though and they have their use.
From the link:
"CheerpJ is based on an unmodified OpenJDK environment, guaranteeing the same behavior on the browser compared to a native JVM. It includes many emulation layers to ensure Filesystem, Networking, Printing, Clipboard and many other subsystems work seamlessly."
Did you actually manage to find anything that works in OpenJDK but not in CherpJ 3.0?
I could be wrong, but I fail to see how 'WASM memory direct Buffers' will work in that scenario.
This is really an exercise in engineering thinking vs. product or 'solution oriented' thinking. You can usually tell where people's heads are at by how they react to these kinds of things.
Should note that 'people doing stuff because' is a core part of organic development, most things would not exist without that ethos floating around.
And you still haven’t explained in any way why the lack of DirectBuffers or "IO" is a disqualifier for being able to run legacy applets in a modern browser without a JRE or other plugins.
The problem with the 'A Customer Can Run An Applet In A Browser' as a solution, is because this won't materially work for most wide, public style deployments - there will be any number of snafus, and for more 'internal' style IT deployments, there are just easier, more robust ways to deploy a Java app.
I have explained how 'Direct Buffers' are a problem to anyone who understands what VM and Direct Buffers used for (they are inherently about bridging to native memory), which obviously is not going to be accessed in WASM context.
Actually - WASM itself is a great analogy for what is going on here. It's 'perennially almost there' tech aka a neat idea that has limited value in the real world, it's been around for so long and there isn't that much activity, or at least not commensurate with what it's supposed to be able to do. By the time WASM catches up, the performance of JS in V8 gets so much better it becomes 'good enough' which obviates the need for WASM. And so on it goes.
Something may eventually come from JVM in WASM, but what we see is a very early experiment.
That is where most money for WASM is now flowing.
One main benefit of WASM arguably isn't performance of newly developed applications, but the possibility to run arbitrary old applications (or new applications built on massive stacks of historically grown libraries) much more efficiently than emscripten alone allows.
> There might be a niche need.
Of course it's a niche need, Java applets haven't been mainstream for many years now! But as with any technology, a long tail exists.
> I have explained how 'Direct Buffers' are a problem to anyone who understands what VM and Direct Buffers used for (they are inherently about bridging to native memory), which obviously is not going to be accessed in WASM context.
I've only ever used Java on the server – are "Direct Buffers" commonly used by Java applets not bridging to JNI (which obviously won't run in a pure Java emulation/compatibility layer)?
It seems to work fine:
https://javafiddle.leaningtech.com/#JYWwDg9gTgLgBAKwIYDckDoB...
Given how WASM works, I'm pretty sure (but not certain) that anything that uses nio to interface to anything else is not going to work, other than maybe things which are 'fully contained'. The 'test' would be to dynamically load a lib/dll and see if the references are valid, which I doubt, firstly that'd imply that native dll/libs are possible, which I also doubt.
These ports generally don't work 'as expected' the whole endeavour is about identifying the parts that work differently, and if/how to work around them or live within the constraints.
Anyway, let's assume you're talking about a DLL or native lib it gets from the same origin. That also should work totally fine. Because CherpJ implements a virtual file system. I suspect even JNI will work fine. Anyway, doing stuff like that on a browser is way out of the norm, so it seems to me you're looking for a challenge and are not really interested in whether this can be used to run nearly every Java application out there, which it seems it can easily do.
TeaVM has become even smaller and faster since that article was published. The test suites are here in case someone wants to conduct a rematch: https://github.com/renatoathaydes/jvm-alternatives-to-js
Show HN: JavaFiddle – Compile, run and share Java code fully client side - https://news.ycombinator.com/item?id=35874969 - May 2023 (5 comments)
TeaVM apps score quite well on Lighthouse. Previous iterations of CheerpJ had challenges in startup time and download size. Results here: https://renato.athaydes.com/posts/comparing-jvm-alternatives...
For a concrete test, try launching this full TeaVM/Flavour 5-letter word game, it downloads in <2s even on mobile. https://frequal.com/wordii/
Strikes me though that all these "managed-memory language VM inside a a WASM VM" projects for WASM keep having to build out garbage collection support (or run an existing GC inside WASM) for their runtimes. It's a bit of a stacking turtles problem, and seems wildly inefficient. I keep wondering how far off GC support for WASM runtimes is.
In the meantime, while this is neat, I feel like it's a mis-use of what WASM is most suited for: cross-compiling C/C++ etc binaries to a "web" target. WASM as a container for multiple language runtimes seems wildly inefficient.
As WASM GC stabilizes we will consider supporting it as well.
Worker on the client side:
https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers...
Server-side/nodejs: https://nodejs.org/api/worker_threads.html#worker_threads_wo...
One of the things I'm following that will use garbage collection support is the Kotlin wasm compiler which is currently available as an experimental option with kotlin 1.8 and beyond. And of course with wasmer and wasmtime, it will also be possible to target edge computing with this eventually (as they roll out gc support in the next months). Fair warning, this stuff is very cutting edge currently. Wait six months or so for this to become more usable. The upcoming kotlin 2.0 is probably going to be seeing some usable early access version.
There is already some experimental wasm support for Jetpack Compose multiplatform in the works as well. Currently you'd mostly use the kotlin-js compiler for that on the web but pretty soon you should be able to compile to wasm instead. This will work both with components that render to a canvas as html based web components. Advantages of kotlin wasm over kotlin-js: faster compilation and loading speeds. And having used kotlin-js, faster compilation speeds are very much welcome.
If you are into multiplatform UI development, another thing that is interesting with this is the IOS support in compose. So with compose you should be able to target Android, IOS (native), Web (js and wasm) and desktop (jvm). Interesting new framework with not a lot of alternatives beyond react native and Flutter so far. And of course Kotlin is popular on Android and for cross IOS/Android development already. This stuff will take some time to mature but there was a lot of buzz around this topic at kotlin conf a few weeks ago. And of course with wasm and wasm components, you'd be able to do cross platform UI development and integrate the same components via wasm on each platform rather than having to engineer bespoke components for each platform.
Probably not intended for new greenfield projects
Web apps are bad enough as it is. But downloading massive jar files and execute them at a major performance penalty? No thanks. I will actively avoid websites that use heavyweight tech like this.
... but can it actually use multiple cores without limit, like the JVM itself?
Is it a bug or a feature that I can not move the swing window despite it having a top-bar and closing X button?
public class JavaFiddle {
public static void main(String[] args) {
new Thread(() -> System.out.println("Hello World from another thread!")).start();
System.out.println("Hello World!");
}
}
on https://javafiddle.leaningtech.com/ and it works...Back in the days of J2ME, some embedded JVMs emulated threads at the VM level, for example (due to the OS they were running on itself not supporting any threads, which was true for e.g. Palm OS and many non-smartphone OSes). This was called "green threads", as far as I remember.
Very small language initially, with some 1990s warts that we all understand, and new features are only added long after they've proved their worth elsewhere.
This means a lot for long-term hiring and maintenance.