Repository: quarkusio/quarkus
Issue: Unsafe concurrent ClassLoader use 

**Describe the bug**
I've noticed the following exception stack being ignored but logged in a CI test run:

```
2020-12-03T20:03:03.4075698Z [INFO] Running io.quarkus.resteasy.test.ClasspathResourceTestCase
2020-12-03T20:03:04.3307728Z 2020-12-03 20:03:03,388 ERROR [org.jbo.exe.uncaught] (executor-thread-4) Thread Thread[executor-thread-4,5,] threw an uncaught exception: java.lang.RuntimeException: Unable to read org/jboss/threads/ThreadLocalResettingRunnable$Resetter.class
2020-12-03T20:03:04.3311111Z 	at io.quarkus.bootstrap.classloading.JarClassPathElement$1$1$1.apply(JarClassPathElement.java:121)
2020-12-03T20:03:04.3313722Z 	at io.quarkus.bootstrap.classloading.JarClassPathElement$1$1$1.apply(JarClassPathElement.java:108)
2020-12-03T20:03:04.3316967Z 	at io.quarkus.bootstrap.classloading.JarClassPathElement.withJarFile(JarClassPathElement.java:149)
2020-12-03T20:03:04.3320155Z 	at io.quarkus.bootstrap.classloading.JarClassPathElement.access$100(JarClassPathElement.java:33)
2020-12-03T20:03:04.3323321Z 	at io.quarkus.bootstrap.classloading.JarClassPathElement$1$1.getData(JarClassPathElement.java:108)
2020-12-03T20:03:04.3326627Z 	at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:400)
2020-12-03T20:03:04.3332639Z 	at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:365)
2020-12-03T20:03:04.3335164Z 	at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:31)
2020-12-03T20:03:04.3337123Z 	at java.base/java.lang.Thread.run(Thread.java:832)
2020-12-03T20:03:04.3338066Z 	at org.jboss.threads.JBossThread.run(JBossThread.java:479)
2020-12-03T20:03:04.3339091Z Caused by: java.util.zip.ZipException: ZipFile closed
2020-12-03T20:03:04.3340369Z 	at java.base/java.util.zip.ZipFile.ensureOpenOrZipException(ZipFile.java:832)
2020-12-03T20:03:04.3342034Z 	at java.base/java.util.zip.ZipFile$ZipFileInputStream.read(ZipFile.java:922)
2020-12-03T20:03:04.3343569Z 	at java.base/java.util.zip.ZipFile$ZipFileInflaterInputStream.fill(ZipFile.java:445)
2020-12-03T20:03:04.3345719Z 	at java.base/java.util.zip.InflaterInputStream.read(InflaterInputStream.java:158)
2020-12-03T20:03:04.3347235Z 	at java.base/java.io.FilterInputStream.read(FilterInputStream.java:106)
2020-12-03T20:03:04.3349996Z 	at io.quarkus.bootstrap.classloading.JarClassPathElement.readStreamContents(JarClassPathElement.java:231)
2020-12-03T20:03:04.3352995Z 	at io.quarkus.bootstrap.classloading.JarClassPathElement$1$1$1.apply(JarClassPathElement.java:113)
2020-12-03T20:03:04.3354368Z 	... 9 more
2020-12-03T20:03:04.3354597Z 
```

It would seem that there is no protection in `io.quarkus.bootstrap.classloading.JarClassPathElement` against a concurrent `close()` while an ongoing classloading event being performed.

Changed files:
independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/JarClassPathElement.java
independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java
independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/ClassPathElement.java

diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/ClassPathElement.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/ClassPathElement.java
index 251ea050b9c..8ce92e2f850 100644
--- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/ClassPathElement.java
+++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/ClassPathElement.java
@@ -1,7 +1,6 @@
 package io.quarkus.bootstrap.classloading;
 
 import java.io.Closeable;
-import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.security.ProtectionDomain;
@@ -78,7 +77,7 @@ public Manifest getManifest() {
         }
 
         @Override
-        public void close() throws IOException {
+        public void close() {
 
         }
     };
diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/JarClassPathElement.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/JarClassPathElement.java
index ada1f110c0e..9db8c2e9b21 100644
--- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/JarClassPathElement.java
+++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/JarClassPathElement.java
@@ -21,6 +21,9 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 import java.util.function.Function;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
@@ -49,17 +52,25 @@ public class JarClassPathElement implements ClassPathElement {
 
     private static final Logger log = Logger.getLogger(JarClassPathElement.class);
     public static final String META_INF_VERSIONS = "META-INF/versions/";
+
     private final File file;
     private final URL jarPath;
     private final Path root;
-    private JarFile jarFile;
-    private boolean closed;
+    private final Lock readLock;
+    private final Lock writeLock;
+
+    //Closing the jarFile requires the exclusive lock, while reading data from the jarFile requires the shared lock.
+    private final JarFile jarFile;
+    private volatile boolean closed;
 
     public JarClassPathElement(Path root) {
         try {
             jarPath = root.toUri().toURL();
             this.root = root;
             jarFile = JarFiles.create(file = root.toFile());
+            ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
+            this.readLock = readWriteLock.readLock();
+            this.writeLock = readWriteLock.writeLock();
         } catch (IOException e) {
             throw new UncheckedIOException("Error while reading file as JAR: " + root, e);
         }
@@ -137,16 +148,21 @@ public boolean isDirectory() {
     }
 
     private <T> T withJarFile(Function<JarFile, T> func) {
-        if (closed) {
-            //we still need this to work if it is closed, so shutdown hooks work
-            //once it is closed it simply does not hold on to any resources
-            try (JarFile jarFile = JarFiles.create(file)) {
+        readLock.lock();
+        try {
+            if (closed) {
+                //we still need this to work if it is closed, so shutdown hooks work
+                //once it is closed it simply does not hold on to any resources
+                try (JarFile jarFile = JarFiles.create(file)) {
+                    return func.apply(jarFile);
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            } else {
                 return func.apply(jarFile);
-            } catch (IOException e) {
-                throw new RuntimeException(e);
             }
-        } else {
-            return func.apply(jarFile);
+        } finally {
+            readLock.unlock();
         }
     }
 
@@ -192,7 +208,7 @@ public Set<String> apply(JarFile jarFile) {
 
     @Override
     public ProtectionDomain getProtectionDomain(ClassLoader classLoader) {
-        URL url = null;
+        final URL url;
         try {
             URI uri = new URI("file", null, jarPath.getPath(), null);
             url = uri.toURL();
@@ -220,8 +236,13 @@ public Manifest apply(JarFile jarFile) {
 
     @Override
     public void close() throws IOException {
-        closed = true;
-        jarFile.close();
+        writeLock.lock();
+        try {
+            jarFile.close();
+            closed = true;
+        } finally {
+            writeLock.unlock();
+        }
     }
 
     public static byte[] readStreamContents(InputStream inputStream) throws IOException {
diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java
index c7ffad2f2e9..9341215bdd4 100644
--- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java
+++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java
@@ -504,7 +504,7 @@ public void close() {
             //all resources are closed, however the CL can still be used
             //but after close no resources will be held past the scope of an operation
             try (ClassPathElement ignored = element) {
-
+                //the close() operation is implied by the try-with syntax
             } catch (Exception e) {
                 log.error("Failed to close " + element, e);
             }