netbeans-html4j icon indicating copy to clipboard operation
netbeans-html4j copied to clipboard

Replacing Grizzly with lightweight HTTP server

Open jtulach opened this issue 5 years ago • 26 comments

The Browser presenter needs an HTTP server to talk to. Original code was using Grizzly, but I find it too heavyweight. Now there is an experimental branch jtulach/WebViews and it currently contains a copy of Browser presenter - we don't want Grizzly server in the platform or IDE, right?

Given the special requirements on the browser/server communication (single-threaded JavaScript/HTTP handler is the best) - e.g. something else than regular HTTP servers are optimized for, I think it makes sense to craft own one. The code will support both servers - however, unless one provides Grizzly libraries explicitly, the simple HTTP server is going to be used:

        <dependency>
            <groupId>org.glassfish.grizzly</groupId>
            <artifactId>grizzly-http-server</artifactId>
            <version>2.3.19</version>
        </dependency>

This PR introduces simple HTTP implementation written by me. Subsequent commits need to polish and complete the implementation and make sure it can be used instead of Grizzly for the Browser presenter purposes.

jtulach avatar Feb 23 '20 05:02 jtulach

What's fragile in Grizzly? Well, for example the browser presenter with Grizzly doesn't run on Windows while using IE11 as the display browser. The browser presenter with SimpleServer runs as tested on dfb2de8 and on build #1294 which timed in browser presenter out and build #1298 which in run fine and failed in just a few tests.

Being able to run browser tests on Windows seems good enough reason to integrate.

I generally agree with not re-inventing the wheels approach, however in this case (which is not a typical HTTP server case) Grizzly brings no benefits and just problems.

jtulach avatar Mar 22 '20 12:03 jtulach

You claim that grizzly is fragile - where is your analysis? Where is the issue report against grizzly? So isn't the right summary: "The html4j implementation makes assumption about the underlying http server, that are generally not met by 'normal' http servers, html4j thus has a hard dependency on the bundled http-like implementation".

Can you provide me with a debuggable (i.e. interactive) testcase? I'd like to see what happens there.

matthiasblaesing avatar Mar 22 '20 12:03 matthiasblaesing

Where is the issue report against grizzly?

Another problem with Grizzly is that it is not maintained anymore. The project page says "...activity is frozen until...", but the problem is that Eclipse already has Jetty and I don't think both projects will survive. It makes no sense to report a bug against Grizzly.

The html4j implementation makes assumption about the underlying http server

That's not fair, Matthias! The first commit in this PR: 6cf8cc4 actually abstracts away any dependencies on the underlaying server. As such it should be easier than ever to plug in alternative implementation. See HttpServer.java for the abstracted contract. The tests are executed against both: the GrizzlyServer as well as the SimpleServer.

Can you provide me with a debuggable (i.e. interactive) testcase?

Thanks in advance for any help. I can give you a sample, but I can't guarantee it is going to misbehave. Errors occur only under heavier load.

I am fixing the browser presenter to make my mpdui project more reliable. Checkout the AnyHttpServer branch and then:

mpdui$ mvn clean install -DskipTests # shoudn't matter if it fails after building client subproject
mpdui$ mvn -f client -Pbrowser-presenter process-classes exec:exec -Dexec.server.arg=server -DskipTests

or you can open the project in NetBeans and execute Run Maven - Run as server. Then connect to port 6680 with any browser. Having a running mpd daemon helps to have something to test, but if you want easy failure, then connect with IE11, then the application won't even bootup with GrizzlyServer.

jtulach avatar Mar 22 '20 13:03 jtulach

For grizzly: I assume, that they have to do the same cleanup as the Apache NetBeans project, so I would not discard grizzly that easily.

For the mpdui project: I figured out, that I had to initialized the submodules, but building after that fails:

[ERROR] Failed to execute goal org.multi-os-engine:moe-maven:1.4.1:setupSDK (default) on project mpdui-moe: Execution default of goal org.multi-os-engine:moe-maven:1.4.1:setupSDK failed: Plugin org.multi-os-engine:moe-maven:1.4.1 or one of its dependencies could not be resolved: Failed to collect dependencies at org.multi-os-engine:moe-maven:jar:1.4.1 -> org.gradle:gradle-tooling-api:jar:3.0: Failed to read artifact descriptor for org.gradle:gradle-tooling-api:jar:3.0: Could not transfer artifact org.gradle:gradle-tooling-api:pom:3.0 from/to gradle (http://repo.gradle.org/gradle/libs-releases-local/): Access denied to: http://repo.gradle.org/gradle/libs-releases-local/org/gradle/gradle-tooling-api/3.0/gradle-tooling-api-3.0.pom -> [Help 1]

matthiasblaesing avatar Mar 22 '20 13:03 matthiasblaesing

I don't have the access denied problem, locally:

Could not transfer artifact org.gradle:gradle-tooling-api:pom:3.0 Access denied to: http://repo.gradle.org/gradle/libs-releases-local/org/gradle/gradle-tooling-api/3.0/gradle-tooling

I guess a lot of modules were compiled OK. Continue with the second command which works with client project. It doesn't need MOE, RoboVM neither Android.

jtulach avatar Mar 22 '20 13:03 jtulach

Ok - checked out the project, initialized submodules, build on windows, run with build failures (document in previous comment), works in IE 11 and Edge as far as I can say

matthiasblaesing avatar Mar 22 '20 14:03 matthiasblaesing

It would really help to have a minimal reproducer - that way the problem could be pin pointed. The mpdui project is to complex and shows the same problem as many JS projects: It pulls in big piles of depedencies and I'm sidetracked by failures, that are not the ones that need to be fixed.

matthiasblaesing avatar Mar 22 '20 14:03 matthiasblaesing

works in IE 11 and Edge as far as I can say

Have you switched to Grizzly? You have to change the code in Browser constructor.

to have a minimal reproducer

There is no simple reproducer. Both solutions GrizzlyServer as well as SimpleServer work fine most of the time. But sometimes they misbehave - running browser project tests on and on may show the failures. Just like the #1298 run showed failure in loadAndParseJSONP and in paintTheGridOnClick.

jtulach avatar Mar 22 '20 16:03 jtulach

The grizzly integration is broken:

Exception in thread "main" java.lang.NoClassDefFoundError: org/glassfish/grizzly/PortRange
        at org.netbeans.html.presenters.browser.GrizzlyServer.init(GrizzlyServer.java:35)
        at org.netbeans.html.presenters.browser.Browser.displayPage(Browser.java:231)
        at net.java.html.boot.BrowserBuilder.showAndWait(BrowserBuilder.java:369)
        at cz.xelfi.music.mpdui.Main.main(Main.java:39)
Caused by: java.lang.ClassNotFoundException: org.glassfish.grizzly.PortRange
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
        ... 4 more

This happens on Windows and Linux

matthiasblaesing avatar Mar 22 '20 19:03 matthiasblaesing

Ok - Problem is the browser/pom.xml held grizzly as provided dependency

matthiasblaesing avatar Mar 22 '20 19:03 matthiasblaesing

For booting in IE11: I disabled the debugging (set org.netbeans.html.presenters.browser.Browser.Config#debug to false) and get as far as with edge. This is grizzly running on linux and IE11 on windows.

Sorry - I'd have to dig through too much code at this time to help further. Yes this is a repetition: Without an analysis exchanging the http implementation on the server side will only plaster over the underlying problem. Most probably there is a problem in the way the communication between browser frontend and http backend is realised.

At this point, instead of throwing with a different implementation at the problem, I would try to reduce the problem to its core. Consider a problem in Swing: You would not throw away the hole implementation, just to find a problem - you'd try to isolate the issue and fix that.

I don't think it is helpful to claim grizzly is unstable, my gut feeling is, that it is just much faster/featureful, than the custom implementation that works. So if html4j should not be tied to one http-like implementation, this needs to be pin pointed. Maybe we are seeing a race condition here (JS in the browser is single threaded, but if requests are issued asynchronously, you'll still see race conditions).

matthiasblaesing avatar Mar 22 '20 20:03 matthiasblaesing

Is there a simple description how I reach a runnable sample? I tried to start the samples from netbeans master, but that results in Unittest failures and is missing documentation how finally run the sample. I'd like to create a minimal browser sample to see the basic machinery, but there are currently to many pitfalls to get to the point. (just noticed, that the failing unittests is no indication, that the project is broken arg)

To illustrate: For swing I invoke a single class, get a Frame, a hello wold, an input field. That is dead simple and for HTML4J I get a multi module project - why so complex?

matthiasblaesing avatar Mar 23 '20 19:03 matthiasblaesing

Now I ran the demo and contrary to my observation on mpdui I don't see any network traffic - it looks as if everything is run in the browser?

matthiasblaesing avatar Mar 23 '20 19:03 matthiasblaesing

There is a blog post describing how to turn the browser-presenter on.

For swing I invoke a single class, get a Frame

It is certainly simple to use Swing to develop desktop application. Is it also that simple to run swing on iOS, Android and in a webpage?

jtulach avatar Mar 24 '20 08:03 jtulach

I anyone else follows the blog post and fails (I got an UnsatisfiedLinkError because my Ubuntu System has no webkitget-3.0), you have to:

  • disable the javafx profile (the archetype assumes the JDK bundles FX, which was never the case for any OpenJDK (the profile is located in the file client/pom.xml)
  • force the browser to AWT, else the webkit browser is tried and blows. Add <argument>-Dcom.dukescript.presenters.browser=AWT</argument> to the configuration of the exec-maven-plugin as the first argument

matthiasblaesing avatar Mar 24 '20 19:03 matthiasblaesing

I am satisfied with the current state of SimpleServer. It works way better for the MPDUI project than the [email protected] version. Once I get the JDK11 Travis crashes (unrelated to the server implementation) under control, I'll integrate.

jtulach avatar Mar 29 '20 18:03 jtulach

From me this is a -1 (as already indicated). I still don't see an analysis, that suggests, that verifies, that grizzly is the problem here. So from my perspective the underlying problem is covered, but not solved. In the long run we will end up with an unmaintained piece of code. While trying to investigate I already ran into multiple problems that show bitrot (dependency on an outdated libwebkit, inability to run on OpenJDK 11) and in a few months noone will remember why this special http server was needed.

matthiasblaesing avatar Mar 29 '20 18:03 matthiasblaesing

Thanks for trying to review my clean up, Matthias. If you have comments about my code changes introducing broken code, please add them next to appropriate lines.

I see it is not easy dig through all the layers that stay in a way. Feel free to report separate bugs as:

  • outdated libwebkit isn't really related to browser presenter
  • do you really have a problem with OpenJDK 11?
  • maybe just OpenJDK8? boot-fx module is known to require JDK8 with JavaFX.

In the long run we will end up with an unmaintained piece of code.

I have to say that I would rather maintain code that I have written than code that has been donated and provably doesn't work.

jtulach avatar Mar 29 '20 19:03 jtulach

and provably doesn't work.

You claim grizzly is broken, yet don't substantiate it ("With my code it works" is neither an analysis of the underlying problem, nor a proof).

For the record I did not do a review - a review can't be done until the problem is understood and I don't think that is the case here. I expressed, that I don't think the code is a step into the right direction.

matthiasblaesing avatar Mar 29 '20 20:03 matthiasblaesing

You are right in both points, Matthias. First of all I haven't provided enough justification to convince you that the move away from Grizzly is good idea. Second, you haven't provided the review I was hoping for. However rather than continuing discussing soundness of my coding practices, I'd like to ask other reviewers to join. Hopefully we'll be able to focus on the code change and not how good/bad programmer I am. @eppleton, @dukescript, @jlahoda, @sdedic, @tzezula, can you please join and help me find threading bugs in SimpleServer and/or in other previously existing code?

jtulach avatar Apr 01 '20 14:04 jtulach

I believe ceb72dd0e9b192e1ce0f0ac77cc9aad07f83922c is the fix Matthias was calling for. Release 1.7.2 seems to provide stable Generic presenter even with Grizzly.

jtulach avatar Jul 02 '21 03:07 jtulach

Now there is an experimental branch jtulach/WebViews and it currently contains a copy of Browser presenter together with the SimpleServer code - we don't want Grizzly server in the platform or IDE, right? However, it'd be better to avoid the copy altogether (done in #42) and host the server code in HTML/Java API rather than letting other modules to sneak in via package private classes or making the pluggable server implementation a public API. In my opinion.

JaroslavTulach avatar Nov 23 '21 08:11 JaroslavTulach

Now there is an experimental branch jtulach/WebViews and it currently contains a copy of Browser presenter together with the SimpleServer code - we don't want Grizzly server in the platform or IDE, right?

I don't want the IDE to open listening network ports to be show a simple GUI. If we really open listening ports with the IDE to show internal UIs, we need to have a more serious discussion, because at that point the IDE becomes a security problem and raises questions like: How is access secured and what is done to ensure that the IDE can't be misused for attacks.

matthiasblaesing avatar Nov 24 '21 19:11 matthiasblaesing

The focus of your review shifted and I take it as a sign that replacing Grizzly is no longer a non-sense.

we need to have a more serious discussion, because at that point the IDE becomes a security problem

Alas, that's a valid concern. I'll keep that in mind before creating a PR for jtulach/WebViews. However it is not really related to the question whether such security problem would be caused by Grizzly or home made server.

JaroslavTulach avatar Nov 25 '21 12:11 JaroslavTulach

I am facing a dilemma, Matthias. I need to integrate the jtulach/WebViews in two weeks. Launching the HTTP server is the only solution I have for providing rich refactoring UIs, but the security issue is real.

The only way I can think of to avoid it is: Create a random UID. Use it for the first connection between the browser and the server. Then stop accepting further request. That'd be safe, right? Plus stop listening on first wrong UID connection or if no connection is made in 5s...

jtulach avatar Nov 29 '21 15:11 jtulach

Launching the HTTP server is the only solution I have for providing rich refactoring UIs, but the security issue is real.

Ähm - NetBeans is not such a bad IDE and it is build around Swing. I know it en en vouge to bash Swing and to call it dead and all, but it is here and I don't see it dying (at least there is no valid successor visible right now). Refactoring works right now.

And yes, I see it when a HTML renderer is used, it is visually different and looks worse, than plain Swing.

The only way I can think of to avoid it is: Create a random UID. Use it for the first connection between the browser and the server. Then stop accepting further request. That'd be safe, right? Plus stop listening on first wrong UID connection or if no connection is made in 5s...

That is one way - or why not use an in-memory transport? That way you don't get zero exposure and direct interaction between the runtimes. For the OpenJFX webview a direct interaction between Java and Javascript code is possible by injecting a Java object into the JS context of the Webview. I bet other environment offer similar options, I doubt, that all webview integration go though a network port for interaction.

matthiasblaesing avatar Nov 29 '21 19:11 matthiasblaesing