diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedDevModeMain.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedDevModeMain.java index 745e6a94317b2..25ef8e28951fd 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedDevModeMain.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedDevModeMain.java @@ -321,6 +321,8 @@ public void close() { RuntimeUpdatesProcessor.INSTANCE.close(); } catch (IOException e) { log.error("Failed to close compiler", e); + } finally { + RuntimeUpdatesProcessor.INSTANCE = null; } } for (HotReplacementSetup i : hotReplacementSetups) { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedRemoteDevModeMain.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedRemoteDevModeMain.java index 4eb1f162bbf56..b01dce10be579 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedRemoteDevModeMain.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedRemoteDevModeMain.java @@ -176,6 +176,8 @@ public void close() { RuntimeUpdatesProcessor.INSTANCE.close(); } catch (IOException e) { log.error("Failed to close compiler", e); + } finally { + RuntimeUpdatesProcessor.INSTANCE = null; } for (HotReplacementSetup i : hotReplacementSetups) { i.close(); diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedTestModeMain.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedTestModeMain.java index dce3eb0cfc3cb..e58029cd79cf2 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedTestModeMain.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedTestModeMain.java @@ -90,6 +90,8 @@ public void close() { RuntimeUpdatesProcessor.INSTANCE.close(); } catch (IOException e) { e.printStackTrace(); + } finally { + RuntimeUpdatesProcessor.INSTANCE = null; } for (HotReplacementSetup i : hotReplacementSetups) { i.close(); diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestSupport.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestSupport.java index a03138e2f7e3a..50fa7935ddc2a 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestSupport.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestSupport.java @@ -17,7 +17,9 @@ import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -322,6 +324,8 @@ public synchronized void stop() { for (var runner : moduleRunners) { runner.abort(); } + TestWatchedFiles.setWatchedFilesListener( + (BiConsumer, List, Boolean>>>) null); } public void runTests() { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/proxy/ProxyFactory.java b/core/deployment/src/main/java/io/quarkus/deployment/proxy/ProxyFactory.java index 7b379d7c1d5af..571c1fcb5232d 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/proxy/ProxyFactory.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/proxy/ProxyFactory.java @@ -79,6 +79,10 @@ public ProxyFactory(ProxyConfiguration configuration) { } } + public ClassLoader getClassLoader() { + return classLoader; + } + private boolean findConstructor(Class clazz, boolean allowPackagePrivate, boolean allowInject) { Constructor[] ctors = clazz.getDeclaredConstructors(); if (allowInject) { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/recording/RecordingProxyFactories.java b/core/deployment/src/main/java/io/quarkus/deployment/recording/RecordingProxyFactories.java index 29f39a82f0c86..73f6c009a55a3 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/recording/RecordingProxyFactories.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/recording/RecordingProxyFactories.java @@ -13,8 +13,9 @@ class RecordingProxyFactories { static void put(Class clazz, ProxyFactory proxyFactory) { RECORDING_PROXY_FACTORIES.put(clazz, proxyFactory); - if (clazz.getClassLoader() instanceof QuarkusClassLoader) { - ((QuarkusClassLoader) clazz.getClassLoader()).addCloseTask(new Runnable() { + ClassLoader proxyClassLoader = proxyFactory.getClassLoader(); + if (proxyClassLoader instanceof QuarkusClassLoader) { + ((QuarkusClassLoader) proxyClassLoader).addCloseTask(new Runnable() { @Override public void run() { RecordingProxyFactories.RECORDING_PROXY_FACTORIES.remove(clazz); diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/ClassTransformingBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/ClassTransformingBuildStep.java index a0f7c275eda8e..f9232542a3a8c 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/ClassTransformingBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/ClassTransformingBuildStep.java @@ -40,6 +40,7 @@ import io.quarkus.deployment.builditem.ApplicationArchivesBuildItem; import io.quarkus.deployment.builditem.ArchiveRootBuildItem; import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem; +import io.quarkus.deployment.builditem.CuratedApplicationShutdownBuildItem; import io.quarkus.deployment.builditem.LaunchModeBuildItem; import io.quarkus.deployment.builditem.LiveReloadBuildItem; import io.quarkus.deployment.builditem.RemovedResourceBuildItem; @@ -71,13 +72,19 @@ public static byte[] transform(String className, byte[] classData) { return lastTransformers.apply(className, classData); } + private static void reset() { + lastTransformers = null; + transformedClassesCache.clear(); + } + @BuildStep TransformedClassesBuildItem handleClassTransformation(List bytecodeTransformerBuildItems, ApplicationArchivesBuildItem appArchives, LiveReloadBuildItem liveReloadBuildItem, LaunchModeBuildItem launchModeBuildItem, ClassLoadingConfig classLoadingConfig, CurateOutcomeBuildItem curateOutcomeBuildItem, List removedResourceBuildItems, ArchiveRootBuildItem archiveRoot, LaunchModeBuildItem launchMode, PackageConfig packageConfig, - ExecutorService buildExecutor) + ExecutorService buildExecutor, + CuratedApplicationShutdownBuildItem shutdown) throws ExecutionException, InterruptedException { if (bytecodeTransformerBuildItems.isEmpty() && classLoadingConfig.removedResources.isEmpty() && removedResourceBuildItems.isEmpty()) { @@ -117,6 +124,7 @@ TransformedClassesBuildItem handleClassTransformation(List> transformed = new ConcurrentLinkedDeque<>(); final Map> transformedClassesByJar = new HashMap<>(); ClassLoader transformCl = Thread.currentThread().getContextClassLoader(); + shutdown.addCloseTask(ClassTransformingBuildStep::reset, true); lastTransformers = new BiFunction() { @Override public byte[] apply(String className, byte[] originalBytes) { diff --git a/core/devmode-spi/src/main/java/io/quarkus/dev/testing/TestWatchedFiles.java b/core/devmode-spi/src/main/java/io/quarkus/dev/testing/TestWatchedFiles.java index 2744d7f636778..134b26f1f333c 100644 --- a/core/devmode-spi/src/main/java/io/quarkus/dev/testing/TestWatchedFiles.java +++ b/core/devmode-spi/src/main/java/io/quarkus/dev/testing/TestWatchedFiles.java @@ -62,7 +62,7 @@ public synchronized static void setWatchedFilePaths(Map watched public synchronized static void setWatchedFilesListener( BiConsumer, List, Boolean>>> watchedFilesListener) { TestWatchedFiles.watchedFilesListener = watchedFilesListener; - if (watchedFilePaths != null) { + if (watchedFilesListener != null && watchedFilePaths != null) { watchedFilesListener.accept(watchedFilePaths, watchedFilePredicates); } } diff --git a/core/runtime/src/main/java/io/quarkus/runtime/dev/io/NioThreadPoolRecorder.java b/core/runtime/src/main/java/io/quarkus/runtime/dev/io/NioThreadPoolRecorder.java index c14b2829d6731..0138b5bf85585 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/dev/io/NioThreadPoolRecorder.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/dev/io/NioThreadPoolRecorder.java @@ -8,12 +8,17 @@ public class NioThreadPoolRecorder { public void updateTccl(ShutdownContext context) { - ClassLoader old = NioThreadPoolThreadFactory.updateTccl(Thread.currentThread().getContextClassLoader()); - context.addLastShutdownTask(new Runnable() { - @Override - public void run() { - NioThreadPoolThreadFactory.updateTccl(old); - } - }); + ClassLoader newTccl = Thread.currentThread().getContextClassLoader(); + ClassLoader oldTccl = NioThreadPoolThreadFactory.updateTccl(newTccl); + if (newTccl != oldTccl) { + context.addLastShutdownTask(new Runnable() { + @Override + public void run() { + NioThreadPoolThreadFactory.updateTccl(oldTccl); + } + }); + } + // Else: don't add an unnecessary shutdown task that may hold a reference to a QuarkusClassLoader, + // which could be a problem with QuarkusUnitTest since it creates one classloader per test. } } diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/menu/ConfigurationProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/menu/ConfigurationProcessor.java index 6818ff8fb5c38..2f2842e10d1b4 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/menu/ConfigurationProcessor.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/menu/ConfigurationProcessor.java @@ -24,6 +24,7 @@ import io.quarkus.deployment.annotations.ExecutionTime; import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.ConfigDescriptionBuildItem; +import io.quarkus.deployment.builditem.CuratedApplicationShutdownBuildItem; import io.quarkus.deployment.builditem.DevServicesLauncherConfigResultBuildItem; import io.quarkus.dev.config.CurrentConfig; import io.quarkus.dev.console.DevConsoleManager; @@ -95,7 +96,8 @@ void registerConfigs(List configDescriptionBuildItem void registerJsonRpcService( BuildProducer jsonRPCProvidersProducer, BuildProducer syntheticBeanProducer, - ConfigDevUIRecorder recorder) { + ConfigDevUIRecorder recorder, + CuratedApplicationShutdownBuildItem shutdown) { DevConsoleManager.register("config-update-property", map -> { Map values = Collections.singletonMap(map.get("name"), map.get("value")); @@ -116,6 +118,13 @@ void registerJsonRpcService( .done()); CurrentConfig.EDITOR = ConfigurationProcessor::updateConfig; + shutdown.addCloseTask(new Runnable() { + @Override + public void run() { + CurrentConfig.EDITOR = null; + CurrentConfig.CURRENT = Collections.emptyList(); + } + }, true); jsonRPCProvidersProducer.produce(new JsonRPCProvidersBuildItem("devui-configuration", ConfigJsonRPCService.class)); } diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java index c6066e29c3890..6b14e7198c9f3 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java @@ -229,7 +229,7 @@ public static void shutDownDevMode() { } rootHandler = null; hotReplacementHandler = null; - + hotReplacementContext = null; } public static void startServerAfterFailedStart() { diff --git a/extensions/vertx/runtime/src/main/java/io/quarkus/vertx/core/runtime/VertxCoreRecorder.java b/extensions/vertx/runtime/src/main/java/io/quarkus/vertx/core/runtime/VertxCoreRecorder.java index 5d578a4160c90..26e0cbb4ad1c9 100644 --- a/extensions/vertx/runtime/src/main/java/io/quarkus/vertx/core/runtime/VertxCoreRecorder.java +++ b/extensions/vertx/runtime/src/main/java/io/quarkus/vertx/core/runtime/VertxCoreRecorder.java @@ -423,6 +423,7 @@ public void handle(AsyncResult ar) { Thread.currentThread().interrupt(); throw new IllegalStateException("Exception when closing Vert.x instance", e); } + LateBoundMDCProvider.setMDCProviderDelegate(null); vertx = null; } } diff --git a/extensions/vertx/runtime/src/main/java/io/quarkus/vertx/runtime/VertxRecorder.java b/extensions/vertx/runtime/src/main/java/io/quarkus/vertx/runtime/VertxRecorder.java index 12dbb305919cf..bd2e203ea3dd0 100644 --- a/extensions/vertx/runtime/src/main/java/io/quarkus/vertx/runtime/VertxRecorder.java +++ b/extensions/vertx/runtime/src/main/java/io/quarkus/vertx/runtime/VertxRecorder.java @@ -77,6 +77,7 @@ public static Vertx getVertx() { void destroy() { messageConsumers = null; + vertx = null; } void registerMessageConsumers(Map messageConsumerConfigurations) {