Repository: quarkusio/quarkus
Issue: Gradle multimodule project + quarkus-container-image-jib: OverlappingFileLockException

**Describe the bug**
I have a gradle + Kotlin multi-module project and try to build container images with the quarkus-container-image-jib extension.  When I run `./gradlew assemble -Dquarkus.container-image.build=true`, I get an `OverlappingFileLockException` (on Windows 10 as well as on the `gradle:6.6-jdk11` docker image). 

Apart from the issues I described in #11900, I can build both images by manually running `./gradlew :serviceA:assemble -Dquarkus.container-image.build=true` and then `./gradlew :serviceB:assemble -Dquarkus.container-image.build=true`. 

Detailed exception: 

```
17:28:34: Executing task 'assemble -Dquarkus.container-image.build=true'...


> Task :serviceA:quarkusGenerateCode
preparing quarkus application
kotlin scripting plugin: project ':serviceA'.compileQuarkus-generated-sourcesKotlin - configuration not found: quarkus-generated-sourcesKotlinScriptDef, the plugin is probably applied by a mistake

> Task :serviceB:quarkusGenerateCode
preparing quarkus application
kotlin scripting plugin: project ':serviceB'.compileQuarkus-generated-sourcesKotlin - configuration not found: quarkus-generated-sourcesKotlinScriptDef, the plugin is probably applied by a mistake

> Task :serviceA:quarkusGenerateCodeTests
preparing quarkus application
kotlin scripting plugin: project ':serviceA'.compileQuarkus-test-generated-sourcesKotlin - configuration not found: quarkus-test-generated-sourcesKotlinScriptDef, the plugin is probably applied by a mistake

> Task :serviceB:quarkusGenerateCodeTests
preparing quarkus application
kotlin scripting plugin: project ':serviceB'.compileQuarkus-test-generated-sourcesKotlin - configuration not found: quarkus-test-generated-sourcesKotlinScriptDef, the plugin is probably applied by a mistake

> Task :serviceB:compileKotlin
> Task :serviceA:compileKotlin
> Task :serviceA:compileJava NO-SOURCE
> Task :serviceB:compileJava NO-SOURCE
> Task :serviceA:processResources
> Task :serviceB:processResources
> Task :serviceA:classes
> Task :serviceB:classes
> Task :serviceA:inspectClassesForKotlinIC
> Task :serviceB:inspectClassesForKotlinIC
> Task :serviceA:jar
> Task :serviceB:jar

> Task :serviceA:quarkusBuild
building quarkus jar

Base image 'fabric8/java-alpine-openjdk11-jre' does not use a specific image digest - build may not be reproducible
Base image 'fabric8/java-alpine-openjdk11-jre' does not use a specific image digest - build may not be reproducible

> Task :serviceB:quarkusBuild
building quarkus jar

> Task :serviceB:quarkusBuild FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':serviceB:quarkusBuild'.
> io.quarkus.builder.BuildException: Build failure: Build failed due to errors
  	[error]: Build step io.quarkus.container.image.jib.deployment.JibProcessor#buildFromJar threw an exception: java.lang.RuntimeException: Unable to create container image
  	at io.quarkus.container.image.jib.deployment.JibProcessor.containerize(JibProcessor.java:153)
  	at io.quarkus.container.image.jib.deployment.JibProcessor.buildFromJar(JibProcessor.java:102)
  	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  	at java.base/java.lang.reflect.Method.invoke(Method.java:567)
  	at io.quarkus.deployment.ExtensionLoader$2.execute(ExtensionLoader.java:932)
  	at io.quarkus.builder.BuildContext.run(BuildContext.java:277)
  	at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
  	at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2046)
  	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1578)
  	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1452)
  	at java.base/java.lang.Thread.run(Thread.java:830)
  	at org.jboss.threads.JBossThread.run(JBossThread.java:479)
  Caused by: java.util.concurrent.ExecutionException: java.nio.channels.OverlappingFileLockException
  	at com.google.common.util.concurrent.AbstractFuture.getDoneValue(AbstractFuture.java:552)
  	at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture.java:533)
  	at com.google.common.util.concurrent.FluentFuture$TrustedFuture.get(FluentFuture.java:82)
  	at com.google.cloud.tools.jib.builder.steps.StepsRunner.lambda$buildImage$8(StepsRunner.java:344)
  	at com.google.common.util.concurrent.TrustedListenableFutureTask$TrustedFutureInterruptibleTask.runInterruptibly(TrustedListenableFutureTask.java:125)
  	at com.google.common.util.concurrent.InterruptibleTask.run(InterruptibleTask.java:57)
  	at com.google.common.util.concurrent.TrustedListenableFutureTask.run(TrustedListenableFutureTask.java:78)
  	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
  	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
  	at java.base/java.lang.Thread.run(Thread.java:830)
  Caused by: java.nio.channels.OverlappingFileLockException
  	at java.base/sun.nio.ch.FileLockTable.checkList(FileLockTable.java:229)
  	at java.base/sun.nio.ch.FileLockTable.add(FileLockTable.java:123)
  	at java.base/sun.nio.ch.FileChannelImpl.lock(FileChannelImpl.java:1110)
  	at java.base/java.nio.channels.FileChannel.lock(FileChannel.java:1076)
  	at com.google.cloud.tools.jib.filesystem.LockFile.lock(LockFile.java:67)
  	at com.google.cloud.tools.jib.cache.CacheStorageWriter.writeMetadata(CacheStorageWriter.java:315)
  	at com.google.cloud.tools.jib.cache.Cache.writeMetadata(Cache.java:81)
  	at com.google.cloud.tools.jib.builder.steps.PullBaseImageStep.pullBaseImage(PullBaseImageStep.java:249)
  	at com.google.cloud.tools.jib.builder.steps.PullBaseImageStep.call(PullBaseImageStep.java:160)
  	at com.google.cloud.tools.jib.builder.steps.PullBaseImageStep.call(PullBaseImageStep.java:56)
  	... 6 more


* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 26s
14 actionable tasks: 14 executed
17:29:00: Task execution finished 'assemble -Dquarkus.container-image.build=true'.
```


**To Reproduce**
Steps to reproduce the behavior:
1. Dowload reproducer project and unzip it [2020-09-04_jib-multimodule-problems.zip](https://github.com/quarkusio/quarkus/files/5175735/2020-09-04_jib-multimodule-problems.zip)
2. Run  `./gradlew assemble -Dquarkus.container-image.build=true`
3. See the error shown above.


**Environment (please complete the following information):**
 - Windows 10 (also happens on `gradle:6.6-jdk11` docker image in my CI builds)
- Gradle
- Kotlin



Changed files:
extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java

diff --git a/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java b/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java
index e406fed36f2..2d79781a403 100644
--- a/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java
+++ b/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java
@@ -6,7 +6,6 @@
 import static io.quarkus.container.image.deployment.util.EnablementUtil.buildContainerImageNeeded;
 import static io.quarkus.container.image.deployment.util.EnablementUtil.pushContainerImageNeeded;
 import static io.quarkus.container.util.PathsUtil.findMainSourcesRoot;
-import static io.quarkus.deployment.pkg.PackageConfig.MUTABLE_JAR;
 
 import java.io.IOException;
 import java.io.UncheckedIOException;
@@ -26,6 +25,7 @@
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.ExecutionException;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -33,6 +33,7 @@
 import org.eclipse.microprofile.config.ConfigProvider;
 import org.jboss.logging.Logger;
 
+import com.google.cloud.tools.jib.api.CacheDirectoryCreationException;
 import com.google.cloud.tools.jib.api.Containerizer;
 import com.google.cloud.tools.jib.api.DockerDaemonImage;
 import com.google.cloud.tools.jib.api.ImageReference;
@@ -42,6 +43,7 @@
 import com.google.cloud.tools.jib.api.JibContainer;
 import com.google.cloud.tools.jib.api.JibContainerBuilder;
 import com.google.cloud.tools.jib.api.LogEvent;
+import com.google.cloud.tools.jib.api.RegistryException;
 import com.google.cloud.tools.jib.api.RegistryImage;
 import com.google.cloud.tools.jib.api.buildplan.AbsoluteUnixPath;
 import com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer;
@@ -249,7 +251,7 @@ private JibContainer containerize(ContainerImageConfig containerImageConfig,
             previousContextStorageSysProp = System.setProperty(OPENTELEMETRY_CONTEXT_CONTEXT_STORAGE_PROVIDER_SYS_PROP,
                     "default");
 
-            JibContainer container = jibContainerBuilder.containerize(containerizer);
+            JibContainer container = containerizeUnderLock(jibContainerBuilder, containerizer);
             log.infof("%s container image %s (%s)\\n",
                     containerImageConfig.isPushExplicitlyEnabled() ? "Pushed" : "Created",
                     container.getTargetImage(),
@@ -300,7 +302,7 @@ private Containerizer createContainerizer(ContainerImageConfig containerImageCon
         }
         containerizer.setToolName("Quarkus");
         containerizer.setToolVersion(Version.getVersion());
-        containerizer.addEventHandler(LogEvent.class, (e) -> {
+        containerizer.addEventHandler(LogEvent.class, e -> {
             if (!e.getMessage().isEmpty()) {
                 log.log(toJBossLoggingLevel(e.getLevel()), e.getMessage());
             }
@@ -311,6 +313,29 @@ private Containerizer createContainerizer(ContainerImageConfig containerImageCon
         return containerizer;
     }
 
+    /**
+     * Wraps the containerize invocation in a synchronized block to avoid OverlappingFileLockException when running parallel jib
+     * builds (e.g. mvn -T2 ...).
+     * Each build thread uses its own augmentation CL (which is why the OverlappingFileLockException prevention in jib doesn't
+     * work here), so the lock object
+     * has to be loaded via the parent classloader so that all build threads lock the same object.
+     * QuarkusAugmentor was chosen semi-randomly (note: quarkus-core-deployment is visible to that parent CL, this jib extension
+     * is not!).
+     */
+    private JibContainer containerizeUnderLock(JibContainerBuilder jibContainerBuilder, Containerizer containerizer)
+            throws InterruptedException, RegistryException, IOException, CacheDirectoryCreationException, ExecutionException {
+        Class<?> lockObj = getClass();
+        ClassLoader parentCL = getClass().getClassLoader().getParent();
+        try {
+            lockObj = parentCL.loadClass("io.quarkus.deployment.QuarkusAugmentor");
+        } catch (ClassNotFoundException e) {
+            log.warnf("Could not load io.quarkus.deployment.QuarkusAugmentor with parent classloader: %s", parentCL);
+        }
+        synchronized (lockObj) {
+            return jibContainerBuilder.containerize(containerizer);
+        }
+    }
+
     private void writeOutputFiles(JibContainer jibContainer, ContainerImageJibConfig jibConfig,
             OutputTargetBuildItem outputTarget) {
         doWriteOutputFile(outputTarget, Paths.get(jibConfig.imageDigestFile), jibContainer.getDigest().toString());