Gradle JavaExec task fails to start embedded Jetty server on Windows

130 views Asked by At

I've been migrating a Maven build to Gradle and have been experiencing some weird behavior specifically on Windows. I've been trying to figure out for a while now with no luck.

Consider the following details:

  1. Gradle 8.3 is being used
  2. A java web application has been written that uses an embedded jetty server (and also bundles it with the produced jar)
  3. A Gradle JavaExec task called startWebapp that is meant for developer purposes and runs the com.example.Application (which then starts the embedded jetty server)
  4. An IntelliJ run configuration that does the same thing as 3. (but it's simply going through IntelliJ which then delegates to Gradle and dynamically generates the same JavaExec task)

The problem:

When you start the application via IntelliJ (4.) everything always works just fine and Jetty is able to find everything on the classpath and the application works just fine (including Vaadin v23):

Classpath: C:\Users\user\project\webapp\build\classes\java\main;C:\Users\user\Desktop\project\build\resources\main;C:\.g\caches\modules-2\files-2.1\org.vaadin.addons.mekaso\progress\1.1.0\80e3e1cd9d58b654a805b5bb3beeeb498eceaf1a\progress-1.1.0.jar;C:\Users\user\project\data\build\libs\data-1.0-SNAPSHOT.jar;C:\Users\user\project\data\build\libs\data-jooq.jar;C:\.g\caches\modules-2\files-2.1\org.slf4j\jcl-over-slf4j\1.7.32\32c060250bcc5282cdbc1fd7008c12eb4ebad00e\jcl-over-slf4j-1.7.32.jar;.....
...
...
22:19:03.285 [main] [1] INFO  o.e.j.server.handler.ContextHandler - Started o.e.j.w.WebAppContext@3316aef6{PORTAL,/,[
    file:///C:/Users/user/Desktop/project/webapp/build/resources/main/webapp/,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/vaadin-time-picker-flow/23.3.18/a24ef5ffac099aae7dfe63cec074c018646cca40/vaadin-time-picker-flow-23.3.18.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/vaadin-iron-list-flow/23.3.18/d7d4585c24110ac1f9c7f933e8997fb6c3ef8a80/vaadin-iron-list-flow-23.3.18.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/vaadin-context-menu-flow/23.3.18/fec91948198bf2eb3ac1510641ca7a2d41018b42/vaadin-context-menu-flow-23.3.18.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/vaadin-date-picker-flow/23.3.18/36374182ba4d0ea0bb99e66d93e52ba6119eb37c/vaadin-date-picker-flow-23.3.18.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/vaadin-combo-box-flow/23.3.18/97ee34b921ebf7ff336eff57b29798baf7a35c5b/vaadin-combo-box-flow-23.3.18.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/org.webjars.npm/mobile-drag-drop/2.3.0-rc.2/5a95a71ecc9e41b2253b80badc1ad0a9b8f3b7a4/mobile-drag-drop-2.3.0-rc.2.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/org.vaadin.artur/a-vaadin-helper/1.6.0/692ea343800b5ea51adb1651bcaf98e627aafaa/a-vaadin-helper-1.6.0.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/org.vaadin.tatu/twincolselect/2.5.2/4a59c13f997078a52768f7721fa492493dbc105/twincolselect-2.5.2.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/vaadin-cookie-consent-flow/23.3.18/e99f90304a8c9b22dc6f12506d4850e2746ca18f/vaadin-cookie-consent-flow-23.3.18.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/vaadin-select-flow/23.3.18/813e308dd1a5b35547b2d338e1dcb7753d676722/vaadin-select-flow-23.3.18.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/vaadin-text-field-flow/23.3.18/e7ef3de85325333a88b6bd224a4144fea4898734/vaadin-text-field-flow-23.3.18.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/flow-push/23.3.16/460e0a502c38511257436b71c334e98d90e7cb3c/flow-push-23.3.16.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/vaadin-grid-flow/23.3.18/cf01e2c605443bddc83d0c759d4f76e663b3bde7/vaadin-grid-flow-23.3.18.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/vaadin-flow-components-base/23.3.18/662f5bc9b17fb5f48e83543c207454397d38c584/vaadin-flow-components-base-23.3.18.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/org.vaadin.addons.componentfactory/vcf-pdf-viewer/2.7.2/a69ba7388a84b033f110b31dd70eef67cec74c4d/vcf-pdf-viewer-2.7.2.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/vaadin-lumo-theme/23.3.18/aab7be1217208672705d42cec3a63b809fdee694/vaadin-lumo-theme-23.3.18.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/vaadin-login-flow/23.3.18/ac9ae3ac844c2e68db1b67e8665a5739d5b61490/vaadin-login-flow-23.3.18.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/flow-client/23.3.16/49ee696e8d9c0089e6e3c3a66730c16cd0db36f/flow-client-23.3.16.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.github.appreciated/vaadin-css-grid/2.0.0/c18252cef8e53dd0e35102a658f97d6683b0cf2c/vaadin-css-grid-2.0.0.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin.componentfactory/togglebutton/3.0.0/3bddbf166f35f4d825494b2a167dd03e007c8ded/togglebutton-3.0.0.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/org.vaadin.olli/file-download-wrapper/6.0.0/fffa0c0d5fac979c69f494526626bab154e4919f/file-download-wrapper-6.0.0.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/flow-dnd/23.3.16/860aa3d49c2c4199761df3bc996d9aecfb3d17a8/flow-dnd-23.3.16.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/vaadin-confirm-dialog-flow/23.3.18/9745a1d982e73f4075f4c5f0a5e878c99e607e17/vaadin-confirm-dialog-flow-23.3.18.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/org.webjars.npm/vaadin__vaadin-mobile-drag-drop/1.0.1/57cea9e7d3c1f5cebaa8c2e99b07c9bdc734878e/vaadin__vaadin-mobile-drag-drop-1.0.1.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/vaadin-notification-flow/23.3.18/de2be63f0d7358f2b909bf2c6bc6c00512cd4efa/vaadin-notification-flow-23.3.18.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/vaadin-renderer-flow/23.3.18/316bdaa15e5e2f884fc000f8d2440e8af6c646b8/vaadin-renderer-flow-23.3.18.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/vaadin-grid-pro-flow/23.3.18/dc2133f6795e76ed256b6dc30b6c0f5f6905d6e0/vaadin-grid-pro-flow-23.3.18.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/vaadin-material-theme/23.3.18/621aa9d5dadc14e0ecfe61fdb907144f54bb553b/vaadin-material-theme-23.3.18.jar!/META-INF/resources,
    file:///C:/Users/user/Desktop/project/webapp/build/resources/main/META-INF/resources/,
    jar:file:///C:/.g/caches/modules-2/files-2.1/in.virit/viritin/1.7.4/956eb20f7d9e55bf170d793ad138fce19d4e604b/viritin-1.7.4.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/org.vaadin.tarek/collapsible-splitlayout/2.0.0/4ca3511471467448c5d02fd9e2c59db8575e7421/collapsible-splitlayout-2.0.0.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/vaadin-virtual-list-flow/23.3.18/c9c59b1af59ea56dc2487f1badaca4215caa4e29/vaadin-virtual-list-flow-23.3.18.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/vaadin-messages-flow/23.3.18/292e3f13db5723d98e06d07cd38585ba6f0ed619/vaadin-messages-flow-23.3.18.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/org.vaddon/mediaquery/4.0.1/74d28205eb548468a02c2fc22645f1f08559e3ac/mediaquery-4.0.1.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/vaadin-menu-bar-flow/23.3.18/8b1fc8b01569604dcbb81c0b9fdd9d3412ea5124/vaadin-menu-bar-flow-23.3.18.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/de.jfancy/star-rating/1.0.3/9ae9580fa72c2a736279d81a89186fe9e2a86ccf/star-rating-1.0.3.jar!/META-INF/resources,
    jar:file:///C:/.g/caches/modules-2/files-2.1/com.vaadin/vaadin-dialog-flow/23.3.18/c1f001d5f346db87c70968d0e5402b1249be7bba/vaadin-dialog-flow-23.3.18.jar!/META-INF/resources
],AVAILABLE}

However, once you start the application from the JavaExec task called startWebapp specifically on Windows -- the application doesn't work at all. It works on Linux/OSx, but never on Windows even with the automatic jar pathing Gradle does:

Classpath: C:\.g\.tmp\gradle-javaexec-classpath14042817672156947631.jar
...
...
22:17:37.212 [main] [1] INFO  o.e.j.server.handler.ContextHandler - Started o.e.j.w.WebAppContext@57fd93f9{PORTAL,/,[
    file:///C:/Users/user/Desktop/project/webapp/build/resources/main/webapp/,
],AVAILABLE}

And also you would occasionally get this exception in the logs

com.vaadin.flow.server.StaticFileHandler.serveStaticResource(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)" because "this.staticFileHandler" is null

Or this one (Vaadin build failure of some kind)

------------------ Starting Frontend compilation. ------------------
22:17:51.584 [ForkJoinPool.commonPool-worker-1] [48] INFO  c.v.b.d.AbstractDevServerRunner - Running Vite to compile frontend resources. This may take a moment, please stand by...
22:17:52.018 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker - node:events:492
22:17:52.018 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker -       throw er; // Unhandled 'error' event
22:17:52.018 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker -       ^
22:17:52.018 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker -
22:17:52.018 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker - Error: read ENOTCONN
22:17:52.019 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker -     at tryReadStart (node:net:683:20)
22:17:52.019 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker -     at Socket._read (node:net:694:5)
22:17:52.019 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker -     at Readable.read (node:internal/streams/readable:496:12)
22:17:52.019 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker -     at Socket.read (node:net:750:39)
22:17:52.019 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker -     at new Socket (node:net:471:12)
22:17:52.019 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker -     at Object.Socket (node:net:340:41)
22:17:52.019 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker -     at createSocket (node:internal/child_process:328:14)
22:17:52.020 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker -     at ChildProcess.spawn (node:internal/child_process:443:23)
22:17:52.020 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker -     at Object.spawn (node:child_process:757:9)
22:17:52.020 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker -     at ensureServiceIsRunning (C:\Users\user\Desktop\project\webapp\node_modules\esbuild\lib\main.js:2052:29)
22:17:52.020 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker - Emitted 'error' event on Socket instance at:
22:17:52.020 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker -     at emitErrorNT (node:internal/streams/destroy:151:8)
22:17:52.021 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker -     at emitErrorCloseNT (node:internal/streams/destroy:116:3)
22:17:52.021 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker -     at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
22:17:52.021 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker -   errno: -4053,
22:17:52.021 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker -   code: 'ENOTCONN',
22:17:52.021 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker -   syscall: 'read'
22:17:52.021 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker - }
22:17:52.021 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker -
22:17:52.021 [dev-server-output] [71] INFO  c.v.b.d.DevServerOutputTracker - Node.js v18.17.1
------------------ Frontend compilation failed. ------------------

Setup

  1. The IntelliJ Run configuration

enter image description here

  1. The startWebapp gradle task is:

     create<JavaExec>("startWebapp") {
         val workingDirPath = projectDir
         val javaTmp = buildDir.resolve("tmp/startWebapp").resolve(System.currentTimeMillis().toString())
    
         systemProperties = mapOf(
             // This is specifically necessary for Windows to avoid "Access Denied" exceptions because
             // for some reason it tries to write to `C:\Windows\jetty-0_0_0_0-8080-webapp-_-any-12211458786276099803`
             "java.io.tmpdir" to javaTmp
         )
         classpath = mainSourceSet.runtimeClasspath
         mainClass.set(mainClassString)
         args = listOf("--profile=local-defaults", "-ea")
         workingDir = workingDirPath
    
         doFirst {
             println("Set -Djava.io.tmpdir=$javaTmp and create directory.")
             javaTmp.mkdirs()
         }
    
         dependsOn("processResources") // make sure git.properties is generated.
         dependsOn("vaadinPrepareFrontend")
     }
    

What I've tried

  1. Manual pathing (similar to Gradle's, but with relative to the pathing.jar paths)
  2. Create an use a argFile that contains exactly the same classpath as the one that works from IntelliJ's run configuration.

Both of these attempts have failed and Jetty just doesn't seem to detect the resources no matter what I do (again.. specifically on WINDOWS).

Ideas?

I suspect IntellIJ is doing some magic behind the scenes that I'm missing. Or maybe there's a configuration flag that needs to be flipped in Jetty that would make it also work with "pathing jars"?

Answers to comments:

  1. C:\.g\.tmp\gradle-javaexec-classpath14042817672156947631.jar!META-INF/MANIFEST.MF

     Manifest-Version: 1.0
     Class-Path: file:/C:/Users/user/--------------project----------------/we
      bapp/build/classes/java/main/ file:/C:/Users/user/---------p---------/-
      ---project------/webapp/build/resources/main/ file:/C:/.g/caches/module
      s-2/files-2.1/org.vaadin.addons.mekaso/progress/1.1.0/80e3e1cd9d58b654a
      805b5bb3beeeb498eceaf1a/progress-1.1.0.jar file:/C:/Users/user/-------/
      --------------project--------/data/build/libs/data-1.0-SNAPSHOT.jar fil
      e:/C:/Users/user/--------------project----------------/data/build/libs/
      data-jooq.jar file:/C:/.g/caches/modules-2/files-2.1/org.slf4j/jcl-over
      -slf4j/1.7.32/32c060250bcc5282cdbc1fd7008c12eb4ebad00e/jcl-over-slf4j-1
      .7.32.jar file:/C:/.g/caches/modules-2/files-2.1/org.slf4j/jul-to-slf4j
      /1.7.32/8a055c04ab44e8e8326901cadf89080721348bdb/jul-to-slf4j-1.7.32.ja
      r file:/C:/.g/caches/modules-2/files-2.1/ch.qos.logback/logback-classic
      /1.2.9/7d495522b08a9a66084bf417e70eedf95ef706bc/logback-classic-1.2.9.j
      ar file:/C:/.g/caches/modules-2/files-2.1/com.twilio.sdk/twilio/9.9.0/e
      8efda227f790e472d9d9253944416a84fd66907/twilio-9.9.0.jar
      ... Trimmed, but contains all classpath artifacts ...
    
1

There are 1 answers

1
Joakim Erdfelt On

Your Class-Path manifest entry shows absolute URLs.

That's not what the spec for JAR and what the Class-Path Attribute says.

See: https://docs.oracle.com/en/java/javase/17/docs/specs/jar/jar.html#class-path-attribute

Those are supposed to be relative URL references.

The absolute entries violates point 2 in the link above. (copy/pasted here)

A Class-Path entry is valid if the following conditions are true:

  • It can be used to create a URL, by resolving it against the context JAR’s URL.
  • It is relative, not absolute, i.e. it does not contain a scheme component, except for the case when the context JAR is loaded from the file system, in which case the file scheme is permitted for compatibility reasons.
  • The location of the JAR file or directory represented by this entry is contained within the containing directory of the context JAR. Use of "../" to navigate to the parent directory is not permitted, except for the case when the context JAR is loaded from the file system.

Invalid entries are ignored. Valid entries are resolved against the context JAR. If the resulting URL is invalid or refers to a resource that cannot be found, then it is ignored. Duplicate URLs are ignored.

I went ahead and filed an issue at https://github.com/gradle/gradle/issues/26313