| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * https://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| package org.apache.tools.ant.taskdefs.optional.ssh; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.text.NumberFormat; |
| |
| import org.apache.tools.ant.BuildException; |
| |
| import com.jcraft.jsch.Channel; |
| import com.jcraft.jsch.ChannelExec; |
| import com.jcraft.jsch.ChannelSftp; |
| import com.jcraft.jsch.JSchException; |
| import com.jcraft.jsch.Session; |
| import com.jcraft.jsch.SftpProgressMonitor; |
| |
| /** |
| * Abstract class for ssh upload and download |
| */ |
| public abstract class AbstractSshMessage { |
| private static final double ONE_SECOND = 1000.0; |
| |
| private final Session session; |
| private final boolean verbose; |
| private final boolean compressed; |
| private LogListener listener = message -> { |
| // do nothing; |
| }; |
| |
| /** |
| * Constructor for AbstractSshMessage |
| * @param session the ssh session to use |
| */ |
| public AbstractSshMessage(final Session session) { |
| this(false, session); |
| } |
| |
| /** |
| * Constructor for AbstractSshMessage |
| * @param verbose if true do verbose logging |
| * @param session the ssh session to use |
| * @since Ant 1.6.2 |
| */ |
| public AbstractSshMessage(final boolean verbose, final Session session) { |
| this(verbose, false, session); |
| } |
| |
| /** |
| * Constructor for AbstractSshMessage |
| * @param verbose if true do verbose logging |
| * @param compressed if true use compression |
| * @param session the ssh session to use |
| * @since Ant 1.9.8 |
| */ |
| public AbstractSshMessage(boolean verbose, boolean compressed, Session session) { |
| this.verbose = verbose; |
| this.compressed = compressed; |
| this.session = session; |
| } |
| |
| /** |
| * Open an ssh channel. |
| * @param command the command to use |
| * @return the channel |
| * @throws JSchException on error |
| */ |
| protected Channel openExecChannel(final String command) throws JSchException { |
| final ChannelExec channel = (ChannelExec) session.openChannel("exec"); |
| channel.setCommand(command); |
| |
| return channel; |
| } |
| |
| /** |
| * Open an ssh sftp channel. |
| * @return the channel |
| * @throws JSchException on error |
| */ |
| protected ChannelSftp openSftpChannel() throws JSchException { |
| return (ChannelSftp) session.openChannel("sftp"); |
| } |
| |
| /** |
| * Send an ack. |
| * @param out the output stream to use |
| * @throws IOException on error |
| */ |
| protected void sendAck(final OutputStream out) throws IOException { |
| final byte[] buf = new byte[1]; |
| buf[0] = 0; |
| out.write(buf); |
| out.flush(); |
| } |
| |
| /** |
| * Reads the response, throws a BuildException if the response |
| * indicates an error. |
| * @param in the input stream to use |
| * @throws IOException on I/O error |
| * @throws BuildException on other errors |
| */ |
| protected void waitForAck(final InputStream in) |
| throws IOException, BuildException { |
| final int b = in.read(); |
| |
| // b may be 0 for success, |
| // 1 for error, |
| // 2 for fatal error, |
| |
| if (b == -1) { |
| // didn't receive any response |
| throw new BuildException("No response from server"); |
| } |
| if (b != 0) { |
| final StringBuilder sb = new StringBuilder(); |
| |
| int c = in.read(); |
| while (c > 0 && c != '\n') { |
| sb.append((char) c); |
| c = in.read(); |
| } |
| |
| if (b == 1) { |
| throw new BuildException("server indicated an error: " |
| + sb.toString()); |
| } else if (b == 2) { |
| throw new BuildException("server indicated a fatal error: " |
| + sb.toString()); |
| } else { |
| throw new BuildException("unknown response, code " + b |
| + " message: " + sb.toString()); |
| } |
| } |
| } |
| |
| /** |
| * Carry out the transfer. |
| * @throws IOException on I/O errors |
| * @throws JSchException on ssh errors |
| */ |
| public abstract void execute() throws IOException, JSchException; |
| |
| /** |
| * Set a log listener. |
| * @param aListener the log listener |
| */ |
| public void setLogListener(final LogListener aListener) { |
| listener = aListener; |
| } |
| |
| /** |
| * Log a message to the log listener. |
| * @param message the message to log |
| */ |
| protected void log(final String message) { |
| listener.log(message); |
| } |
| |
| /** |
| * Log transfer stats to the log listener. |
| * @param timeStarted the time started |
| * @param timeEnded the finishing time |
| * @param totalLength the total length |
| */ |
| protected void logStats(final long timeStarted, |
| final long timeEnded, |
| final long totalLength) { |
| final double duration = (timeEnded - timeStarted) / ONE_SECOND; |
| final NumberFormat format = NumberFormat.getNumberInstance(); |
| format.setMaximumFractionDigits(2); |
| format.setMinimumFractionDigits(1); |
| listener.log("File transfer time: " + format.format(duration) |
| + " Average Rate: " + format.format(totalLength / duration) |
| + " B/s"); |
| } |
| |
| /** |
| * Is the verbose attribute set. |
| * @return true if the verbose attribute is set |
| * @since Ant 1.6.2 |
| */ |
| protected final boolean getVerbose() { |
| return verbose; |
| } |
| |
| /** |
| * Is the compressed attribute set. |
| * @return true if the compressed attribute is set |
| * @since Ant 1.9.8 |
| */ |
| protected final boolean getCompressed() { |
| return compressed; |
| } |
| |
| /** |
| * Track progress every 10% if 100kb < filesize < 1Mb. For larger |
| * files track progress for every percent transmitted. |
| * @param filesize the size of the file been transmitted |
| * @param totalLength the total transmission size |
| * @param percentTransmitted the current percent transmitted |
| * @return the percent that the file is of the total |
| */ |
| protected final int trackProgress(final long filesize, final long totalLength, |
| final int percentTransmitted) { |
| |
| // CheckStyle:MagicNumber OFF |
| final int percent = (int) Math.round(Math.floor((totalLength |
| / (double) filesize) * 100)); |
| |
| if (percent > percentTransmitted) { |
| if (filesize < 1048576) { |
| if (percent % 10 == 0) { |
| if (percent == 100) { |
| System.out.println(" 100%"); |
| } else { |
| System.out.print("*"); |
| } |
| } |
| } else { |
| if (percent == 50) { |
| System.out.println(" 50%"); |
| } else if (percent == 100) { |
| System.out.println(" 100%"); |
| } else { |
| System.out.print("."); |
| } |
| } |
| } |
| // CheckStyle:MagicNumber ON |
| |
| return percent; |
| } |
| |
| private ProgressMonitor monitor = null; |
| |
| /** |
| * Get the progress monitor. |
| * @return the progress monitor. |
| */ |
| protected SftpProgressMonitor getProgressMonitor() { |
| if (monitor == null) { |
| monitor = new ProgressMonitor(); |
| } |
| return monitor; |
| } |
| |
| private class ProgressMonitor implements SftpProgressMonitor { |
| private long initFileSize = 0; |
| private long totalLength = 0; |
| private int percentTransmitted = 0; |
| |
| @Override |
| public void init(final int op, final String src, final String dest, final long max) { |
| initFileSize = max; |
| } |
| |
| @Override |
| public boolean count(final long len) { |
| totalLength += len; |
| percentTransmitted = trackProgress(initFileSize, |
| totalLength, |
| percentTransmitted); |
| return true; |
| } |
| |
| @Override |
| public void end() { |
| } |
| |
| @SuppressWarnings("unused") |
| public long getTotalLength() { |
| return totalLength; |
| } |
| } |
| } |