Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

Official external DevMode server implementations

280 views
Skip to first unread message

Colin Alworth

unread,
Feb 26, 2025, 7:34:37 PMFeb 26
to GWT Contributors
Since before SuperDevMode was first launched, we've been trying to get away from a single-process DevMode in favor of splitting the client and server classpaths. This has been met with resistance by teams who have development workflows that work, and no great benefit to them from changing them. Combine this with our slow update policy for our embedded Jetty version, and you get a lot of frustration. 

GWT 2.12 deprecated the default JettyLauncher, and added a new StaticResourceServer to replace it. In the draft PR https://github.com/gwtproject/gwt/pull/10103 we propose now that GWT 2.13 will switch the default to StaticResourceServer, but before falling back to that default will ask a ServiceLoader if there is exactly one ServletContainerLauncher. Additionally, if more than one SCL is available, they may be selected by a short name rather than the fully qualified class name.

In conjunction with this change, Elias Balasis has been working on a set of SCL implementations that use Cargo to start the server, either externally, or in an isolated classpath. This draft project can be found at https://bitbucket.org/upperlimit-public/gwt-devmode-server, and we would like to propose adopting it as an official gwtproject repository, and providing snapshots and releases of it.

A given project that chooses to use this would then be able to add one of these adapter jars to their project, and when DevMode starts, that server would automatically be launched with their project. Basic tests of this strategy show that it is working - the server can be debugged and have classes hot reloaded, and the client can deploy new JS as needed to each server implementation. We can then accelerate the removal of Jetty from GWT itself, and encourage users to pick one of these official implementations, request/provide missing implementations as needed, or use the base classes to make their own internal implementation.

While I don't love encouraging using DevMode in this way, I also don't want to actively make it harder to continue to develop their projects, and this seems like a decent way to both continue to simplify GWT and reduce its dependencies, but also continue to make it possible for projects to upgrade and preserve their development processes.

Any thoughts on my PR above, or adopting the linked repository under github.com/gwtproject?

eliasb...@gmail.com

unread,
Feb 27, 2025, 6:03:12 PMFeb 27
to GWT Contributors
This looks good to me, introducing discovery of custom launchers by name.

eliasb...@gmail.com

unread,
Feb 28, 2025, 2:04:11 AMFeb 28
to GWT Contributors
This DevMode server Maven modules can be downloaded at:
http://corelimit.net:65481/service/rest/repository/browse/maven-snapshots-public/net/upperlimit/GWT-DevMode-server.impl.embedded.Jetty9.javax/
http://corelimit.net:65481/service/rest/repository/browse/maven-snapshots-public/net/upperlimit/GWT-DevMode-server.impl.embedded.Jetty10.javax/
http://corelimit.net:65481/service/rest/repository/browse/maven-snapshots-public/net/upperlimit/GWT-DevMode-server.impl.embedded.Jetty11.jakarta/

The DeMode server class names are:
net.upperlimit.tools.GWT.DevMode.server.impl.embedded.Jetty9.javax.DevModeServerLauncher
net.upperlimit.tools.GWT.DevMode.server.impl.embedded.Jetty10.javax.DevModeServerLauncher
net.upperlimit.tools.GWT.DevMode.server.impl.embedded.Jetty11.jakarta.DevModeServerLauncher

Support for Java12 will be available, after https://codehaus-cargo.atlassian.net/browse/CARGO-1636 gets released.

I will be adding support for other servers as needed or according to popular demand.

On Thursday, 27 February 2025 at 00:34:37 UTC Colin Alworth wrote:

eliasb...@gmail.com

unread,
Mar 1, 2025, 10:56:42 AMMar 1
to GWT Contributors
The Maven repository coordinates above are wrong.

I will be posting the correct ones soon.

Thomas Broyer

unread,
Mar 6, 2025, 6:52:21 AMMar 6
to GWT Contributors
+1 to switching to StaticResourceServer by default and in the long term removing JettyLauncher then Jetty entirely.

I don't have a strong opinion on using ServiceLoader but I tend to dislike "environmental/contextual" behavior (add a JAR to the classpath and "automagically" this entirely changes the behavior) in favor of always being explicit (in this case, that means using the StaticResourceServer unless told otherwise through an explicit -server option; as to whether that would use an fqcn or ServiceLoader to an SPI to get short names, your call; I'd do that in separate steps though: first default to StaticResourceServer, then possibly add short names)

+1 to adopting and maintaining official implementations (disclaimer: knowing that I won't maintain them myself, but) not having at least "some" official implementations would likely be a show stopper; maybe only have a couple official implementations though, with limited scope (like nowaday's JettyLauncher), and leave the others as "third parties"? (also makes it easier to "abandon" some when/if hardly anybody uses them and/or they become a burden to maintain; and provide more guarantees for the "official" ones?)

eliasb...@gmail.com

unread,
Mar 6, 2025, 7:32:21 AMMar 6
to GWT Contributors
+1 to removing JettyLauncher
but not Jetty entirely, it would still be required by "codeserver" in "DevMode".
Remember, there is no intention to retire DevMode as a whole, only the JettyLauncher.

Regarding ServiceLoader, service name collisions and other side effects are not impossible but only if conventions are not honored, besides the explicit "-server" option will always be present.

Regarding launcher implementations, I will be providing and maintaining a few, including JettyLauncher replacements, some of which we can choose to make official.

Thomas Broyer

unread,
Mar 6, 2025, 7:59:27 AMMar 6
to GWT Contributors
On Thursday, March 6, 2025 at 1:32:21 PM UTC+1 eliasb...@gmail.com wrote:
+1 to removing JettyLauncher
but not Jetty entirely, it would still be required by "codeserver" in "DevMode".

That one can easily be replaced with com.sun.net.httpserver.
It's actually a bigger task replacing the servlet support in JUnitShell (for <servlet/> elements in the gwt.xml, used to exercise GWT-RPC, RequestFactory, and even simple AJAX in GWT's own test suite)

Currently, JettyLauncher is directly used by JUnitShell, so removing it requires rewiring JUnitShell anyway; as briefly discussed in https://github.com/gwtproject/gwt/issues/10092#issuecomment-2643695973 last month.

eliasb...@gmail.com

unread,
Mar 6, 2025, 10:27:31 AMMar 6
to GWT Contributors
Good point.
I do consider retirement of Jetty a rather painful but quite desirable approach.

Perhaps we can consider such a transformation in the future.

Jens

unread,
Mar 6, 2025, 2:18:36 PMMar 6
to GWT Contributors
+1 to adopting and maintaining official implementations (disclaimer: knowing that I won't maintain them myself, but) not having at least "some" official implementations would likely be a show stopper; maybe only have a couple official implementations though, with limited scope (like nowaday's JettyLauncher), and leave the others as "third parties"? (also makes it easier to "abandon" some when/if hardly anybody uses them and/or they become a burden to maintain; and provide more guarantees for the "official" ones?)

I agree that an official implementation should not cover all kinds of servlet containers. The more are supported officially the less an argument can be made to not support additional servlet containers (Where to draw the line?). For every officially supported servlet container implementation someone needs to track the releases, etc. 

Personally I would only provide Jetty 12 as the  single official implementation because it supports EE8, EE9 and EE10 via configuration. That keeps the maintenance burden low while covering EE8-10 and possibly future EE versions. It also matches what GWT offers out of the box today. 

-- J.

eliasb...@gmail.com

unread,
Mar 6, 2025, 3:56:50 PMMar 6
to GWT Contributors
Good point.

Indeed, not too many kinds of servlet containers are needed.

I have implemented embedded Jetty9, Jetty10 and Jetty11 so far.

I am currently working on embedded Jetty12 with EE10 and I would consider EE8, EE9 and future variants.

We could choose to make any of these official somehow.

eliasb...@gmail.com

unread,
Mar 18, 2025, 9:01:59 AMMar 18
to GWT Contributors
I have published the first version of the DevMode server implementations to Maven Central at https://central.sonatype.com/search?q=o.bitbucket.upperlimit-public

<groupId>io.bitbucket.upperlimit-public</groupId>

<artifactId>GWT-DevMode-server.impl.embedded.Jetty9.javax</artifactId> Server class net.upperlimit.tools.GWT.DevMode.server.impl.embedded.launcher.Jetty9.DevModeServerLauncher

<artifactId>GWT-DevMode-server.impl.embedded.Jetty10.javax</artifactId>
Server class net.upperlimit.tools.GWT.DevMode.server.impl.embedded.launcher.Jetty10.DevModeServerLauncher <artifactId>GWT-DevMode-server.impl.embedded.Jetty11.jakarta</artifactId> Server class net.upperlimit.tools.GWT.DevMode.server.impl.embedded.launcher.Jetty11.DevModeServerLauncher
<artifactId>GWT-DevMode-server.impl.Cargo.installed.Jetty9.javax</artifactId> Server class net.upperlimit.tools.GWT.DevMode.server.impl.Cargo.installed.Jetty9.javax.DevModeServerLauncher
<artifactId>GWT-DevMode-server.impl.Cargo.installed.Jetty10.javax</artifactId>
Server class net.upperlimit.tools.GWT.DevMode.server.impl.Cargo.installed.Jetty10.javax.DevModeServerLauncher  
<artifactId>GWT-DevMode-server.impl.Cargo.installed.Jetty11.jakarta</artifactId> Server class net.upperlimit.tools.GWT.DevMode.server.impl.Cargo.installed.Jetty11.jakarta.DevModeServerLauncher

Jetty12 implementation is facing a few obstacles but I am working on it...

For those interested in experimentation, use DevMode with "-server" parameter set to the desired server implementation class in your GWT Plugin launch configuration and run.

The server implementations under the "embedded" package run in the same JVM as DevMode but with isolated classpath, using a child-first class loader.
This approach make classpath fixes possible because the servlet container runs in the same JVM as DevMode.

The server implementations under the "installed" package start a servlet container on a separate JVM, which you can debug by passing the :debuggerPort=<port> option to the "-server" parameter.
This approach cannot deal with classpath issues because the servlet container is running on a separate JVM.

I intend to create alternative Jetty embedded implementations as soon as we manage to decouple Jetty from GWT.

I can create more server implementations upon request but I would suggest caution not to create more than actually needed.

Let me know your thoughts.

eliasb...@gmail.com

unread,
Mar 25, 2025, 11:28:27 AMMar 25
to GWT Contributors
0.0.12 is out, supporting Jetty12 (ee10)

eliasb...@gmail.com

unread,
Mar 26, 2025, 4:15:59 AMMar 26
to GWT Contributors
0.0.17 has been published to Maven central.

eliasb...@gmail.com

unread,
Apr 3, 2025, 2:25:20 PMApr 3
to GWT Contributors
After having discovered further classpath/classloader complications trying to use my custom launchers within actual applications,
I am leaning towards deciding to postpone further efforts,
until Jetty gets decoupled from GWT.

I will update if I discover any workaround that could keep the current effort going.

Colin Alworth

unread,
Apr 4, 2025, 10:23:17 AMApr 4
to GWT Contributors
Can you elaborate here, or in the issue tracker, on what doesn't work, or provide some example projects that demonstrate the issue?

Decoupling GWT from the server it internally uses is likely a first step but could never be a last one - cases like this will continue to break if the server application ever shares any classes with anything on the server classpath - any wrong version of apache commons, JDT, htmlunit, etc could come back and bite us later if we don't have proper isolation. Servlet containers have successfully isolated running applications from each other for decades, and applications that understand they are being isolated in this way must recognize certain limitations, so this should be a relatively solved problem.

eliasb...@gmail.com

unread,
Apr 4, 2025, 12:48:55 PMApr 4
to GWT Contributors
Relatively solved indeed, that's what I expected, which worked for my PoC modules.

The first problem I encountered was loading some application classes through the DevMode launcher's classloader and some others from the application classloader.
for example: SpringFramework interfaces loaded from the isolated DevMode classloader but concrete implementation classes loaded from the application classloader, which produced class loading errors.

I had to start excluding dependencies from the Jetty container's WEB-INF/lib folder using a specific technique so that the classes would be all loaded from the classloader of DevMode instead, but even that wasn't enough.

The problem got worse when I tried to make use of even more dynamic frameworks like SpringSecurity, being unable to prepare the underlying component chains, which is where I stopped.

Overall, it seems that even though the DevMode classloader is isolated, a lot of trickery may still be required.

eliasb...@gmail.com

unread,
Apr 4, 2025, 1:11:04 PMApr 4
to GWT Contributors
Correction:
Even though the DevMode launcher's classloader is isolated, a lot of trickery may still be required, to eliminate classloading issues and others I haven't been able to resolve yet or worse that I haven't come across yet.

So, I started thinking that if an isolated DevMode launcher classloader was not needed anymore, implying decoupling of Jetty from GWT, there would be fewer problems.

Thomas Broyer

unread,
Apr 4, 2025, 1:12:19 PMApr 4
to google-web-tool...@googlegroups.com
On Fri, Apr 4, 2025 at 6:48 PM eliasb...@gmail.com <eliasb...@gmail.com> wrote:
Relatively solved indeed, that's what I expected, which worked for my PoC modules.

The first problem I encountered was loading some application classes through the DevMode launcher's classloader and some others from the application classloader.
for example: SpringFramework interfaces loaded from the isolated DevMode classloader but concrete implementation classes loaded from the application classloader, which produced class loading errors.

I had to start excluding dependencies from the Jetty container's WEB-INF/lib folder using a specific technique so that the classes would be all loaded from the classloader of DevMode instead, but even that wasn't enough.

The problem got worse when I tried to make use of even more dynamic frameworks like SpringSecurity, being unable to prepare the underlying component chains, which is where I stopped.

Overall, it seems that even though the DevMode classloader is isolated, a lot of trickery may still be required.

I haven't looked at the code, but in case you were on that path: please do not try to mimic the current behavior where server-side classes can be loaded from the classpath. Having a breaking change like this is a great opportunity to ditch that and just require that server-side classes are within WEB-INF/classes and WEB-INF/lib. That way, you use the server's already implemented and battle-tested classloaders respecting the Jakarta Servlet specs for precedence and isolation.
That custom classloader was the main reason Jetty wasn't updated more often (in addition to changing its API in minor or even patch releases).

eliasb...@gmail.com

unread,
Apr 4, 2025, 1:20:25 PMApr 4
to GWT Contributors
I understand.

I am actually loading all Jetty classes using a custom child-first classloader and let everything else be loaded by the parent classloaders chain.

That was expected to be sufficient but apparently it is not, a lot of trickery is still required.

Wejden Mrabti

unread,
Apr 19, 2025, 5:06:09 PM (2 days ago) Apr 19
to GWT Contributors

Hi everyone,

Recently, I’ve been working on migrating our GWT application (Java 17 – GWT 2.12.2) to run on Jetty 11.0.25, and I followed the approach  described  by @elias using the Cargo API to launch the app in DevMode.

Here’s what I’ve done so far:

  • I'm launching the app on eclipse IDE  with: -war my_war_path -server net.upperlimit.tools.GWT.DevMode.server.impl.Cargo.installed.Jetty11.jakarta.DevModeServerLauncher

  • Since debugging didn’t work out-of-the-box from Eclipse, I tried attaching a Remote Java Application configuration.

  • To support that, I overrode the DevModeServerLauncher to expose JDWP arguments via VM options, with: -DdebuggerPort=5007 -DdebuggerSuspend=n

  • This allows me to debug server-side code from Eclipse, but client-side debugging (GWT) remains tricky, and Super Dev Mode / hot code reloading doesn't seems to work properly.

So far this setup (based on an installed Jetty instance) feels a bit cumbersome and disconnected from the Eclipse workflow. It seems to spawn a separate JVM, which complicates debugging and classpath management.

 I’m wondering if there’s a recommended way to launch the app using the "embedded Jetty" approach, ideally within Eclipse, to better match developer expectations — single JVM, smoother debugging, and working hot reload.

Any advice, working examples, or gotchas would be greatly appreciated!

Thanks a lot in advance!

eliasb...@gmail.com

unread,
Apr 19, 2025, 5:21:38 PM (2 days ago) Apr 19
to GWT Contributors
I am fully aware that spawning a separate JVM carries its own complications.

I have provided embedded flavours for Jetty 9,10 and 11 but I am afraid, as I pointed out above, I have met further complications.

At this point I am rather at a dead end, at least with Jetty.

Jens

unread,
Apr 20, 2025, 6:04:57 PM (8 hours ago) Apr 20
to GWT Contributors
Would it be too bad to not use embedded container but instead installed container with Cargo? They also provide a simple Java API to download and unpack a servlet container release archive locally. So DevMode could download and install a servlet container in a GWT specific location and then use that. The download URL could also be made configurable using a gwt-devmode.properties file in the project similar to how the gradlew script installs gradle locally if needed.

Cargo documentation explicitly says that embedded containers can quickly end up in classloader / JAR hell and have other downsides: https://codehaus-cargo.github.io/cargo/Embedded+Container.html

-- J.

Reply all
Reply to author
Forward
0 new messages