Repository: quarkusio/quarkus
Issue: mvn quarkus:dev freezes in a project with bidirectional JPA relationships

**Describe the bug**
Having the following entities in a Quarkus app makes `mvn compile quarkus:dev` freeze: 

```java
@Entity
@Table(name = "owners")
public class Owner {

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "owner")
    private Set<Pet> pets;

    public Set<Pet> getPets() {
        return pets;
    }

    public void setPets(Set<Pet> pets) {
        this.pets = pets;
    }
}
```

And 
```java
@Entity
@Table(name = "pets")
public class Pet {

    @ManyToOne
    @JoinColumn(name = "owner_id")
    private Owner owner;

    public Owner getOwner() {
        return owner;
    }

    public void setOwner(Owner owner) {
        this.owner = owner;
    }
}

```

**Expected behavior**
Application should run 

**Actual behavior**
Application is frozen. This is the output of jstack: 
```java
"main" #1 prio=5 os_prio=0 tid=0x00007f65d400d800 nid=0x24228 waiting on condition [0x00007f65db60a000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x0000000726b33bc0> (a java.util.concurrent.CompletableFuture$Signaller)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.CompletableFuture$Signaller.block(CompletableFuture.java:1707)
	at java.util.concurrent.ForkJoinPool.managedBlock(ForkJoinPool.java:3323)
	at java.util.concurrent.CompletableFuture.waitingGet(CompletableFuture.java:1742)
	at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1908)
	at io.quarkus.runner.RuntimeClassLoader.findClass(RuntimeClassLoader.java:214)
	at io.quarkus.runner.RuntimeClassLoader.loadClass(RuntimeClassLoader.java:179)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:348)
	at org.objectweb.asm.ClassWriter.getCommonSuperClass(ClassWriter.java:935)
	at org.objectweb.asm.SymbolTable.addMergedType(SymbolTable.java:1200)
	at org.objectweb.asm.Frame.merge(Frame.java:1299)
	at org.objectweb.asm.Frame.merge(Frame.java:1197)
	at org.objectweb.asm.MethodWriter.computeAllFrames(MethodWriter.java:1607)
	at org.objectweb.asm.MethodWriter.visitMaxs(MethodWriter.java:1543)
	at org.objectweb.asm.ClassReader.readCode(ClassReader.java:2431)
	at org.objectweb.asm.ClassReader.readThis is causeMethod(ClassReader.java:1283)
	at org.objectweb.asm.ClassReader.accept(ClassReader.java:688)
	at org.objectweb.asm.ClassReader.accept(ClassReader.java:400)
	at io.quarkus.hibernate.orm.deployment.HibernateEntityEnhancer$HibernateEnhancingClassVisitor.visitEnd(HibernateEntityEnhancer.java:67)
	at org.objectweb.asm.ClassReader.accept(ClassReader.java:692)
	at org.objectweb.asm.ClassReader.accept(ClassReader.java:400)
	at io.quarkus.runner.RuntimeClassLoader.handleTransform(RuntimeClassLoader.java:430)
	at io.quarkus.runner.RuntimeClassLoader.findClass(RuntimeClassLoader.java:225)
	at io.quarkus.runner.RuntimeClassLoader.loadClass(RuntimeClassLoader.java:179)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:348)
	at io.quarkus.hibernate.orm.runtime.service.FlatClassLoaderService.classForName(FlatClassLoaderService.java:36)
	at org.hibernate.boot.model.source.internal.annotations.AnnotationMetadataSourceProcessorImpl.<init>(AnnotationMetadataSourceProcessorImpl.java:109)
	at org.hibernate.boot.model.process.spi.MetadataBuildingProcess$1.<init>(MetadataBuildingProcess.java:155)
	at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:149)
	at io.quarkus.hibernate.orm.runtime.boot.FastBootMetadataBuilder.build(FastBootMetadataBuilder.java:342)
	at io.quarkus.hibernate.orm.runtime.PersistenceUnitsHolder.createMetadata(PersistenceUnitsHolder.java:111)
	at io.quarkus.hibernate.orm.runtime.PersistenceUnitsHolder.constructMetadataAdvance(PersistenceUnitsHolder.java:84)
	at io.quarkus.hibernate.orm.runtime.PersistenceUnitsHolder.initializeJpa(PersistenceUnitsHolder.java:47)
	at io.quarkus.hibernate.orm.runtime.HibernateOrmRecorder$4.created(HibernateOrmRecorder.java:75)
	at io.quarkus.arc.runtime.ArcRecorder.initBeanContainer(ArcRecorder.java:101)
	at io.quarkus.deployment.steps.ArcProcessor$generateResources25.deploy_0(ArcProcessor$generateResources25.zig:406)
	at io.quarkus.deployment.steps.ArcProcessor$generateResources25.deploy(ArcProcessor$generateResources25.zig:36)
	at io.quarkus.runner.ApplicationImpl.<clinit>(ApplicationImpl.zig:378)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:348)
	at io.quarkus.runner.RuntimeRunner.run(RuntimeRunner.java:152)
	at io.quarkus.dev.DevModeMain.doStart(DevModeMain.java:178)
	- locked <0x00000005d3e2ea78> (a io.quarkus.dev.DevModeMain)
	at io.quarkus.dev.DevModeMain.start(DevModeMain.java:96)
	at io.quarkus.dev.DevModeMain.main(DevModeMain.java:67)
```


**To Reproduce**
Steps to reproduce the behavior:
1. Unzip the project in [jpa-example.zip](https://github.com/quarkusio/quarkus/files/3948053/jpa-example.zip)
2. run `mvn compile quarkus:dev`
3. Output should freeze after the following output is displayed: 
```
[INFO] Launching JVM with command line: /home/ggastald/.sdkman/candidates/java/8.0.232-open/jre/bin/java -XX:TieredStopAtLevel=1 -Xverify:none -Xdebug -Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=n -Djava.util.logging.manager=org.jboss.logmanager.LogManager -jar /tmp/jpa-example/target/jpa-example-dev.jar
```

**Environment (please complete the following information):**
 - Output of `uname -a` or `ver`: `Linux ggastald-laptop 5.3.14-300.fc31.x86_64 #1 SMP Mon Dec 2 15:41:35 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux`
 - Output of `java -version`: 
```
openjdk version "1.8.0_232"
OpenJDK Runtime Environment (build 1.8.0_232-b09)
OpenJDK 64-Bit Server VM (build 25.232-b09, mixed mode)

```
 - GraalVM version (if different from Java):  Not used
 - Quarkus version or git rev: 1.1.0.CR1

**Additional context**
Look at the jstack output to find where the problem is. HINT: recursion in RuntimeClassloader 



Changed files:
core/deployment/src/main/java/io/quarkus/runner/RuntimeClassLoader.java

diff --git a/core/deployment/src/main/java/io/quarkus/runner/RuntimeClassLoader.java b/core/deployment/src/main/java/io/quarkus/runner/RuntimeClassLoader.java
index c323f8ba4de..cc07cb92f26 100644
--- a/core/deployment/src/main/java/io/quarkus/runner/RuntimeClassLoader.java
+++ b/core/deployment/src/main/java/io/quarkus/runner/RuntimeClassLoader.java
@@ -32,7 +32,6 @@
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.Future;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
@@ -44,7 +43,6 @@
 import org.objectweb.asm.ClassWriter;
 
 import io.quarkus.deployment.ClassOutput;
-import io.quarkus.deployment.QuarkusClassWriter;
 
 public class RuntimeClassLoader extends ClassLoader implements ClassOutput, TransformerTarget {
 
@@ -56,6 +54,7 @@ public class RuntimeClassLoader extends ClassLoader implements ClassOutput, Tran
     private final Map<String, byte[]> resources = new ConcurrentHashMap<>();
 
     private volatile Map<String, List<BiFunction<String, ClassVisitor, ClassVisitor>>> bytecodeTransformers = null;
+    private volatile ClassLoader transformerSafeClassLoader;
 
     private final List<Path> applicationClassDirectories;
 
@@ -68,7 +67,7 @@ public class RuntimeClassLoader extends ClassLoader implements ClassOutput, Tran
 
     private static final String DEBUG_CLASSES_DIR = System.getProperty("quarkus.debug.generated-classes-dir");
 
-    private final ConcurrentHashMap<String, Future<Class<?>>> loadingClasses = new ConcurrentHashMap<>();
+    private final ConcurrentHashMap<String, LoadingClass> loadingClasses = new ConcurrentHashMap<>();
 
     static {
         registerAsParallelCapable();
@@ -207,11 +206,15 @@ protected Class<?> findClass(String name) throws ClassNotFoundException {
         Path classLoc = getClassInApplicationClassPaths(name);
 
         if (classLoc != null) {
-            CompletableFuture<Class<?>> res = new CompletableFuture<>();
-            Future<Class<?>> loadingClass = loadingClasses.putIfAbsent(name, res);
+            LoadingClass res = new LoadingClass(new CompletableFuture<>(), Thread.currentThread());
+            LoadingClass loadingClass = loadingClasses.putIfAbsent(name, res);
             if (loadingClass != null) {
+                if (loadingClass.initiator == Thread.currentThread()) {
+                    throw new LinkageError(
+                            "Load caused recursion in RuntimeClassLoader, this is a Quarkus bug loading class: " + name);
+                }
                 try {
-                    return loadingClass.get();
+                    return loadingClass.value.get();
                 } catch (Exception e) {
                     throw new ClassNotFoundException("Failed to load " + name, e);
                 }
@@ -225,13 +228,13 @@ protected Class<?> findClass(String name) throws ClassNotFoundException {
                 bytes = handleTransform(name, bytes);
                 definePackage(name);
                 Class<?> clazz = defineClass(name, bytes, 0, bytes.length, defaultProtectionDomain);
-                res.complete(clazz);
+                res.value.complete(clazz);
                 return clazz;
             } catch (RuntimeException e) {
-                res.completeExceptionally(e);
+                res.value.completeExceptionally(e);
                 throw e;
             } catch (Throwable e) {
-                res.completeExceptionally(e);
+                res.value.completeExceptionally(e);
                 throw e;
             }
         }
@@ -301,6 +304,7 @@ public Writer writeSource(final String className) {
     @Override
     public void setTransformers(Map<String, List<BiFunction<String, ClassVisitor, ClassVisitor>>> functions) {
         this.bytecodeTransformers = functions;
+        this.transformerSafeClassLoader = Thread.currentThread().getContextClassLoader();
     }
 
     public void setApplicationArchives(List<Path> archives) {
@@ -422,7 +426,12 @@ private byte[] handleTransform(String name, byte[] bytes) {
         }
 
         ClassReader cr = new ClassReader(bytes);
-        ClassWriter writer = new QuarkusClassWriter(cr, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+        ClassWriter writer = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
+            @Override
+            protected ClassLoader getClassLoader() {
+                return transformerSafeClassLoader;
+            }
+        };
         ClassVisitor visitor = writer;
         for (BiFunction<String, ClassVisitor, ClassVisitor> i : transformers) {
             visitor = i.apply(name, visitor);
@@ -537,4 +546,13 @@ private ProtectionDomain createDefaultProtectionDomain(Path applicationClasspath
         return protectionDomain;
     }
 
+    static final class LoadingClass {
+        final CompletableFuture<Class<?>> value;
+        final Thread initiator;
+
+        LoadingClass(CompletableFuture<Class<?>> value, Thread initiator) {
+            this.value = value;
+            this.initiator = initiator;
+        }
+    }
 }