// Path: deploy-runner-agent/src/main/java/jetbrains/buildServer/deployer/agent/SyncBuildProcessAdapter.java
// public abstract class SyncBuildProcessAdapter extends BuildProcessAdapter {
//   protected final BuildProgressLogger myLogger;
//   private volatile boolean hasFinished;
//   private volatile BuildFinishedStatus statusCode;
//   private volatile boolean isInterrupted;
// 
// 
//   public SyncBuildProcessAdapter(@NotNull final BuildProgressLogger logger) {
//     myLogger = logger;
//     hasFinished = false;
//     statusCode = null;
//   }
// 
// 
//   @Override
//   public void interrupt() {
//     isInterrupted = true;
//   }
// 
//   @Override
//   public boolean isInterrupted() {
//     return isInterrupted;
//   }
// 
//   @Override
//   public boolean isFinished() {
//     return hasFinished;
//   }
// 
//   @NotNull
//   @Override
//   public BuildFinishedStatus waitFor() throws RunBuildException {
//     while (!isInterrupted() && !hasFinished) {
//       try {
//         Thread.sleep(1000);
//       } catch (InterruptedException e) {
//         throw new RunBuildException(e);
//       }
//     }
//     return hasFinished ? statusCode : BuildFinishedStatus.INTERRUPTED;
//   }
// 
//   @Override
//   public void start() throws RunBuildException {
//     try {
//       statusCode = runProcess();
//       hasFinished = true;
//     } catch (UploadInterruptedException e) {
//       hasFinished = false;
//     }
//   }
// 
//   /**
//    * @return true is process finished successfully
//    */
//   protected abstract BuildFinishedStatus runProcess();
// 
//   protected void checkIsInterrupted() throws UploadInterruptedException {
//     if (isInterrupted()) throw new UploadInterruptedException();
//   }
// 
// }
// 
// Path: deploy-runner-agent/src/main/java/jetbrains/buildServer/deployer/agent/UploadInterruptedException.java
// public class UploadInterruptedException extends RuntimeException {
// 
// }
// 
// Path: deploy-runner-common/src/main/java/jetbrains/buildServer/deployer/common/FTPRunnerConstants.java
// public class FTPRunnerConstants {
//   public static final String PARAM_AUTH_METHOD = "jetbrains.buildServer.deployer.ftp.authMethod";
//   public static final String PARAM_TRANSFER_MODE = "jetbrains.buildServer.deployer.ftp.transferMethod";
//   public static final String TRANSFER_MODE_AUTO = "AUTO";
//   public static final String TRANSFER_MODE_BINARY = "BINARY";
//   public static final String TRANSFER_MODE_ASCII = "ASCII";
//   public static final String AUTH_METHOD_USER_PWD = "USER_PWD";
//   public static final String AUTH_METHOD_ANONYMOUS = "ANONYMOUS";
//   public static final String PARAM_SSL_MODE = "jetbrains.buildServer.deployer.ftp.securityMode";
//   public static final String DATA_CHANNEL_PROTECTION = "jetbrains.buildServer.deployer.ftp.dataChannelProtection";
//   public static final String PARAM_FTP_MODE = "jetbrains.buildServer.deployer.ftp.ftpMode";
//   public static final String PARAM_FTP_CONNECT_TIMEOUT = "jetbrains.deployer.ftp.connectTimeout";
//   public static final String PARAM_FTP_CONTROL_KEEP_ALIVE_TIMEOUT = "jetbrains.deployer.ftp.controlKeepAliveTimeout.seconds";
// }
// 
// Path: deploy-runner-agent/src/main/java/jetbrains/buildServer/deployer/agent/DeployerAgentUtils.java
// public static void logBuildProblem(BuildProgressLogger logger, String message) {
//   logger.logBuildProblem(BuildProblemData
//           .createBuildProblem(String.valueOf(message.hashCode()),
//                   DeployerRunnerConstants.BUILD_PROBLEM_TYPE,
//                   "Deployment problem: " + message));
// }


import com.intellij.openapi.diagnostic.Logger;
import jetbrains.buildServer.agent.BuildFinishedStatus;
import jetbrains.buildServer.agent.BuildRunnerContext;
import jetbrains.buildServer.agent.impl.artifacts.ArtifactsCollection;
import jetbrains.buildServer.deployer.agent.SyncBuildProcessAdapter;
import jetbrains.buildServer.deployer.agent.UploadInterruptedException;
import jetbrains.buildServer.deployer.common.FTPRunnerConstants;
import jetbrains.buildServer.util.StringUtil;
import jetbrains.buildServer.util.WaitFor;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPSClient;
import org.apache.commons.net.util.TrustManagerUtils;
import org.jetbrains.annotations.NotNull;
import javax.net.ssl.SSLException;
import java.io.IOException;
import java.net.SocketException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import static jetbrains.buildServer.deployer.agent.DeployerAgentUtils.logBuildProblem;
      new WaitFor(Long.MAX_VALUE, 1000) {
        @Override
        protected boolean condition() {
          if (uploadThread.getState() == Thread.State.TERMINATED) {
            return true;
          }
          try {
            if (isInterrupted()) {
              client.abort();
              uploadThread.join();
              return true;
            }
          } catch (Exception e) {
            throw new RuntimeException(e);
          }
          return false;
        }
      };

      if (uploadThread.getState() != Thread.State.TERMINATED) {
        LOG.warn("Ftp upload thread did not reach termination state after wait operation, trying to join");
        try {
          uploadThread.join();
        } catch (InterruptedException e) {
          LOG.warnAndDebugDetails("Interrupted while waiting for FTP upload thread to join.", e);
        }
        LOG.warn("thread joined.");
      }

      return processResult.get();
// Path: deploy-runner-agent/src/main/java/jetbrains/buildServer/deployer/agent/SyncBuildProcessAdapter.java
// public abstract class SyncBuildProcessAdapter extends BuildProcessAdapter {
//   protected final BuildProgressLogger myLogger;
//   private volatile boolean hasFinished;
//   private volatile BuildFinishedStatus statusCode;
//   private volatile boolean isInterrupted;
// 
// 
//   public SyncBuildProcessAdapter(@NotNull final BuildProgressLogger logger) {
//     myLogger = logger;
//     hasFinished = false;
//     statusCode = null;
//   }
// 
// 
//   @Override
//   public void interrupt() {
//     isInterrupted = true;
//   }
// 
//   @Override
//   public boolean isInterrupted() {
//     return isInterrupted;
//   }
// 
//   @Override
//   public boolean isFinished() {
//     return hasFinished;
//   }
// 
//   @NotNull
//   @Override
//   public BuildFinishedStatus waitFor() throws RunBuildException {
//     while (!isInterrupted() && !hasFinished) {
//       try {
//         Thread.sleep(1000);
//       } catch (InterruptedException e) {
//         throw new RunBuildException(e);
//       }
//     }
//     return hasFinished ? statusCode : BuildFinishedStatus.INTERRUPTED;
//   }
// 
//   @Override
//   public void start() throws RunBuildException {
//     try {
//       statusCode = runProcess();
//       hasFinished = true;
//     } catch (UploadInterruptedException e) {
//       hasFinished = false;
//     }
//   }
// 
//   /**
//    * @return true is process finished successfully
//    */
//   protected abstract BuildFinishedStatus runProcess();
// 
//   protected void checkIsInterrupted() throws UploadInterruptedException {
//     if (isInterrupted()) throw new UploadInterruptedException();
//   }
// 
// }
// 
// Path: deploy-runner-agent/src/main/java/jetbrains/buildServer/deployer/agent/UploadInterruptedException.java
// public class UploadInterruptedException extends RuntimeException {
// 
// }
// 
// Path: deploy-runner-common/src/main/java/jetbrains/buildServer/deployer/common/FTPRunnerConstants.java
// public class FTPRunnerConstants {
//   public static final String PARAM_AUTH_METHOD = "jetbrains.buildServer.deployer.ftp.authMethod";
//   public static final String PARAM_TRANSFER_MODE = "jetbrains.buildServer.deployer.ftp.transferMethod";
//   public static final String TRANSFER_MODE_AUTO = "AUTO";
//   public static final String TRANSFER_MODE_BINARY = "BINARY";
//   public static final String TRANSFER_MODE_ASCII = "ASCII";
//   public static final String AUTH_METHOD_USER_PWD = "USER_PWD";
//   public static final String AUTH_METHOD_ANONYMOUS = "ANONYMOUS";
//   public static final String PARAM_SSL_MODE = "jetbrains.buildServer.deployer.ftp.securityMode";
//   public static final String DATA_CHANNEL_PROTECTION = "jetbrains.buildServer.deployer.ftp.dataChannelProtection";
//   public static final String PARAM_FTP_MODE = "jetbrains.buildServer.deployer.ftp.ftpMode";
//   public static final String PARAM_FTP_CONNECT_TIMEOUT = "jetbrains.deployer.ftp.connectTimeout";
//   public static final String PARAM_FTP_CONTROL_KEEP_ALIVE_TIMEOUT = "jetbrains.deployer.ftp.controlKeepAliveTimeout.seconds";
// }
// 
// Path: deploy-runner-agent/src/main/java/jetbrains/buildServer/deployer/agent/DeployerAgentUtils.java
// public static void logBuildProblem(BuildProgressLogger logger, String message) {
//   logger.logBuildProblem(BuildProblemData
//           .createBuildProblem(String.valueOf(message.hashCode()),
//                   DeployerRunnerConstants.BUILD_PROBLEM_TYPE,
//                   "Deployment problem: " + message));
// }


// Path: deploy-runner-agent/src/main/java/jetbrains/buildServer/deployer/agent/ftp/FtpBuildProcessAdapter.java
import com.intellij.openapi.diagnostic.Logger;
import jetbrains.buildServer.agent.BuildFinishedStatus;
import jetbrains.buildServer.agent.BuildRunnerContext;
import jetbrains.buildServer.agent.impl.artifacts.ArtifactsCollection;
import jetbrains.buildServer.deployer.agent.SyncBuildProcessAdapter;
import jetbrains.buildServer.deployer.agent.UploadInterruptedException;
import jetbrains.buildServer.deployer.common.FTPRunnerConstants;
import jetbrains.buildServer.util.StringUtil;
import jetbrains.buildServer.util.WaitFor;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPSClient;
import org.apache.commons.net.util.TrustManagerUtils;
import org.jetbrains.annotations.NotNull;
import javax.net.ssl.SSLException;
import java.io.IOException;
import java.net.SocketException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import static jetbrains.buildServer.deployer.agent.DeployerAgentUtils.logBuildProblem;

      new WaitFor(Long.MAX_VALUE, 1000) {
        @Override
        protected boolean condition() {
          if (uploadThread.getState() == Thread.State.TERMINATED) {
            return true;
          }
          try {
            if (isInterrupted()) {
              client.abort();
              uploadThread.join();
              return true;
            }
          } catch (Exception e) {
            throw new RuntimeException(e);
          }
          return false;
        }
      };

      if (uploadThread.getState() != Thread.State.TERMINATED) {
        LOG.warn("Ftp upload thread did not reach termination state after wait operation, trying to join");
        try {
          uploadThread.join();
        } catch (InterruptedException e) {
          LOG.warnAndDebugDetails("Interrupted while waiting for FTP upload thread to join.", e);
        }
        LOG.warn("thread joined.");
      }

      return processResult.get();
    } catch (UploadInterruptedException e) {