void (@NotNull Deployment deployment, final @Nullable DeploymentRuntime runtime) { String deploymentName = deployment.getName(); final MyDeployments.UndeployTransition undeployInProgress = myAllDeployments.startUndeploy(deploymentName); myEventDispatcher.queueDeploymentsChanged(this); final List<LoggingHandlerImpl> handlers = myPerProjectLogManagers.values().stream() .map(nextForProject -> nextForProject.findManager(deployment)) .filter(Objects::nonNull) .map(DeploymentLogManagerImpl::getMainLoggingHandler) .toList(); final Consumer<String> logConsumer = message -> { if (handlers.isEmpty()) { LOG.info(message); } else { handlers.forEach(h -> h.printlnSystemMessage(message)); } }; logConsumer.consume("Undeploying '" + deploymentName + "'..."); DeploymentRuntime.UndeploymentTaskCallback undeploymentTaskCallback = new DeploymentRuntime.UndeploymentTaskCallback() { @Override public void succeeded() { logConsumer.consume("'" + deploymentName + "' has been undeployed successfully."); Set<String> namesToDispose = new LinkedHashSet<>(); namesToDispose.add(deploymentName); if (undeployInProgress != null) { undeployInProgress.succeeded(); undeployInProgress.getSubDeployments().forEach(deployment -> namesToDispose.add(deployment.getName())); } namesToDispose.forEach(name -> disposeAllLogs(name)); myEventDispatcher.queueDeploymentsChanged(ServerConnectionImpl.this); computeDeployments(myRuntimeInstance, EmptyRunnable.INSTANCE); } @Override public void errorOccurred(@NotNull String errorMessage) { logConsumer.consume("Failed to undeploy '" + deploymentName + "': " + errorMessage); if (undeployInProgress != null) { undeployInProgress.failed(); } myEventDispatcher.queueDeploymentsChanged(ServerConnectionImpl.this); } }; if (runtime == null) { undeploymentTaskCallback.succeeded(); } else { runtime.undeploy(undeploymentTaskCallback); } }