Repository: quarkusio/quarkus
Issue: Can't run devmode with application which uses a ServletBuildItem

Happens on current master (ca11b856). To reproduce, simply add an extension that uses a `ServletBuildItem`, for example `smallrye-metrics`.

Here a list of URL mappings is wrapped into unmodifiable list https://github.com/quarkusio/quarkus/blob/ca11b856b1dc82ae3ee5e33199ed116ebdbf2700/extensions/undertow/spi/src/main/java/io/quarkus/undertow/deployment/ServletBuildItem.java#L29 but that turns it into `java.util.Collections$UnmodifiableRandomAccessList`
which doesn't have a default constructor, and because there is a call to `io.quarkus.resteasy.runtime.ExceptionMapperRecorder` (https://github.com/quarkusio/quarkus/blob/ca11b856b1dc82ae3ee5e33199ed116ebdbf2700/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/ExceptionMapperRecorder.java#L16) with the list within its argument, and the `BytecodeRecorderImpl` does not know how to construct it if it is within a different collection, the recording breaks.

Full error
```
09:54:05,587 ERROR [io.qua.dev.DevModeMain] Failed to start quarkus: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
	[error]: Build step io.quarkus.deployment.steps.MainClassBuildStep#build threw an exception: java.lang.RuntimeException: Unable to serialize objects of type class java.util.Collections$UnmodifiableRandomAccessList to bytecode as it has no default constructor
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$53.createValue(BytecodeRecorderImpl.java:1241)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$DeferredArrayStoreParameter$1.write(BytecodeRecorderImpl.java:1548)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$FixedMethodContext.writeInstruction(BytecodeRecorderImpl.java:1646)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$SplitMethodContext.writeInstruction(BytecodeRecorderImpl.java:1603)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$DeferredArrayStoreParameter.doPrepare(BytecodeRecorderImpl.java:1545)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$DeferredParameter.prepare(BytecodeRecorderImpl.java:1524)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$54.doPrepare(BytecodeRecorderImpl.java:1257)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$DeferredParameter.prepare(BytecodeRecorderImpl.java:1524)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$48.prepare(BytecodeRecorderImpl.java:1025)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$54.doPrepare(BytecodeRecorderImpl.java:1260)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$DeferredParameter.prepare(BytecodeRecorderImpl.java:1524)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl.writeBytecode(BytecodeRecorderImpl.java:394)
	at io.quarkus.deployment.steps.MainClassBuildStep.build(MainClassBuildStep.java:103)
	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 io.quarkus.deployment.ExtensionLoader$1.execute(ExtensionLoader.java:840)
	at io.quarkus.builder.BuildContext.run(BuildContext.java:415)
	at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
	at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2011)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1535)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1426)
	at java.lang.Thread.run(Thread.java:748)
	at org.jboss.threads.JBossThread.run(JBossThread.java:479)

	at io.quarkus.runner.RuntimeRunner.run(RuntimeRunner.java:149)
	at io.quarkus.dev.DevModeMain.doStart(DevModeMain.java:180)
	at io.quarkus.dev.DevModeMain.start(DevModeMain.java:94)
	at io.quarkus.dev.DevModeMain.main(DevModeMain.java:66)
Caused by: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
	[error]: Build step io.quarkus.deployment.steps.MainClassBuildStep#build threw an exception: java.lang.RuntimeException: Unable to serialize objects of type class java.util.Collections$UnmodifiableRandomAccessList to bytecode as it has no default constructor
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$53.createValue(BytecodeRecorderImpl.java:1241)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$DeferredArrayStoreParameter$1.write(BytecodeRecorderImpl.java:1548)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$FixedMethodContext.writeInstruction(BytecodeRecorderImpl.java:1646)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$SplitMethodContext.writeInstruction(BytecodeRecorderImpl.java:1603)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$DeferredArrayStoreParameter.doPrepare(BytecodeRecorderImpl.java:1545)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$DeferredParameter.prepare(BytecodeRecorderImpl.java:1524)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$54.doPrepare(BytecodeRecorderImpl.java:1257)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$DeferredParameter.prepare(BytecodeRecorderImpl.java:1524)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$48.prepare(BytecodeRecorderImpl.java:1025)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$54.doPrepare(BytecodeRecorderImpl.java:1260)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$DeferredParameter.prepare(BytecodeRecorderImpl.java:1524)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl.writeBytecode(BytecodeRecorderImpl.java:394)
	at io.quarkus.deployment.steps.MainClassBuildStep.build(MainClassBuildStep.java:103)
	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 io.quarkus.deployment.ExtensionLoader$1.execute(ExtensionLoader.java:840)
	at io.quarkus.builder.BuildContext.run(BuildContext.java:415)
	at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
	at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2011)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1535)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1426)
	at java.lang.Thread.run(Thread.java:748)
	at org.jboss.threads.JBossThread.run(JBossThread.java:479)

	at io.quarkus.builder.Execution.run(Execution.java:108)
	at io.quarkus.builder.BuildExecutionBuilder.execute(BuildExecutionBuilder.java:121)
	at io.quarkus.deployment.QuarkusAugmentor.run(QuarkusAugmentor.java:115)
	at io.quarkus.runner.RuntimeRunner.run(RuntimeRunner.java:110)
	... 3 more
Caused by: java.lang.RuntimeException: Unable to serialize objects of type class java.util.Collections$UnmodifiableRandomAccessList to bytecode as it has no default constructor
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$53.createValue(BytecodeRecorderImpl.java:1241)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$DeferredArrayStoreParameter$1.write(BytecodeRecorderImpl.java:1548)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$FixedMethodContext.writeInstruction(BytecodeRecorderImpl.java:1646)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$SplitMethodContext.writeInstruction(BytecodeRecorderImpl.java:1603)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$DeferredArrayStoreParameter.doPrepare(BytecodeRecorderImpl.java:1545)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$DeferredParameter.prepare(BytecodeRecorderImpl.java:1524)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$54.doPrepare(BytecodeRecorderImpl.java:1257)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$DeferredParameter.prepare(BytecodeRecorderImpl.java:1524)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$48.prepare(BytecodeRecorderImpl.java:1025)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$54.doPrepare(BytecodeRecorderImpl.java:1260)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl$DeferredParameter.prepare(BytecodeRecorderImpl.java:1524)
	at io.quarkus.deployment.recording.BytecodeRecorderImpl.writeBytecode(BytecodeRecorderImpl.java:394)
	at io.quarkus.deployment.steps.MainClassBuildStep.build(MainClassBuildStep.java:103)
	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 io.quarkus.deployment.ExtensionLoader$1.execute(ExtensionLoader.java:840)
	at io.quarkus.builder.BuildContext.run(BuildContext.java:415)
	at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
	at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2011)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1535)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1426)
	at java.lang.Thread.run(Thread.java:748)
	at org.jboss.threads.JBossThread.run(JBossThread.java:479)
```

Changed files:
core/deployment/src/test/java/io/quarkus/deployment/recording/BytecodeRecorderTestCase.java
core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java

diff --git a/core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java b/core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java
index c5ec1b6d1df..a46ffa5f482 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java
@@ -21,6 +21,7 @@
 import java.util.HashSet;
 import java.util.IdentityHashMap;
 import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Map;
@@ -29,6 +30,8 @@
 import java.util.Set;
 import java.util.SortedMap;
 import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Function;
 import java.util.stream.Collectors;
@@ -1231,12 +1234,16 @@ ResultHandle createValue(MethodContext context, MethodCreator method, ResultHand
                         out = method.newInstance(ofConstructor(param.getClass()));
                     } catch (NoSuchMethodException e) {
                         //fallback for collection types, such as unmodifiableMap
-                        if (expectedType == Map.class) {
+                        if (SortedMap.class.isAssignableFrom(expectedType)) {
+                            out = method.newInstance(ofConstructor(TreeMap.class));
+                        } else if (Map.class.isAssignableFrom(expectedType)) {
                             out = method.newInstance(ofConstructor(LinkedHashMap.class));
-                        } else if (expectedType == List.class) {
+                        } else if (List.class.isAssignableFrom(expectedType)) {
                             out = method.newInstance(ofConstructor(ArrayList.class));
-                        } else if (expectedType == Set.class) {
-                            out = method.newInstance(ofConstructor(Set.class));
+                        } else if (SortedSet.class.isAssignableFrom(expectedType)) {
+                            out = method.newInstance(ofConstructor(TreeSet.class));
+                        } else if (Set.class.isAssignableFrom(expectedType)) {
+                            out = method.newInstance(ofConstructor(LinkedHashSet.class));
                         } else {
                             throw new RuntimeException("Unable to serialize objects of type " + param.getClass()
                                     + " to bytecode as it has no default constructor");
diff --git a/core/deployment/src/test/java/io/quarkus/deployment/recording/BytecodeRecorderTestCase.java b/core/deployment/src/test/java/io/quarkus/deployment/recording/BytecodeRecorderTestCase.java
index 9a1d3a1ddc0..d09e14a6c07 100644
--- a/core/deployment/src/test/java/io/quarkus/deployment/recording/BytecodeRecorderTestCase.java
+++ b/core/deployment/src/test/java/io/quarkus/deployment/recording/BytecodeRecorderTestCase.java
@@ -15,7 +15,9 @@
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
+import java.util.Set;
 import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.function.Consumer;
@@ -143,6 +145,40 @@ public void testLargeCollection() throws Exception {
         }, beans);
     }
 
+    @Test
+    public void testUnmodifiableMapWithinAMap() throws Exception {
+        Map<Integer, Map<Integer, TestJavaBean>> outerMap = new HashMap<>();
+        outerMap.put(1, Collections.unmodifiableMap(
+                Collections.singletonMap(1, new TestJavaBean())));
+
+        runTest(generator -> {
+            TestRecorder recorder = generator.getRecordingProxy(TestRecorder.class);
+            recorder.map(outerMap);
+        }, outerMap);
+    }
+
+    @Test
+    public void testUnmodifiableListWithinAMap() throws Exception {
+        Map<Integer, List<TestJavaBean>> map = new HashMap<>();
+        map.put(1, Collections.unmodifiableList(Collections.singletonList(new TestJavaBean())));
+
+        runTest(generator -> {
+            TestRecorder recorder = generator.getRecordingProxy(TestRecorder.class);
+            recorder.map(map);
+        }, map);
+    }
+
+    @Test
+    public void testUnmodifiableSetWithinAMap() throws Exception {
+        Map<Integer, Set<TestJavaBean>> map = new HashMap<>();
+        map.put(1, Collections.unmodifiableSet(Collections.singleton(new TestJavaBean())));
+
+        runTest(generator -> {
+            TestRecorder recorder = generator.getRecordingProxy(TestRecorder.class);
+            recorder.map(map);
+        }, map);
+    }
+
     @Test
     public void testLargeArray() throws Exception {