vaadin / vaadin-gradle-plugin

Gradle plugin for Vaadin 14 applications. Takes care of front-end build, helps to configure repositories and to create various project and file templates.
Apache License 2.0
31 stars 9 forks source link

Build failed - Unable to compute frontend dependencies #81

Closed mcooper7290 closed 4 years ago

mcooper7290 commented 4 years ago

Desktop (please complete the following information):

Describe the bug A clear and concise description of what the bug is. When running "gradle vaadinBuildFrontend" it fails with "Unable to compute frontend dependencies". This occurs if the same command has been previously run and the gradle daemon is still running.

If I kill the gradle daemon and run the same command again it works.

Full stacktrace attached.

gradle-stacktrace.txt

To Reproduce Steps to reproduce the behavior:

  1. Setup project '...'
  2. Run Gradle task '....''
  3. See error '....'

Expected behavior A clear and concise description of what you expected to happen.

Screenshots If applicable, add screenshots to help explain your problem.

Additional context Add any other context about the problem here.

mvysny commented 4 years ago

Hi, thank you for letting us know. Could you please try running Gradle again, but this time with --stacktrace --info switches? That should provide much more insight as to what's going on.

mcooper7290 commented 4 years ago

The log.0.txt file contains output from "gradle --stacktrace --info vaadinBuildFrontend" with the failure. After that I ran "gradle --stop" and then the same "gradle --stacktrace --info vaadinBuildFrontend" which succeeds (see log.1.after.stop.t.xt).

log.0.txt log.1.after.stop.txt

mvysny commented 4 years ago

This looks to be the source of the exception:

JAR entry cmb/cabridge/api/entity/SubjectBindingEntity.class not found in C:\Users\mcooper\ws\cmb\6.0\CmbProduct\Common\build\libs\Common-6.0.85.jar

It could be a bug in Vaadin's class discovery code. Could you please verify that the class exists in the jar file?

Could it be that the jar file is not yet produced completely? Could you please try to disable parallel builds via org.gradle.parallel=false?

mcooper7290 commented 4 years ago

Yes the SubjectBindingEntity.class file is definitely in the Common-X.X.X.jar file.

I ran again using "--no-parallel" and got a similar failure, though it reported a different file:

Caused by: java.io.FileNotFoundException: JAR entry cmb/model/entity/CertificatePurposeEntity.class not found in C:\Users\mcooper\ws\cmb\6.0\CmbProduct\Model\build\libs\Model-6.0.86.jar

I also confirmed Model-6.0.86.jar contains CertificatePurposeEntity.class

log.2.no-parallel.txt

mvysny commented 4 years ago

Thank you so much for taking the time and investigating the issue :+1:

This bug is really interesting, and investigating this issue opens quite a lot of questions - kinda feels like going down a rabbit hole :-D . Comparing the two stack traces:

Caused by: java.io.FileNotFoundException: JAR entry cmb/model/entity/CertificatePurposeEntity.class not found in C:\Users\mcooper\ws\cmb\6.0\CmbProduct\Model\build\libs\Model-6.0.86.jar
    at com.vaadin.flow.server.frontend.scanner.FrontendDependencies.visitClass(FrontendDependencies.java:467)
    at com.vaadin.flow.server.frontend.scanner.FrontendDependencies.visitClass(FrontendDependencies.java:479)

and the older one

Caused by: java.io.FileNotFoundException: JAR entry cmb/cabridge/api/entity/SubjectBindingEntity.class not found in C:\Users\mcooper\ws\cmb\6.0\CmbProduct\Common\build\libs\Common-6.0.85.jar
    at com.vaadin.flow.server.frontend.scanner.FrontendDependencies.visitClass(FrontendDependencies.java:467)
    at com.vaadin.flow.server.frontend.scanner.FrontendDependencies.visitClass(FrontendDependencies.java:479)

we can see that the bug is "caused" by different jar files. That hints to some kind of a threading issue, or alternatively all the files are "somehow broken" at that very point in time, and it's just the matter of iteration order (whichever file is picked first).

I've tried to investigate the stacktrace, but to no avail: the exception is apparently thrown directly by the call to url.openStream() - that's not possible since the exception can only be thrown by a throws clause. This suggests that the JVM is "lying" to us, or somehow collapses the stack-traces. I've found that such an exception can only be thrown at two places on my OpenJDK 1.8: the sun.net.www.protocol.jar.JarURLConnection:142 and sun.net.www.protocol.jar.JarURLConnection:160. The code is pretty horrible and quickly jumps into native code, so it's really hard to see what's going on and whether there actually is a cause exception, masked by the code somewhere.

However, upon investigating, I've discovered that Flow doesn't close the InputStream properly when reading the class file (at FrontendDependencies.visitClass(FrontendDependencies.java:467). Could this be the cause we are looking for? My current theory is that since Flow doesn't close the files properly, they stay open (and locked by the Windows file system) while the Gradle Daemon is running. Running another Gradle build may then start another daemon process, which will try to open the same jar files and fail to do so, since they're opened and locked by the other Daemon process. This would under normal circumstances result in an IOException being thrown; however it's possible that the JVM code swallows the exception somewhere and throws FileNotFoundException instead, much later on :-1: .

This theory is supported by the fact that everything start to work once you kill the Gradle Daemon process. However, opening a file for reading usually doesn't create an exclusive lock on the file, therefore other processes should be able to open the very same jar file for reading as well, which somewhat disproves the hypothesis... Perhaps JarFile accidentally uses exclusive locks?

Could you please try killing the gradle daemon, then building the project repeatedly with --no-daemon flag? If the build succeeds, that would hint that the theory is indeed valid.

Nevertheless, I've created a pull request for Vaadin Flow to correctly close those InputStreams, which could hopefully fix this issue as well :+1: https://github.com/vaadin/flow/pull/9002

mvysny commented 4 years ago

The fix has been merged into Flow and should therefore appear in the next releases of Flow: 4.0.2 for Gradle Plugin master, and 2.3.6 for Gradle Plugin 14.x.

mcooper7290 commented 4 years ago

When I used --no-daemon (e.g. "gr --no-daemon --stacktrace --info vaadinBuildFrontend") the builds worked each time I ran them. So this does seem to confirm your theory.

I will wait for the next releases.

Thanks for fixing!

mvysny commented 4 years ago

Awesome, thank you for confirming :+1: We just need to wait for the next Flow release: https://github.com/vaadin/flow/tags

mvysny commented 4 years ago

The bug is fixed on master, however we're waiting for flow 2.3.6 release to also fix this on the 14.x branch.