From c06f61b209555bec51b66ee2055d5ae79b30bd66 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Ko=C5=BEusznik?= <jan@kozusznik.cz>
Date: Wed, 7 Nov 2018 09:26:55 +0100
Subject: [PATCH] ISS-1323: improve proper handling of upload/download

introduce isUploading status,
report cause of upload/download fail
---
 .../src/main/java/cz/it4i/fiji/haas/Job.java  |  20 +++-
 .../PersistentSynchronizationProcess.java     |  25 +++--
 .../haas/data_transfer/Synchronization.java   |  16 ++-
 .../haas_java_client/HaaSFileTransferImp.java |  23 ++--
 .../core/BenchmarkJobManager.java             |  22 ++--
 .../core/ObservableBenchmarkJob.java          |  21 +++-
 .../ui/BenchmarkSPIMControl.java              |   3 +
 .../fiji/scpclient/AckowledgementChecker.java |  46 ++++++++
 .../cz/it4i/fiji/scpclient/ScpClient.java     | 106 ++++++++----------
 9 files changed, 178 insertions(+), 104 deletions(-)
 create mode 100644 java-scpclient/src/main/java/cz/it4i/fiji/scpclient/AckowledgementChecker.java

diff --git a/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/Job.java b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/Job.java
index b3fd9cbc..db319848 100644
--- a/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/Job.java
+++ b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/Job.java
@@ -193,6 +193,10 @@ public class Job {
 		return getSafeBoolean(getProperty(JOB_IS_UPLOADED));
 	}
 
+	public boolean isUploading() {
+		return synchronization.isUploading();
+	}
+
 	public boolean isDownloaded() {
 		return getSafeBoolean(getProperty(JOB_IS_DOWNLOADED));
 	}
@@ -201,6 +205,10 @@ public class Job {
 		return Boolean.parseBoolean(getProperty(JOB_NEEDS_DOWNLOAD));
 	}
 
+	public boolean isDownloading() {
+		return synchronization.isDownloading();
+	}
+
 	public boolean needsUpload() {
 		return Boolean.parseBoolean(getProperty(JOB_NEEDS_UPLOAD));
 	}
@@ -414,6 +422,12 @@ public class Job {
 		return synchronization.getFileTransferInfo();
 	}
 
+	public void createEmptyFile(String fileName) throws InterruptedIOException {
+		try(HaaSFileTransfer transfer = haasClientSupplier.get().startFileTransfer(getId())) {
+			transfer.upload(new UploadingFileData(fileName));
+		}
+	}
+
 	private void storeInputOutputDirectory() {
 		if (inputDirectory == null) {
 			useDemoData = true;
@@ -502,10 +516,4 @@ public class Job {
 		setProperty(JOB_CAN_BE_DOWNLOADED, b);
 	}
 
-	public void createEmptyFile(String fileName) throws InterruptedIOException {
-		try(HaaSFileTransfer transfer = haasClientSupplier.get().startFileTransfer(getId())) {
-			transfer.upload(new UploadingFileData(fileName));
-		}
-	}
-
 }
diff --git a/haas-java-client/src/main/java/cz/it4i/fiji/haas/data_transfer/PersistentSynchronizationProcess.java b/haas-java-client/src/main/java/cz/it4i/fiji/haas/data_transfer/PersistentSynchronizationProcess.java
index 865c4a03..0a4b8eef 100644
--- a/haas-java-client/src/main/java/cz/it4i/fiji/haas/data_transfer/PersistentSynchronizationProcess.java
+++ b/haas-java-client/src/main/java/cz/it4i/fiji/haas/data_transfer/PersistentSynchronizationProcess.java
@@ -30,9 +30,10 @@ import cz.it4i.fiji.haas_java_client.TransferFileProgressForHaaSClient;
 
 public abstract class PersistentSynchronizationProcess<T> {
 
-	private boolean startFinished = true;
+	
+	public static final String FAILED_ITEM = "Failed item"; 
 
-	public static final Logger log = LoggerFactory
+	private static final Logger log = LoggerFactory
 			.getLogger(cz.it4i.fiji.haas.data_transfer.PersistentSynchronizationProcess.class);
 
 	private static final TransferFileProgressForHaaSClient DUMMY_FILE_PROGRESS = new TransferFileProgressForHaaSClient(
@@ -56,6 +57,8 @@ public abstract class PersistentSynchronizationProcess<T> {
 
 	private ProgressNotifier notifier;
 
+	private boolean startFinished = true;
+	
 	private final AtomicInteger runningProcessCounter = new AtomicInteger();
 	
 	private final Collection<P_HolderOfOpenClosables> openedClosables = new LinkedList<>();
@@ -114,6 +117,10 @@ public abstract class PersistentSynchronizationProcess<T> {
 		this.notifier = notifier;
 	}
 
+	public synchronized boolean isWorking() {
+		return !toProcessQueue.isEmpty();
+	}
+
 	abstract protected Iterable<T> getItems() throws IOException;
 
 	abstract protected void processItem(HaaSFileTransfer tr, T p) throws InterruptedIOException;
@@ -151,15 +158,19 @@ public abstract class PersistentSynchronizationProcess<T> {
 				try {
 					processItem(tr, p);
 					fileTransfered(p);
+					actualnotifier.itemDone(item);
 				}
 				catch (InterruptedIOException | HaaSClientException e) {
-					if (e instanceof HaaSClientException) {
-						log.warn("process ", e);
+					synchronized (this) {
+						toProcessQueue.clear();
+						interrupted = true;
+						if (e instanceof HaaSClientException) {
+							log.warn("process ", e);
+							actualnotifier.addItem(FAILED_ITEM);
+						}
 					}
-					toProcessQueue.clear();
-					interrupted = true;
 				}
-				actualnotifier.itemDone(item);
+				
 			} while(true);
 		} finally {
 			runningTransferThreads.remove(Thread.currentThread());
diff --git a/haas-java-client/src/main/java/cz/it4i/fiji/haas/data_transfer/Synchronization.java b/haas-java-client/src/main/java/cz/it4i/fiji/haas/data_transfer/Synchronization.java
index e0d7f1db..04b9e2ae 100644
--- a/haas-java-client/src/main/java/cz/it4i/fiji/haas/data_transfer/Synchronization.java
+++ b/haas-java-client/src/main/java/cz/it4i/fiji/haas/data_transfer/Synchronization.java
@@ -32,12 +32,16 @@ import cz.it4i.fiji.haas_java_client.UploadingFileImpl;
 
 public class Synchronization implements Closeable {
 
-	public static final Logger log = LoggerFactory.getLogger(cz.it4i.fiji.haas.data_transfer.Synchronization.class);
-
+	private final static Logger log = LoggerFactory.getLogger(
+		cz.it4i.fiji.haas.data_transfer.Synchronization.class);
+	
 	private static final String FILE_INDEX_TO_UPLOAD_FILENAME = ".toUploadFiles";
+	
 	private static final String FILE_INDEX_UPLOADED_FILENAME = ".uploaded";
+	
 	private static final String FILE_INDEX_TO_DOWNLOAD_FILENAME =
 		".toDownloadFiles";
+	
 	private static final String FILE_INDEX_DOWNLOADED_FILENAME = ".downloaded";
 
 	private final Path workingDirectory;
@@ -97,6 +101,10 @@ public class Synchronization implements Closeable {
 		uploadProcess.resume();
 	}
 
+	public boolean isUploading() {
+		return uploadProcess.isWorking();
+	}
+
 	public synchronized CompletableFuture<?> startDownload(Collection<String> files) throws IOException {
 		this.downloadProcess.setItems(files);
 		return this.downloadProcess.start();
@@ -110,6 +118,10 @@ public class Synchronization implements Closeable {
 		this.downloadProcess.resume();
 	}
 
+	public boolean isDownloading() {
+		return downloadProcess.isWorking();
+	}
+
 	public List<FileTransferInfo> getFileTransferInfo() {
 		final List<FileTransferInfo> list = new LinkedList<>();
 		filesUploaded.getIndexedItems().forEach(ii -> {
diff --git a/haas-java-client/src/main/java/cz/it4i/fiji/haas_java_client/HaaSFileTransferImp.java b/haas-java-client/src/main/java/cz/it4i/fiji/haas_java_client/HaaSFileTransferImp.java
index b27c73ec..2695e613 100644
--- a/haas-java-client/src/main/java/cz/it4i/fiji/haas_java_client/HaaSFileTransferImp.java
+++ b/haas-java-client/src/main/java/cz/it4i/fiji/haas_java_client/HaaSFileTransferImp.java
@@ -43,18 +43,13 @@ class HaaSFileTransferImp implements HaaSFileTransfer {
 	public void upload(final UploadingFile file) throws InterruptedIOException {
 		final String destFile = ft.getSharedBasepath() + "/" + file.getName();
 		try (InputStream is = file.getInputStream()) {
-			if (!scpClient.upload(is, destFile, file.getLength(), file.getLastTime(),
-				progress))
-			{
-				throw new HaaSClientException("Uploading of " + file + " to " +
-					destFile + " failed");
-			}
-		}
-		catch (final InterruptedIOException e) {
-			throw e;
+			scpClient.upload(is, destFile, file.getLength(), file.getLastTime(),
+				progress);
+			
 		}
 		catch (JSchException | IOException e) {
-			throw new HaaSClientException(e);
+			throw new HaaSClientException("An upload of " + file + " to " + destFile +
+				" failed: " + e.getMessage(), e);
 		}
 	}
 
@@ -67,16 +62,14 @@ class HaaSFileTransferImp implements HaaSFileTransfer {
 			final Path rFile = workDirectory.resolve(fileName);
 			final String fileToDownload = "'" + ft.getSharedBasepath() + "/" +
 				fileName + "'";
-			if (!scpClient.download(fileToDownload, rFile, progress)) {
-				throw new HaaSClientException("Downloading of " + fileName + " to " +
-					workDirectory + " failed");
-			}
+			scpClient.download(fileToDownload, rFile, progress);
 		}
 		catch (final InterruptedIOException e) {
 			throw e;
 		}
 		catch (JSchException | IOException e) {
-			throw new HaaSClientException(e);
+			throw new HaaSClientException("A download of " + fileName + " to " +
+				workDirectory + " failed: " + e.getMessage());
 		}
 	}
 
diff --git a/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/core/BenchmarkJobManager.java b/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/core/BenchmarkJobManager.java
index 3026d22a..4e5e3058 100644
--- a/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/core/BenchmarkJobManager.java
+++ b/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/core/BenchmarkJobManager.java
@@ -58,7 +58,9 @@ import org.xml.sax.SAXException;
 import cz.it4i.fiji.commons.WebRoutines;
 import cz.it4i.fiji.haas.Job;
 import cz.it4i.fiji.haas.JobManager;
+import cz.it4i.fiji.haas.data_transfer.PersistentSynchronizationProcess;
 import cz.it4i.fiji.haas_java_client.FileTransferInfo;
+import cz.it4i.fiji.haas_java_client.HaaSClientException;
 import cz.it4i.fiji.haas_java_client.HaaSClientSettings;
 import cz.it4i.fiji.haas_java_client.JobSettings;
 import cz.it4i.fiji.haas_java_client.JobSettingsBuilder;
@@ -205,8 +207,8 @@ public class BenchmarkJobManager implements Closeable {
 			job.stopUploadData();
 		}
 
-		public boolean needsUpload() {
-			return job.needsUpload();
+		public boolean isUploading() {
+			return job.isUploading();
 		}
 
 		public void setUploadNotifier(Progress progress) {
@@ -237,10 +239,6 @@ public class BenchmarkJobManager implements Closeable {
 			job.stopDownloadData();
 		}
 
-		public boolean needsDownload() {
-			return downloadingStatus.needsDownload();
-		}
-
 		public void setDownloadNotifier(Progress progress) {
 			job.setDownloadNotifier(downloadNotifier =
 				createDownloadNotifierProcessingResultCSV(convertToProgressNotifier(
@@ -259,6 +257,10 @@ public class BenchmarkJobManager implements Closeable {
 			return downloadingStatus.isDownloaded();
 		}
 
+		public boolean isDownloading() {
+			return job.isDownloading();
+		}
+
 		public void resumeTransfer() {
 			job.resumeDownload();
 			job.resumeUpload();
@@ -427,14 +429,16 @@ public class BenchmarkJobManager implements Closeable {
 				}).whenComplete((X, e) -> {
 					if (e != null) {
 						log.error(e.getMessage(), e);
+						downloadNotifier.addItem(
+							PersistentSynchronizationProcess.FAILED_ITEM);
 					}
 					result.complete(null);
 				});
 		}
 
-		private Set<String> extractNames(Path resolve) {
+		private Set<String> extractNames(Path pathToXML) {
 			Set<String> result = new HashSet<>();
-			try (InputStream fileIS = Files.newInputStream(resolve)) {
+			try (InputStream fileIS = Files.newInputStream(pathToXML)) {
 				DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
 				DocumentBuilder builder = builderFactory.newDocumentBuilder();
 				Document xmlDocument = builder.parse(fileIS);
@@ -448,7 +452,7 @@ public class BenchmarkJobManager implements Closeable {
 					result.add(nl.item(i).getTextContent());
 				}
 			} catch (IOException | ParserConfigurationException | SAXException | XPathExpressionException e) {
-				log.error(e.getMessage(), e);
+				throw new HaaSClientException("Extract names from " + pathToXML, e);
 			}
 			return result;
 		}
diff --git a/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/core/ObservableBenchmarkJob.java b/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/core/ObservableBenchmarkJob.java
index 82f7426a..4918d7fd 100644
--- a/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/core/ObservableBenchmarkJob.java
+++ b/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/core/ObservableBenchmarkJob.java
@@ -2,6 +2,7 @@
 package cz.it4i.fiji.haas_spim_benchmark.core;
 
 import java.io.Closeable;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -12,6 +13,7 @@ import net.imagej.updater.util.Progress;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import cz.it4i.fiji.haas.data_transfer.PersistentSynchronizationProcess;
 import cz.it4i.fiji.haas.ui.UpdatableObservableValue;
 import cz.it4i.fiji.haas_java_client.FileTransferInfo;
 import cz.it4i.fiji.haas_java_client.SynchronizableFileType;
@@ -26,10 +28,10 @@ public class ObservableBenchmarkJob extends
 
 	private final P_TransferProgress downloadProgress = new P_TransferProgress(
 		val -> getValue().setDownloaded(val), () -> getValue().isDownloaded(),
-		() -> getValue().needsDownload());
+		() -> getValue().isDownloading());
 	private final P_TransferProgress uploadProgress = new P_TransferProgress(
 		val -> getValue().setUploaded(val), () -> getValue().isUploaded(),
-		() -> getValue().needsUpload());
+		() -> getValue().isUploading());
 	private final Executor executor;
 
 	private final HaasOutputObservableValueRegistry haasOutputRegistry;
@@ -45,6 +47,8 @@ public class ObservableBenchmarkJob extends
 		public boolean isDone();
 
 		public boolean isWorking();
+		
+		public boolean isFailed();
 
 		public Float getRemainingPercents();
 	}
@@ -115,6 +119,7 @@ public class ObservableBenchmarkJob extends
 		private Long start;
 		private Long remainingMiliseconds;
 		private Float remainingPercents;
+		private boolean failed = false;
 
 		public P_TransferProgress(Consumer<Boolean> doneStatusConsumer,
 			Supplier<Boolean> doneStatusSupplier, Supplier<Boolean> workingSupplier)
@@ -140,7 +145,11 @@ public class ObservableBenchmarkJob extends
 
 		@Override
 		public synchronized void addItem(Object item) {
-			if (start == null) {
+			if (Objects.equals(item, PersistentSynchronizationProcess.FAILED_ITEM)) {
+				failed = true;
+				doneStatusConsumer.accept(false);
+				fireValueChangedEvent();
+			} else if (start == null) {
 				setDone(false);
 				clearProgress();
 				start = System.currentTimeMillis();
@@ -160,6 +169,11 @@ public class ObservableBenchmarkJob extends
 		public synchronized boolean isWorking() {
 			return workingSupplier.get();
 		}
+		
+		@Override
+		public boolean isFailed() {
+			return failed ;
+		}
 
 		@Override
 		public synchronized Long getRemainingMiliseconds() {
@@ -193,6 +207,7 @@ public class ObservableBenchmarkJob extends
 		}
 
 		private void setDone(boolean val) {
+			failed = false;
 			doneStatusConsumer.accept(val);
 		}
 	}
diff --git a/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/ui/BenchmarkSPIMControl.java b/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/ui/BenchmarkSPIMControl.java
index a691722a..b94cdf5d 100644
--- a/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/ui/BenchmarkSPIMControl.java
+++ b/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/ui/BenchmarkSPIMControl.java
@@ -368,6 +368,9 @@ public class BenchmarkSPIMControl extends BorderPane implements
 	}
 
 	private String decorateTransfer(TransferProgress progress) {
+		if (progress.isFailed()) {
+			return "Failed";
+		}
 		if (!progress.isWorking() && !progress.isDone()) {
 			return "";
 		}
diff --git a/java-scpclient/src/main/java/cz/it4i/fiji/scpclient/AckowledgementChecker.java b/java-scpclient/src/main/java/cz/it4i/fiji/scpclient/AckowledgementChecker.java
new file mode 100644
index 00000000..130f800b
--- /dev/null
+++ b/java-scpclient/src/main/java/cz/it4i/fiji/scpclient/AckowledgementChecker.java
@@ -0,0 +1,46 @@
+package cz.it4i.fiji.scpclient;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class AckowledgementChecker {
+	
+	private int lastStatus;
+	
+	private StringBuilder lastMessage;
+	
+	public boolean checkAck(InputStream in) throws IOException {
+		lastMessage = new StringBuilder();
+		return checkAck(in, lastMessage);
+	}
+	
+	
+	public String getLastMessage() {
+		return lastMessage.toString();
+	}
+	
+	
+	public int getLastStatus() {
+		return lastStatus;
+	}
+	
+	private boolean checkAck(InputStream in, StringBuilder sb) throws IOException {
+		lastStatus = in.read();
+		// b may be 0 for success,
+		// 1 for error,
+		// 2 for fatal error,
+		// -1
+		if (lastStatus == 0) return true;
+		if (lastStatus == -1) return false;
+
+		if (lastStatus == 1 || lastStatus == 2) {
+			int c;
+			do {
+				c = in.read();
+				sb.append((char) c);
+			}
+			while (c != '\n');
+		}
+		return lastStatus == 0;
+	}
+}
diff --git a/java-scpclient/src/main/java/cz/it4i/fiji/scpclient/ScpClient.java b/java-scpclient/src/main/java/cz/it4i/fiji/scpclient/ScpClient.java
index 82026467..9290ea26 100644
--- a/java-scpclient/src/main/java/cz/it4i/fiji/scpclient/ScpClient.java
+++ b/java-scpclient/src/main/java/cz/it4i/fiji/scpclient/ScpClient.java
@@ -34,11 +34,17 @@ import cz.it4i.fiji.commons.DoActionEventualy;
 
 public class ScpClient implements Closeable {
 
-		private static final String NO_SUCH_FILE_OR_DIRECTORY_ERROR_TEXT = "No such file or directory";
+	private static final String NO_SUCH_FILE_OR_DIRECTORY_ERROR_TEXT =
+		"No such file or directory";
 
 	public static final Logger log = LoggerFactory.getLogger(
 		cz.it4i.fiji.scpclient.ScpClient.class);
 	
+	private static String constructExceptionText(AckowledgementChecker ack) {
+		return "Check acknowledgement failed with status: " + ack.getLastStatus() +
+			" and message: " + ack.getLastMessage();
+	}
+	
 	private static final int MAX_NUMBER_OF_CONNECTION_ATTEMPTS = 5;
 
 	private static final long TIMEOUT_BETWEEN_CONNECTION_ATTEMPTS = 500;
@@ -98,20 +104,21 @@ public class ScpClient implements Closeable {
 		download(lfile, rFile, dummyProgress);
 	}
 
-	public boolean download(String lfile, Path rfile,
+	public void download(String lfile, Path rfile,
 		TransferFileProgress progress) throws JSchException, IOException
 	{
 		if (!Files.exists(rfile.getParent())) {
 			Files.createDirectories(rfile.getParent());
 		}
 		try (OutputStream os = Files.newOutputStream(rfile)) {
-			return download(lfile, os, progress);
+			download(lfile, os, progress);
 		}
 	}
 
-	public boolean download(String lfile, OutputStream os,
+	public void download(String lfile, OutputStream os,
 		TransferFileProgress progress) throws JSchException, IOException
 	{
+		AckowledgementChecker ack = new AckowledgementChecker();
 		// exec 'scp -f rfile' remotely
 		String command = "scp -f " + lfile;
 		Channel channel = getConnectedSession().openChannel("exec");
@@ -134,8 +141,8 @@ public class ScpClient implements Closeable {
 				out.flush();
 
 				while (true) {
-					int c = checkAck(in);
-					if (c != 'C') {
+					ack.checkAck(in);
+					if (ack.getLastStatus() != 'C') {
 						break;
 					}
 
@@ -186,8 +193,8 @@ public class ScpClient implements Closeable {
 						if (filesize == 0L) break;
 					}
 
-					if (checkAck(in) != 0) {
-						return false;
+					if (!ack.checkAck(in)) {
+						throw new JSchException(constructExceptionText(ack));
 					}
 
 					// send '\0'
@@ -205,36 +212,35 @@ public class ScpClient implements Closeable {
 		finally {
 			channel.disconnect();
 		}
-		return true;
 	}
 
-	public boolean upload(Path file, String rfile) throws JSchException,
+	public void upload(Path file, String rfile) throws JSchException,
 		IOException
 	{
-		return upload(file, rfile, dummyProgress);
+		upload(file, rfile, dummyProgress);
 	}
 
-	public boolean upload(Path file, String rfile, TransferFileProgress progress)
+	public void upload(Path file, String rfile, TransferFileProgress progress)
 		throws JSchException, IOException
 	{
 		try (InputStream is = Files.newInputStream(file)) {
-			return upload(is, rfile, file.toFile().length(), file.toFile()
+			upload(is, rfile, file.toFile().length(), file.toFile()
 				.lastModified(), progress);
 		}
 	}
 
-	public boolean upload(InputStream is, String fileName, long length,
+	public void upload(InputStream is, String fileName, long length,
 		long lastModified, TransferFileProgress progress) throws JSchException,
 		IOException
 	{
 		int noSuchFileExceptionThrown = 0;
 		do {
-			
 			try {
-				return scp2Server(is, fileName, length, lastModified, progress);
+				scp2Server(is, fileName, length, lastModified, progress);
+				break;
 			} catch (NoSuchFileException e) {
-				if(noSuchFileExceptionThrown > MAX_NUMBER_OF_CONNECTION_ATTEMPTS) {
-					break;
+				if (noSuchFileExceptionThrown > MAX_NUMBER_OF_CONNECTION_ATTEMPTS) {
+					throw new JSchException(e.getReason());
 				}
 				if (noSuchFileExceptionThrown > 0) {
 					try {
@@ -248,10 +254,10 @@ public class ScpClient implements Closeable {
 				continue;
 			}
 		} while(true);
-		return false;
 	}
 
 	public long size(String lfile) throws JSchException, IOException {
+		AckowledgementChecker ack = new AckowledgementChecker();
 		// exec 'scp -f rfile' remotely
 		String command = "scp -f " + lfile;
 		Channel channel = getConnectedSession().openChannel("exec");
@@ -274,8 +280,8 @@ public class ScpClient implements Closeable {
 				out.flush();
 
 				while (true) {
-					int c = checkAck(in);
-					if (c != 'C') {
+					ack.checkAck(in);
+					if (ack.getLastStatus() != 'C') {
 						break;
 					}
 
@@ -391,10 +397,11 @@ public class ScpClient implements Closeable {
 		return session;
 	}
 
-	private boolean scp2Server(InputStream is, String fileName, long length,
+	private void scp2Server(InputStream is, String fileName, long length,
 		long lastModified, TransferFileProgress progress) throws JSchException,
 		IOException, InterruptedIOException
 	{
+		AckowledgementChecker ack = new AckowledgementChecker();
 		boolean ptimestamp = true;
 		// exec 'scp -t rfile' remotely
 		String command = "scp " + (ptimestamp ? "-p" : "") + " -t '" + fileName + "'";
@@ -405,8 +412,8 @@ public class ScpClient implements Closeable {
 				InputStream in = channel.getInputStream())
 		{
 			channel.connect();
-			if (checkAck(in) != 0) {
-				return false;
+			if (!ack.checkAck(in)) {
+				throw new JSchException(constructExceptionText(ack));
 			}
 	
 			if (ptimestamp) {
@@ -416,8 +423,8 @@ public class ScpClient implements Closeable {
 				command += (" " + (lastModified / 1000) + " 0\n");
 				out.write(command.getBytes());
 				out.flush();
-				if (checkAck(in) != 0) {
-					return false;
+				if (!ack.checkAck(in)) {
+					throw new JSchException(constructExceptionText(ack));
 				}
 			}
 	
@@ -428,13 +435,14 @@ public class ScpClient implements Closeable {
 			command += "\n";
 			out.write(command.getBytes());
 			out.flush();
-			StringBuilder sb = new StringBuilder();
-			int result;
-			if ((result = checkAck(in, sb)) != 0) {
-				if (result == 1 && sb.toString().contains(NO_SUCH_FILE_OR_DIRECTORY_ERROR_TEXT) ) {
-					throw new NoSuchFileException(getParent(fileName));
+			if (!ack.checkAck(in)) {
+				if (ack.getLastStatus() == 1 && ack.getLastMessage().contains(
+					NO_SUCH_FILE_OR_DIRECTORY_ERROR_TEXT))
+				{
+					throw new NoSuchFileException(getParent(fileName), null,
+						constructExceptionText(ack));
 				}
-				return false;
+				throw new JSchException(constructExceptionText(ack));
 			}
 			byte[] buf = new byte[getBufferSize()];
 			// send a content of lfile
@@ -448,8 +456,8 @@ public class ScpClient implements Closeable {
 			buf[0] = 0;
 			out.write(buf, 0, 1);
 			out.flush();
-			if (checkAck(in) != 0) {
-				return false;
+			if (!ack.checkAck(in)) {
+				throw new JSchException(constructExceptionText(ack));
 			}
 			out.close();
 	
@@ -461,7 +469,6 @@ public class ScpClient implements Closeable {
 		finally {
 			channel.disconnect();
 		}
-		return true;
 	}
 
 	private int mkdir(String file) throws JSchException {
@@ -515,31 +522,6 @@ public class ScpClient implements Closeable {
 
 	}
 
-	static int checkAck(InputStream in) throws IOException {
-		StringBuilder sb = new StringBuilder();
-		int result = checkAck(in, sb);
-		if (result != 0) {
-			log.warn(sb.toString());
-		}
-		return result;
-	}
-	static int checkAck(InputStream in, StringBuilder sb) throws IOException {
-		int b = in.read();
-		// b may be 0 for success,
-		// 1 for error,
-		// 2 for fatal error,
-		// -1
-		if (b == 0) return b;
-		if (b == -1) return b;
-
-		if (b == 1 || b == 2) {
-			int c;
-			do {
-				c = in.read();
-				sb.append((char) c);
-			}
-			while (c != '\n');
-		}
-		return b;
-	}
+	
+	
 }
-- 
GitLab