@Override
  public void undeploy(@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);
    }
  }
@Override
  public void undeploy(@NotNull Deployment deployment, final @Nullable DeploymentRuntime runtime)