Skip to content

Conversation

@jtulach
Copy link
Contributor

@jtulach jtulach commented Feb 23, 2020

The Browser presenter needs an HTTP server to talk to. Original code was using Grizzly, but it has been found too heavyweight adding up to 16MB of overhead in certain setups.

Given the special requirements on the browser/server communication when using netbeans-html4j project (single-threaded JavaScript/HTTP handler works the best) - e.g. something else than regular HTTP servers are optimized for, it makes sense to use a dedicated crafted implementation. The Browser presenter continues to support both servers - however, unless one provides Grizzly libraries explicitly, the simple HTTP server is used. To turn grizzly on, add following dependency into your application pom.xml:

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

This PR provides simple HTTP implementation that is good enough for all Browser presenter purposes.

Copy link

@matthiasblaesing matthiasblaesing left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a bad feeling if people start writing their own http server because its "easy". My experience is, that somewhere something silently breaks and in the end you find, that you broke it yourself.
Given the statement:

Original code was using Grizzly, but I find it too heavyweight and fragile (when I use it in my MPD UI project, it breaks when typing fast).

Leads me to the question: What is fagile in grizzly? It is at the core of the glassfish application server and was/is used for several production sites. I'm pretty sure, that if it would be that fragile I would have seen some fallout from this.

@jtulach
Copy link
Contributor Author

jtulach commented Mar 22, 2020

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.

@matthiasblaesing
Copy link

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.

@jtulach
Copy link
Contributor Author

jtulach commented Mar 22, 2020

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.

@matthiasblaesing
Copy link

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]

@jtulach
Copy link
Contributor Author

jtulach commented Mar 22, 2020

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.

@matthiasblaesing
Copy link

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
Copy link

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.

Browser(String app, Config config) {

Browser(String app, Config config, Supplier<HttpServer<?,?,?, ?>> serverProvider) {
this.serverProvider = serverProvider != null ? serverProvider : SimpleServer::new;
Copy link
Contributor Author

@jtulach jtulach Mar 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By default selects SimpleServer - change to GrizzlyServer to try that one. Plus, as pointed by Matthias, one has to add Grizzly on classpath - currently the grizzly modules only available for compilation & tests - e.g. the provided scope.

@jtulach
Copy link
Contributor Author

jtulach commented Mar 22, 2020

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.

@matthiasblaesing
Copy link

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
Copy link

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

@matthiasblaesing
Copy link

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).

Copy link
Contributor Author

@jtulach jtulach left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking at the code. I am glad I am not the only one who digs through this contribution.

Clearly the problem is a race condition:

  • JavaScript to JVM and back may be one set of problems
  • Grizzly has four threads processing requests in parallel - that adds another complexity
  • the Fn.Presenter JVM code is then executed in yet another worker thread
  • the Generic infrastructure converting calls to messages isn't bulletproof either

Errors can be in every layer.

To shift your Swing example a bit, let's compare HTML/Java system to Swing. Grizzly is just a small misbehaving component like JTable. Replacing it with another component, even homemade is something people normally do without giving up on whole Swing. E.g. Grizzly isn't essential in this case - presenters and especially Generic one are the important pieces.

}
w.write(""
+ " request.open('GET', '" + prefix + "command.js?id=" + id + "', true);\n"
+ " request.setRequestHeader('Content-Type', 'text/plain; charset=utf-8');\n"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I can tell, this is the main idea for running in the browser:

  • the browser opens an asynchronous GET connection and waits for command from the JVM
  • the JVM keeps the connection pending
  • when it needs to execute a JavaScript, it replies to this connection
  • the waitForCommand is then repeated over and over again

}
sb.append(""
+ "request.open('PUT', url, false);\n"
+ "request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8');\n"
Copy link
Contributor Author

@jtulach jtulach Mar 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whenever JavaScript needs to invoke something in the JVM, it initiates new blocking PUT request to the JVM server. JVM is supposed to invoke the desired method and returns the result back to JavaScript.

The trouble starts if the JVM needs to execute some JavaScript while processing the PUT request. Then it returns the JavaScript snippet to execute from this method, lets the JavaScript side process it and invoke another PUT request to continue the JVM execution. Most of this logic is handled by the Generic class as far as I can tell.

@matthiasblaesing
Copy link

matthiasblaesing commented Mar 23, 2020

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
Copy link

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?

@jtulach
Copy link
Contributor Author

jtulach commented Mar 24, 2020

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?

@matthiasblaesing
Copy link

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

@jtulach jtulach force-pushed the AnyHttpServer branch 2 times, most recently from f2ec112 to 255427e Compare March 29, 2020 17:54
@jtulach
Copy link
Contributor Author

jtulach commented Mar 29, 2020

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.

@matthiasblaesing
Copy link

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.

@jtulach
Copy link
Contributor Author

jtulach commented Mar 29, 2020

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.

@matthiasblaesing
Copy link

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.

@jtulach
Copy link
Contributor Author

jtulach commented Apr 1, 2020

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
Copy link
Contributor Author

jtulach commented Jul 2, 2021

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

@JaroslavTulach
Copy link

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.

@matthiasblaesing
Copy link

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.

@JaroslavTulach
Copy link

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.

@jtulach
Copy link
Contributor Author

jtulach commented Nov 29, 2021

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...

@matthiasblaesing
Copy link

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.

Copy link

@matthiasblaesing matthiasblaesing left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As already written I would try not to get through the network layer to access a library (I consider a webview a library). If you can't interact with the webview directly it looks broken to me.

In general I still think, that implementing a HTTP server from scratch is not something that should be done. Maybe this is an alternative:

https://docs.oracle.com/en/java/javase/11/docs/api/jdk.httpserver/com/sun/net/httpserver/HttpServer.html

Its a minimal http server embedded in the JDK. Reading the @SInCE info it is there since SOAP support was implemented (at least I think I remember such a comment) and given JEP 408 and that the class was extended in 18 it is unlikely to go away anytime soon.

@jtulach
Copy link
Contributor Author

jtulach commented Jun 20, 2025

@jtulach
Copy link
Contributor Author

jtulach commented Jun 20, 2025

@jtulach jtulach self-assigned this Jun 21, 2025
@jtulach jtulach merged commit a1ffd57 into apache:master Jun 21, 2025
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants