Repository: quarkusio/quarkus
Issue: Potential NPE in QuarkusClassLoader

I am trying to run a Quarkus app in a local container and use [remote development mode](https://quarkus.io/guides/maven-tooling#remote-development-mode) and I'm facing the same error:
```
backend     | 2022-09-20 14:56:35,633 ERROR [io.qua.dep.dev.IsolatedDevModeMain] (vert.x-worker-thread-1) Failed to start quarkus: java.lang.RuntimeException: java.lang.NullPointerException: Cannot invoke "io.quarkus.bootstrap.classloading.ClassPathResource.getUrl()" because "res" is null
backend     | 	at io.quarkus.runner.bootstrap.AugmentActionImpl.runAugment(AugmentActionImpl.java:330)
backend     | 	at io.quarkus.runner.bootstrap.AugmentActionImpl.reloadExistingApplication(AugmentActionImpl.java:265)
backend     | 	at io.quarkus.runner.bootstrap.AugmentActionImpl.reloadExistingApplication(AugmentActionImpl.java:60)
backend     | 	at io.quarkus.deployment.dev.IsolatedDevModeMain.restartApp(IsolatedDevModeMain.java:251)
backend     | 	at io.quarkus.deployment.dev.IsolatedDevModeMain.restartCallback(IsolatedDevModeMain.java:234)
backend     | 	at io.quarkus.deployment.dev.RuntimeUpdatesProcessor.doScan(RuntimeUpdatesProcessor.java:536)
backend     | 	at io.quarkus.deployment.dev.RuntimeUpdatesProcessor.doScan(RuntimeUpdatesProcessor.java:436)
backend     | 	at io.quarkus.vertx.http.runtime.devmode.VertxHttpHotReplacementSetup$4.handle(VertxHttpHotReplacementSetup.java:152)
backend     | 	at io.quarkus.vertx.http.runtime.devmode.VertxHttpHotReplacementSetup$4.handle(VertxHttpHotReplacementSetup.java:139)
backend     | 	at io.vertx.core.impl.ContextBase.lambda$null$0(ContextBase.java:137)
backend     | 	at io.vertx.core.impl.ContextInternal.dispatch(ContextInternal.java:264)
backend     | 	at io.vertx.core.impl.ContextBase.lambda$executeBlocking$1(ContextBase.java:135)
backend     | 	at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
backend     | 	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
backend     | 	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1452)
backend     | 	at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
backend     | 	at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
backend     | 	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
backend     | 	at java.base/java.lang.Thread.run(Thread.java:833)
backend     | Caused by: java.lang.NullPointerException: Cannot invoke "io.quarkus.bootstrap.classloading.ClassPathResource.getUrl()" because "res" is null
backend     | 	at io.quarkus.bootstrap.classloading.QuarkusClassLoader.getResources(QuarkusClassLoader.java:250)
backend     | 	at io.quarkus.bootstrap.classloading.QuarkusClassLoader.getResources(QuarkusClassLoader.java:265)
backend     | 	at io.quarkus.bootstrap.classloading.QuarkusClassLoader.getResources(QuarkusClassLoader.java:197)
backend     | 	at io.smallrye.common.classloader.ClassPathUtils.consumeAsPaths(ClassPathUtils.java:84)
backend     | 	at io.smallrye.config.AbstractLocationConfigSourceLoader.tryClassPath(AbstractLocationConfigSourceLoader.java:128)
backend     | 	at io.smallrye.config.AbstractLocationConfigSourceLoader.loadConfigSources(AbstractLocationConfigSourceLoader.java:93)
backend     | 	at io.smallrye.config.AbstractLocationConfigSourceLoader.loadConfigSources(AbstractLocationConfigSourceLoader.java:76)
backend     | 	at io.quarkus.runtime.configuration.ApplicationPropertiesConfigSourceLoader$InClassPath.getConfigSources(ApplicationPropertiesConfigSourceLoader.java:30)
backend     | 	at io.quarkus.runtime.configuration.ApplicationPropertiesConfigSourceLoader$InClassPath.getConfigSources(ApplicationPropertiesConfigSourceLoader.java:27)
backend     | 	at io.smallrye.config.SmallRyeConfigBuilder.build(SmallRyeConfigBuilder.java:439)
backend     | 	at io.quarkus.deployment.ExtensionLoader.loadStepsFrom(ExtensionLoader.java:179)
backend     | 	at io.quarkus.deployment.QuarkusAugmentor.run(QuarkusAugmentor.java:105)
backend     | 	at io.quarkus.runner.bootstrap.AugmentActionImpl.runAugment(AugmentActionImpl.java:328)
backend     | 	... 18 more
```
I am not using any database migration tool. Could it be related?

_Originally posted by @y-luis in https://github.com/quarkusio/quarkus/issues/25256#issuecomment-1252562974_

Changed files:
core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java
independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/DevModeMediator.java

diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java
index 57df8f03e13..3b869b5fadf 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java
@@ -53,6 +53,7 @@
 import org.jboss.jandex.Indexer;
 import org.jboss.logging.Logger;
 
+import io.quarkus.bootstrap.runner.DevModeMediator;
 import io.quarkus.bootstrap.runner.Timing;
 import io.quarkus.changeagent.ClassChangeAgent;
 import io.quarkus.deployment.dev.DevModeContext.ModuleInfo;
@@ -602,6 +603,7 @@ public Set<String> syncState(Map<String, String> fileHashes) {
                     ret.add(i.getKey());
                 }
             }
+            List<Path> removedFiles = List.of();
             for (Map.Entry<String, String> remaining : ourHashes.entrySet()) {
                 String file = remaining.getKey();
                 if (file.endsWith("META-INF/MANIFEST.MF") || file.contains("META-INF/maven")
@@ -609,8 +611,14 @@ public Set<String> syncState(Map<String, String> fileHashes) {
                     //we have some filters, for files that we don't want to delete
                     continue;
                 }
-                log.info("Deleting removed file " + file);
-                Files.deleteIfExists(applicationRoot.resolve(file));
+                log.info("Scheduled for removal " + file);
+                if (removedFiles.isEmpty()) {
+                    removedFiles = new ArrayList<>();
+                }
+                removedFiles.add(applicationRoot.resolve(file));
+            }
+            if (!removedFiles.isEmpty()) {
+                DevModeMediator.removedFiles.addLast(removedFiles);
             }
             return ret;
         } catch (IOException e) {
diff --git a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/DevModeMediator.java b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/DevModeMediator.java
index 0cc428bf191..87d382db3c6 100644
--- a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/DevModeMediator.java
+++ b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/DevModeMediator.java
@@ -9,9 +9,11 @@
 import java.net.URLClassLoader;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.Deque;
 import java.util.List;
 import java.util.Timer;
 import java.util.TimerTask;
+import java.util.concurrent.LinkedBlockingDeque;
 
 import org.jboss.logging.Logger;
 
@@ -24,6 +26,8 @@ public class DevModeMediator {
 
     protected static final Logger LOGGER = Logger.getLogger(DevModeMediator.class);
 
+    public static final Deque<List<Path>> removedFiles = new LinkedBlockingDeque<>();
+
     static void doDevMode(Path appRoot) throws IOException, ClassNotFoundException, IllegalAccessException,
             InvocationTargetException, NoSuchMethodException {
         Path deploymentClassPath = appRoot.resolve(QuarkusEntryPoint.LIB_DEPLOYMENT_DEPLOYMENT_CLASS_PATH_DAT);
@@ -88,6 +92,13 @@ public void run() {
                         closeable.close();
                     }
                     closeable = null;
+                    final List<Path> pathsToDelete = removedFiles.pollFirst();
+                    if (pathsToDelete != null) {
+                        for (Path p : pathsToDelete) {
+                            LOGGER.info("Deleting " + p);
+                            Files.deleteIfExists(p);
+                        }
+                    }
                     try {
                         closeable = doStart(appRoot, deploymentClassPath);
                     } catch (Exception e) {