练字 
逆天,真的强
大佬,我也想去杭州
仿佛身临其境: 面试你的人只是找了一个你能接受的结果。 
我在某金融公司三面 (应该算三面还是四面? 笔试、hr、领导一、领带二) 被拒绝的理由: 是买房子的时候会从老婆的意见,从而得出我缺乏主观判断能力。 
比较起来,你这个更让人难受,我这个更让人接受 (他只是不想要我而已,够体面了)。
先收藏,再赞,然后看 
都是被测试耽误的天王级歌手
有那么一瞬间。想回家建设家乡 
哈哈哈,没绷住,笑了,仿佛看到了自己
就从来没这个仪式 你是不是开心多了
拿下,等调货,x 东居然没有现货,差评~
别提了,某直拒上还跟你说话的基本就剩下华为的 OD 了
郑州啊,好想回去~~~~ 大佬这是你的公司?
1, 4 楼正解,主要在团队里工作,需要向团队整体规划倾斜,哪个会的多,可能选型更偏向哪个,毕竟项目合作,不可能选一个只有你会的语言,这个没办法。
当然我提倡可以都学都会,万一 (没有万一,因为我这里基本不会给你机会脱离大趋势) 有机会用你喜欢的呢。。~~
羡慕,终我半辈子都没有碰到的机会,你们还有挑的余地 
大半年没来了,趁着社区恢复,打个卡
我把社区的网站清除了缓存,重新进来好了,不知道是不是啥时候误点了哪里,哈哈。
这道题两个月前刚做过,我大概知道作者是去了哪个公司的碰到了笔试题 
testerhome 系统字体都变繁体了吗,还是一直都是繁体,而我才刚发现 

好的,祝你好运
你好像没有弄清楚它的工作原理吧,你这个命令只是起一个 java 服务的,与以前不一样的是,你起这个 java 服务的时候,注入了 jacoco 的代理,让你有对这个 java 服务统计代码执行状况的能力,后续的测试过程和生成报告是独立的哦
换一下 jackson 的版本试试
看你说的一堆问题,那就一个一个解决么,问题这种东西,解决一个少一个,干掉一个轻松一下。
回答你的第一个问题:总结为代码比对的问题。
差异化的覆盖,大致可以在两个阶段做处理,我提一些我的思路,看能不能对你有一点帮助。
你去过滤 class 的信息,选择性生成报告,这样你可以只要那些过滤出来的变更代码的数据,原来的数据可以丢掉。当然需要你对 jacoco 自己的机制要清楚。
你去针对它的报告,结合自己的代码比对结果对报告做定制。当然,这个时候你可能需要对它的百分比单独处理, 小心数据不准确。
回答你的第二个问题:关于容器的问题,我不是很精通。
但我猜测是否可以把你对应的那个 tcp 服务的端口也映射出去给宿主,dump 的时候请求直接发给主机。
至于怎么拿出来,那应该有不少方式把。
搜一下 ASM,了解一下他的一些机制,再看看有没有头绪
你好像没有理清集成测试覆盖率的流程。
测试环境启动的时候,注入 jacoco,只是提供了一个 jacoco 的功能,同时在测试服务器上开启 tcp 服务存放当前应用的执行覆盖数据。
当然,这个 tcp 服务成功开启之后,是允许远程调用的,就是你可以在其他的服务器上甚至在你本机发起 tcp 连接,去获取它存放的覆盖数据。这一点 jacoco 提供的有专门的的 api。
你可以把这个过程理解为,测试环境集成 jacoco 功能且开启了一个 tcp 服务之后,你后续的覆盖率统计,已经基本和这个测试环境没什么关系了。
剩余的过程,就是你自己生成报告的方法了,总之生成报告只需要三个东西,上面有提到,至于你的源码存放在哪里并不重要,你只要保证你的测试环境启动的 jar 包的编译源码跟你生成报告时候用的源码是一致的就可以了。
不知道我有没有描述清楚。
想怎么指导呢~~~
这问题属于覆盖率范畴之外的通用功能了,用代码调用 sftp 下载、执行 scp 或者 xcopy 之类的命令,都可以。
给你个样例吧,这是从一个 linux 远程服务器上下载的,根据自己需要去调整,里面有的从远程服务器下载目录的时候 finally 里的 close 我给注释掉了,因为我认为下载目录不需要每个文件都执行重连,不是很规范,这个你自己调整吧。
当然里面还有一些本地代码,你想办法自己定义一下,或者删除掉就好了,这代码里的本地和远程,代指的是当前系统的服务器 (本地),测试服务器 (远程)。
(如果这里面涉及到下载文件的安全问题啥的,可以自己调一下)
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.administrator.platform.exception.base.BusinessValidationException;
import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.SCPClient;
import ch.ethz.ssh2.SCPInputStream;
import ch.ethz.ssh2.SCPOutputStream;
import ch.ethz.ssh2.SFTPv3Client;
import ch.ethz.ssh2.SFTPv3DirectoryEntry;
import ch.ethz.ssh2.SFTPv3FileAttributes;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;
public class GanymedSshClient {
    private static GanymedSshClient instance;
    private Session session;
    private ServerInfo serverInfo;
    private Connection connection;
    private SCPClient scpClient;
    private SFTPv3Client sftPv3Client;
    private static final String MODE = "0644";
    private static final int MKDIR_POSIX_PERMISSION = 0755;
    private static final long LENGTH = 1024;
    private static final String REMOTE_PATH_SEPARATOR = "/";
    private static final String NO_SUCH_FILE_ERROR_MESSAGE = "No such file (SSH_FX_NO_SUCH_FILE:";
    private final List<SFTPv3DirectoryEntry> childrenList = new ArrayList<>();
    private static final Logger LOGGER = LoggerFactory
            .getLogger(GanymedSshClient.class);
    private GanymedSshClient() {
    }
    public Session getSession() {
        return session;
    }
    public void setSession(Session session) {
        this.session = session;
    }
    public ServerInfo getServerInfo() {
        return serverInfo;
    }
    public void setServerInfo(ServerInfo serverInfo) {
        this.serverInfo = serverInfo;
    }
    public GanymedSshClient(ServerInfo serverInfo) {
        this.serverInfo = serverInfo;
    }
    public GanymedSshClient(String host, int port, String username,
            String password) {
        this.serverInfo = new ServerInfo(host, port, username, password);
    }
    public GanymedSshClient(String host, String username, String password) {
        this.serverInfo = new ServerInfo(host, username, password);
    }
    public GanymedSshClient(String host) {
        this.serverInfo = new ServerInfo(host);
    }
    /**
     * 初始化session
     * 
     * @see :
     * @param :
     * @return : void
     */
    private void initConnection() {
        if (null == this.connection
                || !this.connection.isAuthenticationComplete()) {
            this.connection = new Connection(this.serverInfo.getHost());
            LOGGER.debug("开始初始化连接");
            try {
                this.connection.connect();
                boolean authed = connection.authenticateWithPassword(
                        this.serverInfo.getUsername(),
                        this.serverInfo.getPassword());
                if (!authed) {
                    throw new BusinessValidationException("初始化ssh连接失败,认证异常");
                }
                LOGGER.debug("CONNECTION 初始化完成");
            } catch (IOException e) {
                LOGGER.error("初始化连接失败,失败原因:{}", e.getMessage());
                throw new BusinessValidationException("初始化连接失败");
            }
        }
    }
    private Connection getConnection() {
        initConnection();
        return this.connection;
    }
    /**
     * 初始化session
     * 
     * @see :
     * @param :
     * @return : void
     */
    private void initSession() {
        if (null == this.session) {
            LOGGER.debug("初始化session");
            try {
                this.session = getConnection().openSession();
                if (null != this.session) {
                    LOGGER.debug("session初始化完成");
                }
            } catch (IOException e) {
                LOGGER.error("获取会话失败,失败原因:{}", e.getMessage());
            }
        }
    }
    private Session getCurrentSession() {
        initSession();
        return this.session;
    }
    private SCPClient getScpClient() {
        if (null == this.scpClient) {
            this.scpClient = new SCPClient(getConnection());
        }
        return this.scpClient;
    }
    private void close() {
        if (null != this.connection) {
            this.connection.close();
            this.connection = null;
            this.scpClient = null;
        }
        if (null != this.session) {
            this.session.close();
            this.session = null;
        }
        if (null != this.sftPv3Client) {
            this.sftPv3Client.close();
            this.sftPv3Client = null;
        }
    }
    /**
     * 上传文件
     * 
     * @see :
     * @param :
     * @return : void
     * @param localFile
     * @param remoteFileName
     * @param remoteFolder
     */
    public void uploadFile(String localFile, String remoteFolder) {
        LOGGER.debug("上传文件:{},到:{}", localFile, remoteFolder);
        try (SCPOutputStream os = getScpOutputStream(localFile, remoteFolder);
                FileInputStream fis = new FileInputStream(localFile);) {
            byte[] b = new byte[4096];
            int i;
            while ((i = fis.read(b)) != -1) {
                os.write(b, 0, i);
            }
            os.flush();
        } catch (IOException e) {
            LOGGER.error("scp 上传文件失败:{}", e.getMessage());
            throw new BusinessValidationException("上传文件失败");
        } finally {
            close();
        }
    }
    /**
     * 获取文件输出流失败
     * 
     * @see :
     * @param :
     * @return : SCPOutputStream
     * @param localFile
     * @param remoteFolder
     * @return
     */
    private SCPOutputStream getScpOutputStream(String localFile,
            String remoteFolder) {
        try {
            File file = new File(localFile);
            return getScpClient().put(file.getName(), file.length(),
                    remoteFolder, MODE);
        } catch (IOException e) {
            LOGGER.error("获取文件输出流失败:{}", e.getMessage());
            throw new BusinessValidationException("获取文件输出流失败");
        }
    }
    /**
     * 批量上传文件
     * 
     * @see :
     * @param :
     * @return : void
     * @param localFiles
     * @param remoteFolder
     */
    public void uploadFiles(String[] localFiles, String remoteFolder) {
        for (String localFile : localFiles) {
            File thisFile = new File(localFile);
            if (thisFile.exists() && thisFile.isDirectory()) {
                File[] files = thisFile.listFiles();
                String dirName = remoteFolder + REMOTE_PATH_SEPARATOR
                        + thisFile.getName();
                mkdir(dirName);
                uploadFiles(files, dirName);
            } else if (thisFile.exists() && thisFile.isFile()) {
                uploadFile(localFile, remoteFolder);
            }
        }
    }
    /**
     * 上传本地文件到远程服务器端,即将本地的文件localFile上传到远程Linux服务器中的remoteTargetDirectory目录下
     * 
     * @param localFileList
     * @param remoteTargetDirectory
     */
    public void uploadFiles(List<String> localFileList,
            String remoteTargetDirectory) {
        uploadFiles(localFileList.toArray(new String[] {}),
                remoteTargetDirectory);
    }
    /**
     * 上传本地文件到远程服务器端,即将本地的文件localFile上传到远程Linux服务器中的remoteTargetDirectory目录下
     * 
     * @param localFileList
     * @param remoteTargetDirectory
     */
    public void uploadFiles(File[] localFileList,
            String remoteTargetDirectory) {
        String[] filePaths = new String[localFileList.length];
        for (int i = 0; i < localFileList.length; i++) {
            filePaths[i] = localFileList[i].getAbsolutePath();
        }
        uploadFiles(filePaths, remoteTargetDirectory);
    }
    /**
     * 上传文件
     * 
     * @see :
     * @param :
     * @return : void
     * @param localFolder
     * @param remoteFolder
     */
    public void uploadFolder(String localFolder, String remoteFolder) {
        File localFileFolder = new File(localFolder);
        File[] files = localFileFolder.listFiles();
        uploadFiles(files, remoteFolder);
    }
    /**
     * 下载单个文件
     * 
     * @see :
     * @param :
     * @return : void
     * @param remoteFile
     * @param destFile
     */
    public boolean downloadFile(String remoteFile, String remoteDir,
            String destFileFolder) {
        File destFile = new File(destFileFolder);
        if (!destFile.exists()) {
            destFile.mkdirs();
        }
        String localFile = remoteFile;
        /**
         * 2019年8月12日 20:38:22 modified by 孙留平
         * 
         * @see: 当远程文件中有$符号的时候,会被转义,因此远程的时候,需要换上传义字符,尤其是java的内部类被编译出来的class都是带有$符号的
         * 
         */
        if (remoteFile.contains("$")) {
            remoteFile = remoteFile.replace("$", "\\$");
        }
        try (SCPInputStream scpInputStream = getRemoteFileInputStream(
                remoteFile, remoteDir);
                FileOutputStream fos = new FileOutputStream(
                        new File(destFileFolder, localFile));) {
            String message = String.format("下载文件:%s/%s,到:%s", remoteDir,
                    remoteFile, destFileFolder);
            byte[] b = new byte[4096];
            int i;
            while ((i = scpInputStream.read(b)) != -1) {
                fos.write(b, 0, i);
            }
            fos.flush();
            LOGGER.debug("{}成功", message);
            return true;
        } catch (IOException e) {
            LOGGER.error("下载文件失败,原因:{}", e.getMessage());
            return false;
        }
        // finally {
        // close();
        // }
    }
    /**
     * 获取远程流
     * 
     * @see :
     * @param :
     * @return : SCPInputStream
     * @param remoteFile
     * @param remoteDir
     * @return
     */
    private SCPInputStream getRemoteFileInputStream(String remoteFile,
            String remoteDir) {
        SCPInputStream scpInputStream;
        try {
            scpInputStream = getScpClient()
                    .get(remoteDir + REMOTE_PATH_SEPARATOR + remoteFile);
            return scpInputStream;
        } catch (IOException e) {
            LOGGER.error("获取目录:{}下的文件:{}流失败,失败原因{}", remoteDir, remoteFile,
                    e.getMessage());
            throw new BusinessValidationException("获取输入流失败");
        }
    }
    /**
     * 下载单个文件
     * 
     * @see :
     * @param :
     * @return : void
     * @param remoteFiles
     * @param destFile
     */
    public boolean downloadFiles(String[] remoteFiles, String remoteFileFolder,
            String destFileFolder) {
        File destFile = new File(destFileFolder);
        if (destFile.isFile()) {
            destFileFolder = destFile.getParent();
        }
        for (String string : remoteFiles) {
            downloadFile(string, remoteFileFolder, destFileFolder);
        }
        return true;
    }
    /**
     * 下载目录
     * 
     * @see :
     * @param :
     * @return : void
     * @param remoteFileFolder
     * @param destFileFolder
     */
    public void downloadFolder(String remoteFileFolder, String destFileFolder) {
        // listRemoteDir(remoteFileFolder);
        List<SFTPv3DirectoryEntry> descendantsFiles = getChildren(
                remoteFileFolder);
        File destFolderFile = new File(destFileFolder);
        if (!destFolderFile.exists()) {
            destFolderFile.mkdirs();
        }
        LOGGER.debug("开始下载:从{}到:{}", remoteFileFolder, destFileFolder);
        for (SFTPv3DirectoryEntry sftPv3DirectoryEntry : descendantsFiles) {
            // 如果是文件夹,则下载文件夹
            if (sftPv3DirectoryEntry.attributes.isDirectory()) {
                downloadFolder(
                        remoteFileFolder + REMOTE_PATH_SEPARATOR
                                + sftPv3DirectoryEntry.filename,
                        destFileFolder + File.separator
                                + sftPv3DirectoryEntry.filename);
            } else if (sftPv3DirectoryEntry.attributes.isRegularFile()) {
                downloadFile(sftPv3DirectoryEntry.filename, remoteFileFolder,
                        destFileFolder);
            }
        }
    }
    /**
     * 下载目录,从远程数组,到本地文件夹,意思把数组中的远程目录都下载到同一个文件夹下,小心文件覆盖
     * 
     * @see :
     * @param :
     * @return : void
     * @param remoteFileFolder
     * @param destFileFolder
     */
    public void downloadFolders(String[] remoteFileFolder,
            String destFileFolder) {
        for (String string : remoteFileFolder) {
            downloadFolder(string, destFileFolder);
        }
    }
    /**
     * 下载目录,从远程数组、到本地数组,一一对应
     * 
     * @see :
     * @param :
     * @return : void
     * @param remoteFileFolder
     * @param destFileFolder
     */
    public void downloadFolders(String[] remoteFileFolder,
            String[] destFileFolder) {
        if (null == remoteFileFolder || null == destFileFolder) {
            throw new BusinessValidationException("远程文件夹和本地文件夹都不能为null");
        }
        if (remoteFileFolder.length == 0 || destFileFolder.length == 0) {
            throw new BusinessValidationException("远程文件夹和本地文件夹都不能为空");
        }
        if (remoteFileFolder.length != destFileFolder.length) {
            throw new BusinessValidationException("远程文件夹数组长度,和本地文件夹数组长度,必须得一样");
        }
        for (int i = 0; i < destFileFolder.length; i++) {
            downloadFolder(remoteFileFolder[i], destFileFolder[i]);
        }
    }
    /**
     * 执行命令
     * 
     * @see :
     * @param :
     * @return : void
     * @param command
     */
    public String executeShell(String command) {
        LOGGER.debug("执行命令:{}", command);
        Session currentSession = getCurrentSession();
        try (InputStream stdStream = new StreamGobbler(
                currentSession.getStdout());
                InputStream stdErrStream = new StreamGobbler(
                        currentSession.getStderr());
                BufferedReader bReader = new BufferedReader(
                        new InputStreamReader(stdStream));
                BufferedReader bufferedErrorReader = new BufferedReader(
                        new InputStreamReader(stdErrStream))) {
            currentSession.execCommand(command);
            StringBuilder outputBuilder = new StringBuilder();
            String line = null;
            while (true) {
                line = bReader.readLine();
                if (null == line) {
                    break;
                }
                outputBuilder.append(line).append("\n");
            }
            String errorLine = null;
            while ((errorLine = bufferedErrorReader.readLine()) != null) {
                outputBuilder.append(errorLine).append("\n");
            }
            LOGGER.debug("命令执行结果:\n{}", outputBuilder);
            return outputBuilder.toString();
        } catch (IOException e1) {
            LOGGER.error("执行命令失败:{}", e1.getMessage());
            throw new BusinessValidationException("执行命令失败");
        } finally {
            close();
        }
    }
    /**
     * 删除远程文件或者目录
     * 
     * @see :
     * @param :
     * @return : void
     * @param remoteFileOrFolder
     */
    public boolean deleteRemoteFile(String remoteFile) {
        try {
            LOGGER.debug("尝试删除文件:{}", remoteFile);
            getSftpV3Client().rm(remoteFile);
            LOGGER.debug("删除文件成功:{}", remoteFile);
            return true;
        } catch (IOException e) {
            LOGGER.error("删除文件失败:{}", e.getMessage());
            if (e.getMessage().contains(NO_SUCH_FILE_ERROR_MESSAGE)) {
                LOGGER.debug("文件夹不存在,不需要删除");
                return true;
            }
            return false;
        } finally {
            close();
        }
    }
    /**
     * 删除远程文件或者目录
     * 
     * @see :
     * @param :
     * @return : void
     * @param remoteFileOrFolder
     */
    public boolean deleteRemoteFile(String[] remoteFiles) {
        try {
            LOGGER.debug("尝试删除一组文件");
            for (String remoteFile : remoteFiles) {
                getSftpV3Client().rm(remoteFile);
                LOGGER.debug("删除文件成功:{}", remoteFile);
            }
            LOGGER.debug("批量删除文件成功");
            return true;
        } catch (IOException e) {
            LOGGER.error("删除文件失败:{}", e.getMessage());
            if (e.getMessage().contains(NO_SUCH_FILE_ERROR_MESSAGE)) {
                LOGGER.debug("文件不存在,不需要删除");
                return true;
            }
            return false;
        } finally {
            close();
        }
    }
    /**
     * 删除远程目录
     * 
     * @see :
     * @param :
     * @return : void
     * @param remoteFileOrFolder
     */
    public boolean deleteRemoteFileFolder(String remoteFileFolder) {
        try {
            LOGGER.debug("尝试删除文件夹:{}", remoteFileFolder);
            getSftpV3Client().rmdir(remoteFileFolder);
            LOGGER.debug("删除文件夹成功:{}", remoteFileFolder);
            return true;
        } catch (IOException e) {
            LOGGER.error("删除文件夹失败:{}", e.getMessage());
            if (e.getMessage().contains(NO_SUCH_FILE_ERROR_MESSAGE)) {
                LOGGER.debug("文件夹不存在,不需要删除");
                return true;
            }
            if (e.getMessage().contains("Failure (SSH_FX_FAILURE:")) {
                return deleteNoneEmptyRemoteFolder(remoteFileFolder);
            }
            return false;
        } finally {
            close();
        }
    }
    /**
     * 删除非空文件夹
     * 
     * @see :
     * @param :
     * @return : boolean
     * @param remoteFolder
     * @return
     */
    private boolean deleteNoneEmptyRemoteFolder(String remoteFolder) {
        LOGGER.debug("删除非空文件夹:{}", remoteFolder);
        String command = "rm -rf " + remoteFolder;
        String deleteNoneEmptyFolder = executeShell(command);
        LOGGER.debug("删除非空文件夹结果:{}", deleteNoneEmptyFolder);
        return true;
    }
    /**
     * 在远端linux上创建文件夹
     * 
     * @param dirName
     *            文件夹名称
     * @param posixPermissions
     *            目录或者文件夹的权限
     */
    public boolean mkdir(String dirName, int posixPermissions) {
        try {
            LOGGER.debug("创建文件夹:{}", dirName);
            getSftpV3Client().mkdir(dirName, posixPermissions);
            LOGGER.debug("创建文件夹:{}成功", dirName);
            return true;
        } catch (IOException e) {
            LOGGER.error("创建文件夹失败:{}", e.getMessage());
            return false;
        }
    }
    /**
     * 在远端linux上创建文件夹
     * 
     * @param dirName
     *            文件夹名称
     */
    public boolean mkdir(String dirName) {
        return mkdir(dirName, MKDIR_POSIX_PERMISSION);
    }
    /**
     * 在远程Linux服务器端移动文件或者文件夹到新的位置
     * 
     * @param oldPath
     * @param newPath
     */
    public boolean moveFileOrDir(String oldPath, String newPath) {
        try {
            LOGGER.debug("把文件或者文件夹从:{},移动到:{}", oldPath, newPath);
            getSftpV3Client().mv(oldPath, newPath);
            LOGGER.debug("把文件或者文件夹从:{},移动到:{}成功", oldPath, newPath);
            return true;
        } catch (Exception e) {
            LOGGER.error("移动文件失败:从何{}到:{}", oldPath, newPath);
            return false;
        }
    }
    /**
     * 获取sftpclient
     * 
     * @see :
     * @param :
     * @return : SFTPv3Client
     * @return
     */
    private SFTPv3Client getSftpV3Client() {
        try {
            if (null == this.sftPv3Client) {
                LOGGER.debug("初始化SFTPv3Client");
                this.sftPv3Client = new SFTPv3Client(getConnection());
                LOGGER.debug("初始化SFTPv3Client完成");
            }
            return this.sftPv3Client;
        } catch (IOException e) {
            LOGGER.error("获取SFTPv3Client失败,失败原因:{}", e.getMessage());
            throw new BusinessValidationException("获取SFTPv3Client失败");
        }
    }
    /**
     * 列举远程目录文件
     * 
     * @see :
     * @param :
     * @return : List<File>
     * @param remoteDir
     * @return
     */
    private void listRemoteDir(String remoteDir) {
        List<SFTPv3DirectoryEntry> children = null;
        try {
            children = getSftpV3Client().ls(remoteDir);
            if (children.isEmpty()) {
                return;
            }
            Iterator iterator = children.iterator();
            while (iterator.hasNext()) {
                SFTPv3DirectoryEntry thisChild = (SFTPv3DirectoryEntry) iterator
                        .next();
                SFTPv3FileAttributes attributes = thisChild.attributes;
                if (!".".equals(thisChild.filename)
                        && !"..".equals(thisChild.filename)) {
                    String childFolder = remoteDir + "/" + thisChild.filename;
                    if (attributes.isDirectory()) {
                        listRemoteDir(childFolder);
                    }
                    this.childrenList.add(thisChild);
                }
            }
        } catch (IOException e) {
            LOGGER.error("获取子孙文件或者文件夹失败:{}", e.getMessage());
            throw new BusinessValidationException("获取文件夹下的内容失败");
        }
    }
    /**
     * 列举远程目录文件
     * 
     * @see :
     * @param :
     * @return : List<File>
     * @param remoteDir
     * @return
     */
    private List<SFTPv3DirectoryEntry> getChildren(String remoteDir) {
        List<SFTPv3DirectoryEntry> children = null;
        List<SFTPv3DirectoryEntry> finalChildren = new ArrayList<>();
        try {
            children = getSftpV3Client().ls(remoteDir);
            Iterator iterator = children.iterator();
            while (iterator.hasNext()) {
                SFTPv3DirectoryEntry thisChild = (SFTPv3DirectoryEntry) iterator
                        .next();
                if (!".".equals(thisChild.filename)
                        && !"..".equals(thisChild.filename)) {
                    finalChildren.add(thisChild);
                }
            }
            return finalChildren;
        } catch (IOException e) {
            LOGGER.error("获取子孙文件或者文件夹失败:{}", e.getMessage());
            throw new BusinessValidationException("获取文件夹下的内容失败");
        }
        // finally {
        // close();
        // }
    }
    /**
     * 单例模式
     * 懒汉式
     * 线程安全
     * 
     * @return
     */
    public static GanymedSshClient getInstance() {
        if (null == instance) {
            synchronized (GanymedSshClient.class) {
                if (null == instance) {
                    instance = new GanymedSshClient();
                }
            }
        }
        return instance;
    }
    /**
     * 获取实例
     * 
     * @see :
     * @param :
     * @return : GanymedSshClient
     * @param ip
     * @param port
     * @param name
     * @param password
     * @return
     */
    public static GanymedSshClient getInstance(String ip, int port, String name,
            String password) {
        if (null == instance) {
            synchronized (GanymedSshClient.class) {
                if (null == instance) {
                    instance = new GanymedSshClient(ip, port, name, password);
                }
            }
        }
        return instance;
    }
    /**
     * 获取实例
     * 
     * @see :
     * @param :
     * @return : GanymedSshClient
     * @param ip
     * @param port
     * @param name
     * @param password
     * @return
     */
    public static GanymedSshClient getInstance(String ip, String name,
            String password) {
        if (null == instance) {
            synchronized (GanymedSshClient.class) {
                if (null == instance) {
                    instance = new GanymedSshClient(ip, name, password);
                }
            }
        }
        return instance;
    }
    /**
     * 获取实例
     * 
     * @see :
     * @param :
     * @return : GanymedSshClient
     * @param ip
     * @param port
     * @param name
     * @param password
     * @return
     */
    public static GanymedSshClient getInstance(String ip) {
        if (null == instance) {
            synchronized (GanymedSshClient.class) {
                if (null == instance) {
                    instance = new GanymedSshClient(ip);
                }
            }
        }
        return instance;
    }
    /**
     * 获取实例
     * 
     * @see :
     * @param :
     * @return : GanymedSshClient
     * @param ip
     * @param port
     * @param name
     * @param password
     * @return
     */
    public static GanymedSshClient getInstance(ServerInfo serverInfo) {
        if (null == instance) {
            synchronized (GanymedSshClient.class) {
                if (null == instance) {
                    instance = new GanymedSshClient(serverInfo);
                }
            }
        }
        return instance;
    }
    /**
     * 判断服务器是否可认证
     * 
     * @see :
     * @param :
     * @return : boolean
     * @return
     */
    public boolean serverCanBeAuthed() {
        try {
            initConnection();
            return true;
        } catch (Exception e) {
            LOGGER.error("认证授权失败,原因:{}", e.getMessage());
            return false;
        }
    }
    /**
     * 判断服务器是否可连通
     * 
     * @see :
     * @param :
     * @return : boolean
     * @return
     */
    public boolean serverIpCanBeConnected() {
        try {
            return InetAddress.getByName(this.serverInfo.getHost())
                    .isReachable(3000);
        } catch (UnknownHostException e) {
            LOGGER.error("测试连接服务器失败,失败原因:{}", e.getMessage());
            return false;
        } catch (IOException e) {
            LOGGER.error("连接服务器IO异常,失败原因:{}", e.getMessage());
            return false;
        }
    }
    public static void main(String[] args) {
        GanymedSshClient ganymedSshClient = new GanymedSshClient(
                "192.168.110.31", "admin", "admin");
        String destFolder = "E:\\test3\\tq-datamanagement";
        ganymedSshClient.downloadFolder(
                "/home/admin/codecoverge/tq-datamanagement", destFolder);
    }
}