takari / maven-wrapper

The easiest way to integrate Maven into your project!

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Concurrent wrapper downloads fail

dagguh opened this issue · comments

Steps

  1. Use Maven Wrapper 0.4.2 in two separate projects
  2. Remove your ~/.m2/wrapper cache
  3. Run both projects via mvnw in parallel
  4. Go to step 2, unless you've been here 10 times already

Expectation

All calls work fine every time.

Reality

5 times are fine.
4 times, we get:

Exception in thread "main" java.util.zip.ZipException: error in opening zip file
	at java.util.zip.ZipFile.open(Native Method)
	at java.util.zip.ZipFile.<init>(ZipFile.java:225)
	at java.util.zip.ZipFile.<init>(ZipFile.java:155)
	at java.util.zip.ZipFile.<init>(ZipFile.java:169)
	at org.apache.maven.wrapper.Installer.unzip(Installer.java:161)
	at org.apache.maven.wrapper.Installer.createDist(Installer.java:78)
	at org.apache.maven.wrapper.WrapperExecutor.execute(WrapperExecutor.java:121)
	at org.apache.maven.wrapper.MavenWrapperMain.main(MavenWrapperMain.java:55)

1 time, we get:

Exception in thread "main" java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.apache.maven.wrapper.BootstrapMainStarter.start(BootstrapMainStarter.java:39)
	at org.apache.maven.wrapper.WrapperExecutor.execute(WrapperExecutor.java:122)
	at org.apache.maven.wrapper.MavenWrapperMain.main(MavenWrapperMain.java:55)
Caused by: java.lang.NoClassDefFoundError: org/eclipse/aether/transfer/TransferListener
	at java.lang.Class.getDeclaredMethods0(Native Method)
	at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
	at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
	at java.lang.Class.getMethod0(Class.java:3018)
	at java.lang.Class.getMethod(Class.java:1784)
	at org.codehaus.plexus.classworlds.launcher.Launcher.getEnhancedMainMethod(Launcher.java:172)
	at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:268)
	at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
	at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
	at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
	... 7 more
Caused by: java.lang.ClassNotFoundException: org.eclipse.aether.transfer.TransferListener
	at org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy.loadClass(SelfFirstStrategy.java:50)
	at org.codehaus.plexus.classworlds.realm.ClassRealm.unsynchronizedLoadClass(ClassRealm.java:271)
	at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:247)
	at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:239)
	... 17 more

Use case

We provide a JVM library. We provide runnable Maven projects as examples. They use Maven Wrapper to reduce the barrier of entry for users, contributors and CI. Our integration tests run these examples. We run integration tests in parallel to reduce the build time.

PS. It might have happened before: https://github.com/rimerosolutions/maven-wrapper-example/issues/6

At this stage I dont think maven wrapper is designed to run in the same VM multiple times in parallel. You would have to debug and see if you can create a fix. We will not have time any time soon to look into this edgecase.

Why not just run the sequentially .. this is only ever a problem if there is no maven wrapper installed distro in place and two start at the same time in parallel. In the real world usage I dont anticipate this to be a problem. Even for your test runs.. just run those few in sequence. Parallel is not realistic in this scenario since you are violating the principle of not having side effect in your tests...

Why not just run the sequentially

Because we run integration tests in parallel to reduce the build time.

In the real world usage I dont anticipate this to be a problem.

Integration tests on a CI server are a real world usage for us.

Parallel is not realistic in this scenario since you are violating the principle of not having side effect in your tests...

Unit tests should indeed have no side-effects, but functional and integration tests are meant to interact with real systems like databases and filesystems. They should still be isolated from each other, e.g. work on different, temporary DB instances and separate filesystem directories. And we do isolate them as best we can, we have two different mvnw files in two separate directories, with separate pom.xml and everything else. However, the "global cache" nature of Maven Wrapper breaks that isolation. Is there a way to override the "cache" path via the CLI?

You can set the MAVEN_USER_HOME env var for this purpose
See https://github.com/takari/maven-wrapper/blob/master/README.md#specifying-maven-distribution-base-part

Set it to different value for each parallel build
But be aware that if the local maven repository is used by several maven build at the same time you may also have concurrency errors. For instance, especially when 2 builds are downloading the same dependency at the same time
You can also check https://github.com/takari/takari-local-repository which aims to fix this issue