• 哈哈哈,没绷住,笑了,仿佛看到了自己

  • 就从来没这个仪式 你是不是开心多了

  • 拿下,等调货,x 东居然没有现货,差评~

  • 别提了,某直拒上还跟你说话的基本就剩下华为的 OD 了

  • 郑州啊,好想回去~~~~ 大佬这是你的公司?

  • 1, 4 楼正解,主要在团队里工作,需要向团队整体规划倾斜,哪个会的多,可能选型更偏向哪个,毕竟项目合作,不可能选一个只有你会的语言,这个没办法。

    当然我提倡可以都学都会,万一 (没有万一,因为我这里基本不会给你机会脱离大趋势) 有机会用你喜欢的呢。。~~

  • 羡慕,终我半辈子都没有碰到的机会,你们还有挑的余地😂

  • 这是已经恢复了吗? at 2022年01月05日

    大半年没来了,趁着社区恢复,打个卡

  • 我把社区的网站清除了缓存,重新进来好了,不知道是不是啥时候误点了哪里,哈哈。

    这道题两个月前刚做过,我大概知道作者是去了哪个公司的碰到了笔试题😂

  • testerhome 系统字体都变繁体了吗,还是一直都是繁体,而我才刚发现😂

  • 好的,祝你好运

  • 你好像没有弄清楚它的工作原理吧,你这个命令只是起一个 java 服务的,与以前不一样的是,你起这个 java 服务的时候,注入了 jacoco 的代理,让你有对这个 java 服务统计代码执行状况的能力,后续的测试过程和生成报告是独立的哦

  • 换一下 jackson 的版本试试

  • 看你说的一堆问题,那就一个一个解决么,问题这种东西,解决一个少一个,干掉一个轻松一下。
    回答你的第一个问题:总结为代码比对的问题。
    差异化的覆盖,大致可以在两个阶段做处理,我提一些我的思路,看能不能对你有一点帮助。

    • 在 jacoco 自己的生成报告之前。

    你去过滤 class 的信息,选择性生成报告,这样你可以只要那些过滤出来的变更代码的数据,原来的数据可以丢掉。当然需要你对 jacoco 自己的机制要清楚。

    • 在 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);
        }
    }
    
    
  • 应该是最好独立的,只需要提供 class 文件就可以解析

  • 这个没明白具体什么意思,可以描述清晰一些?或者直接上图~

  • 你这样做的目的是什么呢?
    如果是单项目多工程,那一般也只有一个属于启动项目,其他属于 jar 包引入,这时候需要一个 jacoco 服务也就够了;
    如果是多项目多工程,那项目之间也不需要组合统计吧?一个 jacoco 服务,对着多个系统服务,即使能启动起来不报端口占用错误,那也没什么太大意义吧。
    你统计数据的时候还是要按照不同的项目或者不同的系统服务来分开,但是一个 jacoco 的话,多个系统的东西都堆到这一个服务里面,覆盖率的执行数据也是同一时间保存的,那不是纯粹增加解析的工作量么。

    难道仅仅是为了给服务器节省一个 jacoco 服务的启动端口?

    不知道我有没有说明白~~

    (关于端口占用的问题,他只是启动的时候一个提示,你可以关注一下即使报了这个端口占用错误,系统还是不时能起来的,我印象中这个不影响应用的启动,只是显示启动这个 jacoco 的 tcp 服务失败,但实际上这个 jacoco 服务 (就是之前被另一个系统启动起来的那个) 能不能统计你这个新系统的覆盖数据,你可以尝试一下,看看拉去出来的覆盖率数据有没有你的新系统的代码,这个底层细节我没有深入去看。)

  • 当然可以

  • 你这个应该问题不大吧,即使你用脚本启动,也可以给 shell 脚本传端口参数吧。
    不太清楚你 jenkins 去打包部署的时候是怎么做的,如果同一个项目起多个服务,那你用 jenkins 做也是 java 命令后面跟-Dspring.profile.active 这个参数或者是直接指定 port 吧,那应该直接把 jacoco 的配置传过去应该是类似的?

    就是不知道我有没有准确理解你的意图

  • 咦,书都到我手里一周了 ,你现在才来文章😂
    在里面看到了茹老师和恒捷兄的推荐序哈哈,祝一切安好!!!

  • 妥。我修修