Package org.voltdb.processtools

Source Code of org.voltdb.processtools.SFTPSession$SSHException

/* This file is part of VoltDB.
* Copyright (C) 2008-2014 VoltDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with VoltDB.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.voltdb.processtools;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.voltcore.logging.VoltLogger;

import com.google_voltpatches.common.base.Charsets;
import com.google_voltpatches.common.base.Preconditions;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.ChannelSftp.LsEntrySelector;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;

/**
* Utility class that aides the copying of files to remote hosts using SFTP.
*
* @author stefano
*
*/
public class SFTPSession {

    /**
     * default logger
     */
    protected static final VoltLogger sftpLog = new VoltLogger("HOST");
    /*
     * regular expression that matches file names ending in jar, so, and jnilib
     */
    private final static Pattern ARTIFACT_REGEXP = Pattern.compile("\\.(?:jar|so|jnilib)\\Z");
    /*
     * JSCH session
     */
    private Session m_session;
    /*
     * JSch SFTP channel
     */
    private ChannelSftp m_channel;
    /*
     *  remote host name
     */
    private final String m_host;
    /*
     * instance logger
     */
    private final VoltLogger m_log;

    /**
     * Instantiate a wrapper around a JSch Sftp Channel
     *
     * @param user SFTP connection user name
     * @param key SFTP connection private key
     * @param host SFTP remote host name
     * @param password SFTP connection password
     * @param port SFTP port
     * @param log logger
     *
     * @throws {@link SFTPException} when it cannot connect, and establish a SFTP
     *   session
     */
    public SFTPSession(
            final String user, final String password, final String key, final String host,
            int port, final VoltLogger log) {
        Preconditions.checkArgument(
                user != null && !user.trim().isEmpty(),
                "specified empty or null user"
                );
        Preconditions.checkArgument(
                host != null && !host.trim().isEmpty(),
                "specified empty or null host"
                );
        Preconditions.checkArgument(
                port > 1,
                "specified invalid port"
                );

        m_host = host;
        if (log == null) m_log = sftpLog;
        else m_log = log;

        JSch jsch = new JSch();

        if (key != null && !key.trim().isEmpty()) {
            try {
                jsch.addIdentity(key);
            } catch (JSchException jsex) {
                throw new SFTPException("add identity file " + key, jsex);
            }
        }

        try {
            m_session = jsch.getSession(user, host, port);
            m_session.setTimeout(15000);
            m_session.setConfig("StrictHostKeyChecking", "no");
            m_session.setDaemonThread(true);

            if (password != null && !password.trim().isEmpty()) {
                m_session.setPassword(password);
            }
        } catch (JSchException jsex) {
            throw new SFTPException("create a JSch session", jsex);
        }

        try {
            m_session.connect();
        } catch (JSchException jsex) {
            throw new SFTPException("connect a JSch session", jsex);
        }

        ChannelSftp channel;
        try {
            channel = (ChannelSftp)m_session.openChannel("sftp");
        } catch (JSchException jsex) {
            throw new SFTPException("create an SFTP channel", jsex);
        }

        try {
            channel.connect();
        } catch (JSchException jsex) {
            throw new SFTPException("open an SFTP channel", jsex);
        }
        m_channel = channel;
    }

    public SFTPSession( final String user, final String password, final String key,
            final String host, final VoltLogger log) {
        this(user, password, key, host, 22, log);
    }

    public SFTPSession(
            final String user, final String key, final String host,
            int port, final VoltLogger log) {
        this(user, null, key, host, 22, null);
    }

    public SFTPSession( final String user, final String key, final String host) {
        this(user, null, key, host, 22, null);
    }

    public SFTPSession( final String user, final String key,
            final String host, final VoltLogger log) {
        this(user, null, key, host, 22, log);
    }

    /**
     * Given a map where their keys contain absolute source file, and their associated
     * values contain their respective absolute destinations files (not directories)
     * ensure that the directories used in the destination files exist (creating
     * them if needed), removes previously installed artifacts files that end
     * with .so, .jar, and .jnilib, and copy over the source files to their
     * destination.
     *
     * @param files Map where their keys contain absolute source file, and their associated
     * values contain their respective absolute destinations files. NB destinations must not
     * be directory names, but fully specified file names
     *
     * @throws {@link SFTPException} when an error occurs during SFTP operations
     *   performed by this method
     */
    public void install( final Map<File, File> files) {
        Preconditions.checkArgument(
                files != null, "null file collection"
                );

        ensureDirectoriesExistFor(files.values());
        deletePreviouslyInstalledArtifacts(files.values());
        copyOverFiles(files);
    }

    /**
     * Given a map where their keys contain absolute source file, and their associated
     * values contain their respective absolute destinations files (not directories)
     * copy over the source files to their destination.
     *
     * @param files Map where their keys contain absolute source file, and their associated
     * values contain their respective absolute destinations files. NB destinations must not
     * be directory names, but fully specified file names
     *
     * @throws {@link SFTPException} when an error occurs during SFTP operations
     *   performed by this method
     */
    public void copyOverFiles( final Map<File, File> files) {
        Preconditions.checkArgument(
                files != null, "null file collection"
                );
        Preconditions.checkState(
                m_channel != null, "stale session"
                );
        verifyAllAreAbsolutePaths(files);

        for (Map.Entry<File, File> entry: files.entrySet()) {
            String src = entry.getKey().getPath();
            String dst = entry.getValue().getPath();
            try {
                m_channel.put(src, dst);

                if (m_log.isDebugEnabled()) {
                    m_log.debug("SFTP: put " + src + " " + dst);
                }
            } catch (SftpException sfex) {
                throw new SFTPException("put " + src + " " + dst, sfex);
            }
        }
    }

    /**
     * Given a map where their keys contain absolute source file, and their associated
     * values contain their respective absolute destinations files (not directories)
     * copy over the source files to their destination.
     *
     * @param files Map where their keys contain absolute source file, and their associated
     * values contain their respective absolute destinations files. NB destinations must not
     * be directory names, but fully specified file names
     *
     * @throws {@link SFTPException} when an error occurs during SFTP operations
     *   performed by this method
     */
    public void copyInFiles( final Map <File,File> files) {
        Preconditions.checkArgument(
                files != null, "null file collection"
                );
        Preconditions.checkState(
                m_channel != null, "stale session"
                );
        verifyAllAreAbsolutePaths(files);

        for (Map.Entry<File, File> entry: files.entrySet()) {
            String src = entry.getKey().getPath();
            String dst = entry.getValue().getPath();
            try {
                m_channel.get(src, dst);
                if (m_log.isDebugEnabled()) {
                    m_log.debug("SFTP: get " + src + " " + dst);
                }
            } catch (SftpException sfex) {
                throw new SFTPException("get " + src + " " + dst, sfex);
            }
        }
    }

    /**
     * Delete the given list of absolute files paths
     *
     * @param files a collection of files specified as absolute paths
     * @throws SFTPException when an error occurs during SFTP operations performed
     *   by this method
     */
    public void deleteFiles(final Collection<File> files) {
        Preconditions.checkArgument(
                files != null, "null file collection"
                );
        Preconditions.checkState(
                m_channel != null, "stale session"
                );
        verifyAllAreAbsolutePaths(files);

        for (File f: files) {
            try {
                m_channel.rm(f.getPath());
                if (m_log.isDebugEnabled()) {
                    m_log.debug("SFTP: rm " + f);
                }
            } catch (SftpException sfex) {
                throw new SFTPException("rm " + f, sfex);
            }
        }
    }

    /**
     * if found, it deletes artifacts held in the directories that
     * contain the given list of absolute file paths
     *
     * @param files a collection of files specified as absolute paths
     *
     * @throws SFTPException when an error occurs during SFTP operations performed
     *   by this method
     */
    public void deletePreviouslyInstalledArtifacts( final Collection<File> files) {
        Preconditions.checkArgument(
                files != null, "null file collection"
                );
        Preconditions.checkState(
                m_channel != null, "stale session"
                );
        verifyAllAreAbsolutePaths(files);

        // dedup directories containing files
        TreeSet<File> directories = new TreeSet<File>();
        for (File f: files) {
            directories.add( f.getParentFile());
        }
        // look for file artifacts that end with .so, .jar, and .jnilib
        for (File d: directories) {
            final ArrayList<String> toBeDeleted = new ArrayList<String>();
            LsEntrySelector selector = new LsEntrySelector() {
                @Override
                public int select(LsEntry entry) {
                    Matcher mtc = ARTIFACT_REGEXP.matcher(entry.getFilename());
                    SftpATTRS attr = entry.getAttrs();
                    if (mtc.find() && !attr.isDir() && !attr.isLink()) {
                        toBeDeleted.add(entry.getFilename());
                    }
                    return CONTINUE;
                }
            };
            try {
                m_channel.ls( d.getPath(), selector);
                if (m_log.isDebugEnabled()) {
                    m_log.debug("SFTP: ls " + d.getPath());
                }
            } catch (SftpException sfex) {
                throw new SFTPException("list directory " + d, sfex);
            }
            // delete found artifacts
            for (String f: toBeDeleted) {
                File artifact = new File( d, f);
                try {
                    m_channel.rm(artifact.getPath());
                    if (m_log.isDebugEnabled()) {
                        m_log.debug("SFTP: rm " + artifact.getPath());
                    }
                } catch (SftpException sfex) {
                    throw new SFTPException("remove artifact " + artifact, sfex);
                }
            }
        }
    }

    /**
     * Akin to mkdir -p for all directories containing the given collection of
     * remote files.
     *
     * @param files a collection of files specified as absolute paths
     *
     * @throws {@link SFTPException} when an error occurs during SFTP operations
     *   performed by this method
     */
    public void ensureDirectoriesExistFor( final Collection<File> files) {
        Preconditions.checkArgument(
                files != null, "null file collection"
                );
        Preconditions.checkState(
                m_channel != null, "stale session"
                );
        verifyAllAreAbsolutePaths(files);

        /*
         * directory entries are sorted first by their level (/l1 < /l1/l2 < /l1/l2/l3)
         * and then their name. This loop adds all the directories that are required
         * to ensure that given list of destination files can be copied over successfully
         */
        TreeSet<DirectoryEntry> directories = new TreeSet<DirectoryEntry>();
        for (File f: files) {
           addDirectoryAncestors(f.getParentFile(), directories);
        }
        /*
         * for each entry it tests whether or not it already exists, and if it
         * does not, it creates it (akin to mkdir -p)
         */
        for (DirectoryEntry entry: directories) {
            if (!directoryExists(entry.getDirectory())) {
                try {
                    m_channel.mkdir(entry.getDirectory().getPath());
                    if (m_log.isDebugEnabled()) {
                        m_log.debug("SFTP: mkdir " + entry.getDirectory().getPath());
                    }
                } catch (SftpException sfex) {
                    throw new SFTPException("create directory " + entry, sfex);
                }
            }
        }
        directories.clear();
    }

    /**
     * Akin to mkdir -p for all directories containing the given collection of
     * of local files.
     *
     * @param files a collection of files specified as absolute paths
     *
     * @throws {@link RuntimeException} when an error occurs during local file
     *   operations performed by this method
     */
    public void ensureLocalDirectoriesExistFor(final Collection<File> files) {
        Preconditions.checkArgument(
                files != null, "null file collection"
                );
        Preconditions.checkState(
                m_channel != null, "stale session"
                );
        verifyAllAreAbsolutePaths(files);

        // dedup directories containing files
        TreeSet<File> directories = new TreeSet<File>();
        for (File f: files) {
            directories.add( f.getParentFile());
        }
        for (File dir: directories) {
            dir.mkdirs();
            if (   !dir.exists()
                || !dir.isDirectory()
                || !dir.canRead()
                || !dir.canWrite()
                || !dir.canExecute()
            ) {
                throw new SFTPException(dir + " is not write accessible");
            }
        }
        directories.clear();
    }

    /**
     * Test whether or not the given directory exists on the remote host
     *
     * @param directory directory name
     *
     * @return true if does, false if it does not
     *
     * @throws {@link SFTPException} when an error occurs during SFTP operations
     *   performed by this method
     */
    public boolean directoryExists(final File directory) {
        Preconditions.checkArgument(
                directory != null, "null directory"
                );
        Preconditions.checkState(
                m_channel != null, "stale session"
                );

        DirectoryExistsSelector selector = new DirectoryExistsSelector(directory);
        try {
            m_channel.ls(directory.getParent(), selector);
            if (m_log.isDebugEnabled()) {
                m_log.debug("SFTP: ls " + directory.getParent());
            }
        } catch (SftpException sfex) {
            throw new SFTPException("list directory " + directory.getParent(), sfex);
        }
        return selector.doesExist();
    }

    /**
     * Executes the given command with the given list as its input
     *
     * @param list input
     * @param command command to execute on remote host
     * @return the output of the command as a list
     * @throws {@link SSHException} when an error occurs during SSH
     *   command performed by this method
     */
    public List<String> pipeListToShellCommand(
            final Collection<String> list, final String command) {

        Preconditions.checkArgument(
                command != null && !command.trim().isEmpty(),
                "specified empty or null command string"
                );
        Preconditions.checkState(
                m_channel != null, "stale session"
                );

        ChannelExec e = null;
        BufferedReader sherr = null;
        BufferedReader shout = null;

        List<String> shellout = new ArrayList<String>();

        try {
            try {
                e = (ChannelExec)m_channel.getSession().openChannel("exec");
            } catch (JSchException jex) {
                throw new SSHException("opening ssh exec channel", jex);
            }
            try {
                shout = new BufferedReader(
                        new InputStreamReader(
                                e.getInputStream(), Charsets.UTF_8));
            } catch (IOException ioex) {
                throw new SSHException("geting exec channel input stream", ioex);
            }
            try {
                sherr = new BufferedReader(
                        new InputStreamReader(
                                e.getErrStream(), Charsets.UTF_8));
            } catch (IOException ioex) {
                throw new SSHException("getting exec channel error stream", ioex);
            }
            if (list != null && !list.isEmpty()) {
                e.setInputStream( listAsInputStream(list));
            }
            e.setCommand(command);

            try {
                e.connect(5000);
                int retries = 50;
                while (!e.isClosed() && retries-- > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException ignoreIt) {}
                }
                if (retries < 0) {
                    throw new SSHException("'" + command + "' timed out");
                }
            } catch (JSchException jex) {
                throw new SSHException("executing '" + command + "'", jex);
            }

            try {
                String outputLine = shout.readLine();
                while (outputLine != null) {
                    shellout.add(outputLine);
                    outputLine = shout.readLine();
                }
            } catch (IOException ioex) {
                throw new SSHException("capturing '" + command + "' output", ioex);
            }
            if (e.getExitStatus() != 0) {
                try {
                    String errorLine = sherr.readLine();
                    while (errorLine != null) {
                        shellout.add(errorLine);
                        errorLine = sherr.readLine();
                    }
                } catch (IOException ioex) {
                    throw new SSHException("capturing '" + command + "' error", ioex);
                }
                throw new SSHException(
                        "error output from '" +
                        command + "':\n\t" + join(shellout,"\n\t")
                        );
            }
            if (m_log.isDebugEnabled()) {
                m_log.debug("SSH: " + command);
            }
        } finally {
            if (sherr != null) try { sherr.close(); } catch (Exception ignoreIt) {}
            if (shout != null) try { shout.close(); } catch (Exception ignoreIt) {}
        }

        return shellout;
    }

    /**
     * Creates hard links of the given list of absolute paths by appending
     * the given extension to them
     *
     * @param files a collection of files specified as absolute paths
     * @param linkExtension
     *
     * @throws {@link SFTPException} when an error occurs during SFTP operations
     *   performed by this method
     */
    public void createLinks(final Collection<File> files, final String linkExtension) {
        Preconditions.checkArgument(
                files != null, "null file collection"
                );
        Preconditions.checkArgument(
                linkExtension != null && !linkExtension.trim().isEmpty(),
                "specified null or empty linkEtension"
                );
        Preconditions.checkState(
                m_channel != null, "stale session"
                );
        verifyAllAreAbsolutePaths(files);

        ArrayList<String> fileNames = new ArrayList<String>();
        for (File f: files) {
            fileNames.add(f.getPath());
        }

        pipeListToShellCommand(
                fileNames, "xargs -I {} ln -f {} {}" + linkExtension);

        if (m_log.isDebugEnabled()) {
            for (String fileName: fileNames) {
                m_log.debug("CMD: 'ln " +
                        fileName + " " +
                        fileName+linkExtension + "'"
                        );
            }
        }
    }

    /**
     * Joins the given list of string using the given join string
     *
     * @param list of strings
     * @param joinWith string to join the list with
     * @return items of the given list joined with the given join string
     */
    protected final static String join(
            final Collection<String> list, final String joinWith) {
        Preconditions.checkArgument(list != null, "specified null list");
        Preconditions.checkArgument(joinWith != null, "specified null joinWith string");

        int cnt = 0;
        StringBuilder sb = new StringBuilder();
        for (String item: list) {
            if (cnt++ > 0) sb.append(joinWith);
            sb.append(item);
        }
        return sb.toString();
    }

    /**
     * traverses up all the given directory ancestors, and adds them as directory
     * entries, designated by their level, to the given set of directory entries
     *
     * @param directory directory name
     * @param directories set of directory entries
     * @return
     */
    protected int addDirectoryAncestors(
            final File directory,
            final TreeSet<DirectoryEntry> directories)
    {
        // root folder
        if (directory == null) return 0;
        // if not root recurse and return this directory level
        int level = addDirectoryAncestors(directory.getParentFile(), directories);
        // add it to the set if it is not a root folder
        if( level > 0) {
            directories.add( new DirectoryEntry(level, directory));
        }
        return level + 1;
    }

    public String exec(String command) {
        return exec(command, 5000);
    }

    public String exec(String command, int timeout) {
        ChannelExec channel = null;
        BufferedReader outStrBufRdr = null;
        BufferedReader errStrBufRdr = null;

        StringBuilder result = new StringBuilder(2048);

        try {
            try {
                channel = (ChannelExec)m_session.openChannel("exec");
            } catch (JSchException jex) {
                throw new SSHException("opening ssh exec channel", jex);
            }

            // Direct stdout output of command
            try {
                InputStream out = channel.getInputStream();
                InputStreamReader outStrRdr = new InputStreamReader(out, "UTF-8");
                outStrBufRdr = new BufferedReader(outStrRdr);
            } catch (IOException ioex) {
                throw new SSHException("geting exec channel input stream", ioex);
            }

            // Direct stderr output of command
            try {
                InputStream err = channel.getErrStream();
                InputStreamReader errStrRdr = new InputStreamReader(err, "UTF-8");
                errStrBufRdr = new BufferedReader(errStrRdr);
            } catch (IOException ioex) {
                throw new SSHException("getting exec channel error stream", ioex);
            }
            channel.setCommand(command);

            StringBuffer stdout = new StringBuffer();
            StringBuffer stderr = new StringBuffer();

            try {
                channel.connect(timeout);
                int retries = timeout / 100;
                while (!channel.isClosed() && retries-- > 0) {
                    // Read from both streams here so that they are not blocked,
                    // if they are blocked because the buffer is full, channel.isClosed() will never
                    // be true.
                    int ch;
                    try {
                        while (outStrBufRdr.ready() && (ch = outStrBufRdr.read()) > -1) {
                            stdout.append((char) ch);
                        }
                    } catch (IOException ioex) {
                        throw new SSHException("capturing '" + command + "' output", ioex);
                    }
                    try {
                        while (errStrBufRdr.ready() && (ch = errStrBufRdr.read()) > -1) {
                            stderr.append((char) ch);
                        }
                    } catch (IOException ioex) {
                        throw new SSHException("capturing '" + command + "' error", ioex);
                    }

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException ignoreIt) {}
                }
                if (retries < 0) {
                    throw new SSHException("'" + command + "' timed out");
                }
            } catch (JSchException jex) {
                throw new SSHException("executing '" + command + "'", jex);
            }

            // In case there's still some more stuff in the buffers, read them
            int ch;
            try {
                while ((ch = outStrBufRdr.read()) > -1) {
                    stdout.append((char) ch);
                }
            } catch (IOException ioex) {
                throw new SSHException("capturing '" + command + "' output", ioex);
            }
            try {
                while ((ch = errStrBufRdr.read()) > -1) {
                    stderr.append((char) ch);
                }
            } catch (IOException ioex) {
                throw new SSHException("capturing '" + command + "' error", ioex);
            }
            if (stderr.length() > 0) {
                throw new SSHException(stderr.toString());
            }

            result.append(stdout.toString());
            result.append(stderr.toString());
        } finally {
            if (outStrBufRdr != null) try { outStrBufRdr.close(); } catch (Exception ignoreIt) {}
            if (errStrBufRdr != null) try { errStrBufRdr.close(); } catch (Exception ignoreIt) {}

            if (channel != null && channel.isConnected()) {
                // Shutdown the connection
                channel.disconnect();
            }
        }

        return result.toString();
    }

    /**
     * Terminate the SFTP session associated with this instance
     */
    public void terminate() {
        try {
            if (m_channel == null) return;
            Session session = null;
            try { session = m_channel.getSession(); } catch (Exception ignoreIt) {}
            try { m_channel.disconnect(); } catch (Exception ignoreIt) {}
            if (session != null)
                try { session.disconnect(); } catch (Exception ignoreIt) {}
        } finally {
            m_channel = null;
        }
    }

    /**
     * Return an {@link InputStream} that encompasses the the content
     * of the given list separated by the new line character
     *
     * @param list of strings
     * @return an {@link InputStream} that encompasses the the content
     *   of the given list separated by the new line character
     */
    protected final static InputStream listAsInputStream(
            final Collection<String> list) {
        Preconditions.checkArgument(list != null, "specified null list");
        StringBuilder sb = new StringBuilder();
        for (String item: list) {
            sb.append(item).append("\n");
        }
        return new ByteArrayInputStream(sb.toString().getBytes(Charsets.UTF_8));
    }

    /**
     * Verifies that given collection of files contain only files specified
     * as absolute paths
     * @param files a collection of files
     * @throws IllegalArgumentException when a file is not specified as an absolute path
     */
    protected void verifyAllAreAbsolutePaths( final Collection<File> files) {
        for (final File f: files) {
            Preconditions.checkArgument(f.isAbsolute(), f + " is not an absolute path");
        }
    }

    /**
     * Verifies that given map of files contain only files specified
     * as absolute paths
     * @param files a collection of files
     * @throws IllegalArgumentException when a file is not specified as an absolute path
     */
    protected void verifyAllAreAbsolutePaths( final Map<File,File> files) {
        for (final Map.Entry<File, File> e: files.entrySet()) {
            Preconditions.checkArgument(
                    e.getKey().isAbsolute(),
                    "source file "+e.getKey()+" is not an absolute path"
                    );
            Preconditions.checkArgument(
                    e.getValue().isAbsolute(),
                    "destination file "+e.getValue()+" is not an absolute path"
                    );
        }
    }

    protected final static class DirectoryExistsSelector implements LsEntrySelector {
        boolean m_exists = false;
        final File m_directory;

        private DirectoryExistsSelector(final File directory) {
            this.m_directory = directory;
        }

        @Override
        public int select(LsEntry entry) {
            m_exists = m_directory.getName().equals(entry.getFilename())
                    && (entry.getAttrs().isDir() || entry.getAttrs().isLink());
            if (m_exists) return BREAK;
            else return CONTINUE;
        }

        private boolean doesExist() {
            return m_exists;
        }
    }

    protected final static class DirectoryEntry implements Comparable<DirectoryEntry> {
        final int m_level;
        final File m_directory;

        DirectoryEntry( final int level, final File directory) {
            this.m_level = level;
            this.m_directory = directory;
        }

        int getLevel() {
            return m_level;
        }

        File getDirectory() {
            return m_directory;
        }

        @Override
        public int compareTo(DirectoryEntry o) {
            int cmp = this.m_level - o.m_level;
            if ( cmp == 0) cmp = this.m_directory.compareTo(o.m_directory);
            return cmp;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result
                    + ((m_directory == null) ? 0 : m_directory.hashCode());
            result = prime * result + m_level;
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            DirectoryEntry other = (DirectoryEntry) obj;
            if (m_directory == null) {
                if (other.m_directory != null)
                    return false;
            } else if (!m_directory.equals(other.m_directory))
                return false;
            if (m_level != other.m_level)
                return false;
            return true;
        }

        @Override
        public String toString() {
            return "DirectoryEntry [level=" + m_level + ", directory="
                    + m_directory + "]";
        }
    }

    public class SFTPException extends RuntimeException {

        private static final long serialVersionUID = 9135753444480123857L;

        public SFTPException() {
            super("(Host: " + m_host + ")");
        }

        public SFTPException(String message, Throwable cause) {
            super("(Host: " + m_host + ") " + message, cause);
        }

        public SFTPException(String message) {
            super("(Host: " + m_host + ") " + message);
        }

        public SFTPException(Throwable cause) {
            super("(Host: " + m_host + ")",cause);
        }
    }

    public class SSHException extends SFTPException {

        private static final long serialVersionUID = 8494735481584692337L;

        public SSHException() {
            super();
        }

        public SSHException(String message, Throwable cause) {
            super(message, cause);
        }

        public SSHException(String message) {
            super(message);
        }

        public SSHException(Throwable cause) {
            super(cause);
        }
    }

    /**
     * Convenient file attribute translation helper class
     * @author ssantoro
     *
     */
    public static class BasicAttributes {
        private final int m_modifyTime;
        private final boolean m_isExecutable;

        public BasicAttributes(final SftpATTRS attr) {
            Preconditions.checkArgument(attr != null, "specified null sftp attributes");
            m_modifyTime = attr.getMTime();
            m_isExecutable = (attr.getPermissions() & 0100) != 0;
        }

        public BasicAttributes(final File file) {
            Preconditions.checkArgument(
                    file != null && file.exists() && file.canRead(),
                    "specified file is null or inaccessible"
                    );
            m_modifyTime = (int)(file.lastModified() / 1000);
            m_isExecutable = file.canExecute();
        }

        public void setFor(SftpATTRS attr) {
            if (attr == null) return;
            attr.setACMODTIME(m_modifyTime, m_modifyTime);
            if (m_isExecutable) {
                attr.setPERMISSIONS(attr.getPermissions() | 0100);
            }
        }

        public void setFor(File file) {
            if (file == null || !file.exists() || !file.canWrite()) return;
            file.setLastModified(m_modifyTime * 1000);
            if (m_isExecutable) {
                file.setExecutable(true);
            }
        }
    }
}
TOP

Related Classes of org.voltdb.processtools.SFTPSession$SSHException

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
script> ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');