Jenkins CLI二次开发工具类

使用Jenkins CLI进行二次开发

使用背景

公司自研CI/DI平台,借助Jenkins+SonarQube进行代码质量管理。

对接版本

Jenkins版本为:Version 2.428
SonarQube版本为:Community EditionVersion 10.2.1 (build 78527)

技术选型

Java对接Jenkins有第三方组件,比如jenkins-rest、jenkins-client,但是考虑到第三方组件会引入其他jar包,而且会存在漏洞问题。
到时候升级组件时可能会和项目框架本身使用的第三方jar起冲突。因此,使用jenkins-cli来实现自己的需求。

注意:这里不是单纯的使用java -jar jenkins-cli.jar -http xxxx来实现接口调用,
而且提取并修改jenkins-cli.jar中的源码来达到自身需求开发的目的。
比如创建View时,使用jenkins-cli.jar时需要传入xml内容(标准输入流),
所以我们进行了改造,可以支持传入byte数组的形式,使用体验更符合大众。

jenkins-cli.jar的源码修改

java 复制代码
package com.infosec.autobuild.util;

import com.cdancy.jenkins.rest.JenkinsClient;
import com.cdancy.jenkins.rest.domain.job.BuildInfo;
import com.cdancy.jenkins.rest.domain.queue.QueueItem;
import com.infosec.autobuild.dto.*;
import com.infosec.autobuild.dto.jenkins.BuildInfoDto;
import com.infosec.autobuild.dto.jenkins.CredentialDto;
import com.infosec.autobuild.dto.jenkins.JobDto;
import com.infosec.autobuild.dto.jenkins.ViewDto;
import com.infosec.autobuild.hudson.cli.CLI;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Jenkins cli工具类
 */
@Slf4j
public class JenkinsUtil {

    private static final String PROTOCOL_HTTP = "-http";
    private static final String PROTOCOL_WEB_SOCKET = "-webSocket";
    /**
     * 结束符:\r\n
     */
    private static final String END_SYMBOL = "\\r\\n";
    /**
     * 换行符:\n
     */
    private static final String LINE_BREAK_SYMBOL = "\\n";

    /**
     * Jenkins 凭证相关操作(暂不包括domain相关操作)
     */
    public static class Credentials {
        /**
         * system::system::jenkins等同于SystemCredentialsProvider::SystemContextResolver::jenkins
         * Provider可以通过java -jar jenkins-cli.jar -auth admin:admin -s http://xxx:8080/ -http list-credentials-providers获取
         * Resolver可以通过java -jar jenkins-cli.jar -auth admin:admin -s http://xxx:8080/ -http list-credentials-context-resolvers获取
         */
        private static final String SYSTEM_STORE_ID = "system::system::jenkins";
        /**
         * 默认全局domain
         */
        private static final String GLOBAL_DOMAIN = "(global)";

        private static final String CREDENTIAL_NOT_FOUND = "No such credential";

        enum Operation {
            /**
             * 查询Jenkins Credentials集合:list-credentials
             */
            LIST_CREDENTIALS("list-credentials", "查询Jenkins Credentials集合"),
            /**
             * 查询Jenkins Credentials集合,返回xml:list-credentials-as-xml
             */
            LIST_CREDENTIALS_AS_XML("list-credentials-as-xml", "查询Jenkins Credentials集合,返回xml"),
            /**
             * 创建Jenkins Credentials:create-credentials-by-xml
             */
            CREATE_CREDENTIALS("create-credentials-by-xml", "创建Jenkins Credentials"),
            /**
             * 更新Jenkins Credentials:create-credentials-by-xml
             */
            UPDATE_CREDENTIALS("update-credentials-by-xml", "更新Jenkins Credentials"),
            /**
             * 删除Jenkins Credentials:delete-credentials
             */
            DELETE_CREDENTIALS("delete-credentials", "删除Jenkins Credentials"),
            /**
             * 查询Jenkins Credentials:get-credentials-as-xml
             */
            GET_CREDENTIALS("get-credentials-as-xml", "查询Jenkins Credentials"),

            ;
            @Setter
            @Getter
            private String op;
            @Setter
            @Getter
            private String desc;

            Operation(String op, String desc) {
                this.setOp(op);
                this.setDesc(desc);
            }
        }

        /**
         * 创建Jenkins 凭证(凭证id为空时,默认使用36位UUID)
         *
         * @param host          Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth          Jenkins登录用户名:密码,比如:admin:admin
         * @param credentialDto 凭证对象
         * @return ResultDto
         */
        public static ResultDto createCredential(String host, String auth, CredentialDto credentialDto) {
            if (!StringUtils.hasText(credentialDto.getId())) {
                credentialDto.setId(UUID.randomUUID().toString());
            }
            String xml = CredentialDto.parseDto2Xml(credentialDto);
            CLI cli = doWork(host, auth, new String[]{Operation.CREATE_CREDENTIALS.op, SYSTEM_STORE_ID, ""}, PROTOCOL_HTTP, xml);
            int code = cli.code;
            String msg = cli.msg;
            return ResultDto.SUCCESS.equals(String.valueOf(code)) ?
                    ResultDto.buildSuccessDto() : ResultDto.buildErrorDto().msg(msg);
        }

        /**
         * 删除Jenkins 凭证
         *
         * @param host         Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth         Jenkins登录用户名:密码,比如:admin:admin
         * @param credentialId 凭证id
         * @return ResultDto
         */
        public static ResultDto deleteCredential(String host, String auth, String credentialId) {
            CLI cli = doWork(host, auth,
                    new String[]{Operation.DELETE_CREDENTIALS.op, SYSTEM_STORE_ID, "", credentialId},
                    PROTOCOL_HTTP, null);
            int code = cli.code;
            String msg = cli.msg;
            if (StringUtils.hasText(msg) && msg.contains(CREDENTIAL_NOT_FOUND)) {
                return ResultDto.buildSuccessDto();
            }
            return ResultDto.SUCCESS.equals(String.valueOf(code)) ?
                    ResultDto.buildSuccessDto() : ResultDto.buildErrorDto().msg(msg);
        }

        /**
         * 更新Jenkins 凭证(凭证id无法修改)
         *
         * @param host          Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth          Jenkins登录用户名:密码,比如:admin:admin
         * @param credentialDto 凭证对象
         * @return ResultDto
         */
        public static ResultDto updateCredential(String host, String auth, CredentialDto credentialDto) {
            if (!StringUtils.hasText(credentialDto.getId())) {
                return ResultDto.buildErrorDto().msg("凭证id不能为空");
            }
            String xml = CredentialDto.parseDto2Xml(credentialDto);
            CLI cli = doWork(host, auth, new String[]{Operation.UPDATE_CREDENTIALS.op, SYSTEM_STORE_ID,
                    GLOBAL_DOMAIN, credentialDto.getId()}, PROTOCOL_HTTP, xml);
            int code = cli.code;
            String msg = cli.msg;
            if (StringUtils.hasText(msg) && msg.contains(CREDENTIAL_NOT_FOUND)) {
                msg = "根据凭证id未找到对应记录";
                return ResultDto.buildErrorDto().msg(msg);
            }
            return ResultDto.SUCCESS.equals(String.valueOf(code)) ?
                    ResultDto.buildSuccessDto() : ResultDto.buildErrorDto().msg(msg);
        }

        /**
         * 查询Jenkins 凭证
         *
         * @param host         Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth         Jenkins登录用户名:密码,比如:admin:admin
         * @param credentialId 凭证id
         * @return ResultDto
         */
        public static ResultDto<CredentialDto> getCredential(String host, String auth, String credentialId) {
            CLI cli = doWork(host, auth,
                    new String[]{Operation.GET_CREDENTIALS.op, SYSTEM_STORE_ID, "", credentialId},
                    PROTOCOL_HTTP, null);
            int code = cli.code;
            String msg = cli.msg;
            if (StringUtils.hasText(msg) && msg.contains(CREDENTIAL_NOT_FOUND)) {
                msg = "根据凭证id未找到对应记录";
                return ResultDto.buildErrorDto().msg(msg);
            }
            boolean ifSuccess = ResultDto.SUCCESS.equals(String.valueOf(code));
            CredentialDto credentialDto = new CredentialDto();
            if (ifSuccess) {
                if (StringUtils.hasText(msg)) {
                    CredentialDto.parseXmlStr2Dto(credentialDto, msg, null);
                }
            }
            return ifSuccess ? ResultDto.buildSuccess(credentialDto) : ResultDto.buildError(credentialDto).msg(msg);
        }

        /**
         * 查询Jenkins上的凭证列表
         *
         * @param host Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth Jenkins登录用户名:密码,比如:admin:admin
         * @return ResultDto
         */
        public static ResultDto<List<CredentialDto>> listCredentials(String host, String auth) {
            CLI cli = doWork(host, auth, new String[]{Operation.LIST_CREDENTIALS_AS_XML.op, SYSTEM_STORE_ID},
                    PROTOCOL_HTTP, null);
            int code = cli.code;
            String msg = cli.msg;
            List<CredentialDto> credentialDtoList = new ArrayList<>(10);
            boolean ifSuccess = ResultDto.SUCCESS.equals(String.valueOf(code));
            if (ifSuccess) {
                CredentialDto.parseXmlStr2List(credentialDtoList, msg);
            }
            return ifSuccess ? ResultDto.buildSuccess(credentialDtoList) : ResultDto.buildError(credentialDtoList).msg(msg);
        }

        /**
         * 查询Jenkins上的凭证列表
         *
         * @param ip       Jenkins服务IP
         * @param port     Jenkins服务端口
         * @param username Jenkins服务登录用户名
         * @param password Jenkins服务登录密码
         * @return ResultDto
         */
        public static ResultDto listCredentials(String ip, String port, String username, String password) {
            String host = "http://" + ip + ":" + port;
            String auth = username + ":" + password;
            return listCredentials(host, auth);
        }
    }

    /**
     * Jenkins Job相关操作(即Item)
     */
    public static class Jobs {

        private static String JOB_BUILD_DETAIL_URL = "@host/job/@jobName/@buildId/api/json";

        private static String QUEUE_LIST_URL = "@host/job/@jobName/@buildId/api/json";

        enum Operation {
            /**
             * 创建Jenkins Job
             */
            CREATE("create-job", "创建Jenkins Job"),
            /**
             * 复制Jenkins Job
             */
            COPY("copy-job", "复制Jenkins Job"),
            /**
             * 更新Jenkins Job
             */
            UPDATE("update-job", "更新Jenkins Job"),
            /**
             * 删除Jenkins Job
             */
            DELETE("delete-job", "删除Jenkins Job"),
            /**
             * 构建Jenkins Job
             */
            BUILD("build", "构建Jenkins Job"),
            /**
             * Jenkins Job构建日志输出
             */
            CONSOLE("console", "Jenkins Job构建日志输出"),
            /**
             * 查询Jenkins Job集合
             */
            LIST("list-jobs", "查询Jenkins Job集合"),
            /**
             * 查询Jenkins Job
             */
            GET("get-job", "查询Jenkins Job"),
            /**
             * 将Jenkins Job添加到视图中
             */
            ADD_TO_VIEW("add-job-to-view", "将Jenkins Job添加到视图中"),
            /**
             * 将Jenkins Job从视图中移除
             */
            REMOVE_FROM_VIEW("remove-job-from-view", "将Jenkins Job从视图中移除"),
            /**
             * 启用Jenkins Job
             */
            ENABLE("enable-job", "启用Jenkins Job"),
            /**
             * 禁用Jenkins Job
             */
            DISABLE("disable-job", "禁用Jenkins Job"),
            /**
             * 重新加载Jenkins Job
             */
            RELOAD("reload-job", "重新加载Jenkins Job"),
            /**
             * 停止构建Jenkins Jobs
             */
            STOP_BUILDS("stop-builds", "停止构建Jenkins Job");

            @Setter
            @Getter
            private String op;
            @Setter
            @Getter
            private String desc;

            Operation(String op, String desc) {
                this.setOp(op);
                this.setDesc(desc);
            }
        }

        /**
         * 查询Jenkins上的Job列表
         *
         * @param host     Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth     Jenkins登录用户名:密码,比如:admin:admin
         * @param viewName Jenkins视图名称,为空时,查询全部Jobs
         * @return ResultDto
         */
        public static ResultDto listJobs(String host, String auth, String viewName) {
            String[] baseArgs = new String[]{Operation.LIST.op};
            String[] finalArgs = StringUtils.hasText(viewName) ? Stream.concat(Arrays.stream(baseArgs),
                    Arrays.stream(new String[]{viewName})).toArray(String[]::new) : baseArgs;
            CLI cli = doWork(host, auth, finalArgs, PROTOCOL_HTTP, null);
            int code = cli.code;
            String msg = cli.msg;
            List<JobDto> jobDtoList = new ArrayList<>(10);
            boolean ifSuccess = ResultDto.SUCCESS.equals(String.valueOf(code));
            boolean listAllJobs = StringUtils.hasText(viewName) ? true : false;
            if (ifSuccess) {
                if (StringUtils.hasText(msg)) {
                    String[] arr = msg.split(END_SYMBOL);
                    for (String tmp : arr) {
                        JobDto jobDto = new JobDto();
                        jobDto.setJobName(tmp);
                        jobDto.setViewName(listAllJobs ? viewName : "");
                        jobDtoList.add(jobDto);
                    }
                }
            }
            return ifSuccess ? ResultDto.buildSuccess(jobDtoList) : ResultDto.buildError(jobDtoList).msg(msg);
        }

        /**
         * 查询Jenkins上的凭证列表
         *
         * @param ip       Jenkins服务IP
         * @param port     Jenkins服务端口
         * @param username Jenkins服务登录用户名
         * @param password Jenkins服务登录密码
         * @param viewName Jenkins视图名称,为空时,查询全部Jobs
         * @return ResultDto
         */
        public static ResultDto listJobs(String ip, String port, String username, String password, String viewName) {
            String host = "http://" + ip + ":" + port;
            String auth = username + ":" + password;
            return listJobs(host, auth, viewName);
        }

        public static ResultDto addJob2View(String host, String auth, String viewName, String[] jobNames) {
            String[] baseArgs = new String[]{Operation.ADD_TO_VIEW.op};
            String[] finalArgs = Stream.of(baseArgs, new String[]{viewName}, jobNames).flatMap(Arrays::stream)
                    .toArray(String[]::new);
            CLI cli = doWork(host, auth, finalArgs, PROTOCOL_HTTP, null);
            int code = cli.code;
            String msg = cli.msg;
            List<JobDto> jobDtoList = new ArrayList<>(10);
            return ResultDto.SUCCESS.equals(String.valueOf(code)) ?
                    ResultDto.buildSuccess(jobDtoList) : ResultDto.buildError(jobDtoList).msg(msg);
        }

        public static ResultDto addJob2View(String ip, String port, String username, String password,
                                            String viewName, String[] jobNames) {
            String host = "http://" + ip + ":" + port;
            String auth = username + ":" + password;
            return addJob2View(host, auth, viewName, jobNames);
        }

        public static ResultDto removeJobFromView(String host, String auth, String viewName, String[] jobNames) {
            String[] baseArgs = new String[]{Operation.REMOVE_FROM_VIEW.op};
            String[] finalArgs = Stream.of(baseArgs, new String[]{viewName}, jobNames).flatMap(Arrays::stream)
                    .toArray(String[]::new);
            CLI cli = doWork(host, auth, finalArgs, PROTOCOL_HTTP, null);
            int code = cli.code;
            String msg = cli.msg;
            List<JobDto> jobDtoList = new ArrayList<>(10);
            return ResultDto.SUCCESS.equals(String.valueOf(code)) ?
                    ResultDto.buildSuccess(jobDtoList) : ResultDto.buildError(jobDtoList).msg(msg);
        }

        /**
         * 创建Job
         *
         * @param host Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth Jenkins登录用户名:密码,比如:admin:admin
         * @param job  Jenkins Job
         * @return {@link ResultDto}
         */
        public static ResultDto createJob(String host, String auth, JobDto job) {
            List<JobDto.SvnInfoDto> svnInfoDtoList = job.getSvnInfoList();
            List<JobDto.GitInfoDto> gitInfoDtoList = job.getGitInfoList();
            boolean scmCheck = CollectionUtils.isEmpty(svnInfoDtoList) && CollectionUtils.isEmpty(gitInfoDtoList);
            if (scmCheck) {
                return ResultDto.buildErrorDto().msg("源码管理地址信息不能为空");
            }
            String xml = "<?xml version='1.1' encoding='UTF-8'?>\n" +
                    "<project>\n" +
                    "    <actions/>\n" +
                    "    <description>" + job.getDescription() + "</description>\n" +
                    "    <keepDependencies>false</keepDependencies>\n" +
                    "    <properties/>\n";
            if (!CollectionUtils.isEmpty(svnInfoDtoList)) {
                xml += buildSvn(job);
            }
            if (!CollectionUtils.isEmpty(gitInfoDtoList)) {
                xml += buildGit(job);
            }
            xml += "        </locations>\n" +
                    "        <excludedRegions></excludedRegions>\n" +
                    "        <includedRegions></includedRegions>\n" +
                    "        <excludedUsers></excludedUsers>\n" +
                    "        <excludedRevprop></excludedRevprop>\n" +
                    "        <excludedCommitMessages></excludedCommitMessages>\n" +
                    "        <workspaceUpdater class=\"hudson.scm.subversion.UpdateUpdater\"/>\n" +
                    "        <ignoreDirPropChanges>false</ignoreDirPropChanges>\n" +
                    "        <filterChangelog>false</filterChangelog>\n" +
                    "        <quietOperation>true</quietOperation>\n" +
                    "    </scm>\n" +
                    "    <canRoam>true</canRoam>\n" +
                    "    <disabled>" + job.getDisabled() + "</disabled>\n" +
                    "    <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>\n" +
                    "    <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>\n" +
                    "    <triggers/>\n" +
                    "    <concurrentBuild>false</concurrentBuild>\n";
            xml += buildBuilders(job);
            xml += "    <publishers/>\n" +
                    "    <buildWrappers/>\n" +
                    "</project>";
            CLI cli = doWork(host, auth, new String[]{Operation.CREATE.op, job.getJobName()}, PROTOCOL_HTTP, xml);
            int code = cli.code;
            String msg = cli.msg;
            return ResultDto.SUCCESS.equals(String.valueOf(code)) ?
                    ResultDto.buildSuccessDto() : ResultDto.buildErrorDto().msg(msg);
        }

        /**
         * 更新Job
         *
         * @param host Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth Jenkins登录用户名:密码,比如:admin:admin
         * @param job  Jenkins Job
         * @return {@link ResultDto}
         */
        public static ResultDto updateJob(String host, String auth, JobDto job) {
            //TODO:待补充完善
            return null;
        }

        /**
         * 删除单个或多个Job
         *
         * @param host     Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth     Jenkins登录用户名:密码,比如:admin:admin
         * @param jobNames Jenkins Job名称集
         * @return {@link ResultDto}
         */
        public static ResultDto deleteJob(String host, String auth, String[] jobNames) {
            String[] finalArgs = Stream.of(new String[]{Operation.DELETE.op}, jobNames)
                    .flatMap(Arrays::stream).toArray(String[]::new);
            return operation(host, auth, finalArgs);
        }

        /**
         * 复制Job
         *
         * @param host        Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth        Jenkins登录用户名:密码,比如:admin:admin
         * @param srcJobName  需要被复制的Jenkins Job名称
         * @param destJobName 新Jenkins Job名称
         * @return {@link ResultDto}
         */
        public static ResultDto copyJob(String host, String auth, String srcJobName, String destJobName) {
            String[] finalArgs = new String[]{Operation.COPY.op, srcJobName, destJobName};
            return operation(host, auth, finalArgs);
        }

        /**
         * 查询单个Job
         *
         * @param host    Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth    Jenkins登录用户名:密码,比如:admin:admin
         * @param jobName Jenkins Job名称
         * @return {@link ResultDto}
         */
        public static ResultDto<JobDto> getJob(String host, String auth, String jobName) {
            String[] finalArgs = new String[]{Operation.GET.op, jobName};
            ResultDto resultDto = operation(host, auth, finalArgs);
            JobDto jobDto = new JobDto();
            jobDto.setJobName(jobName);
            //解析xml,获取名称、描述等信息
            String msg = ObjectUtils.isEmpty(resultDto.getData()) ? "" : resultDto.getData().toString();
            if (StringUtils.hasText(msg)) {
                jobDto.parseXmlStr2Dto(msg);
            }
            return resultDto.data(jobDto);
        }

        /**
         * 构建单个Job
         *
         * @param host    Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth    Jenkins登录用户名:密码,比如:admin:admin
         * @param jobName Jenkins Job名称
         * @return {@link ResultDto}
         */
        public static ResultDto buildJob(String host, String auth, String jobName) {
            return operation(host, auth, new String[]{Operation.BUILD.op, jobName});
        }

        /**
         * 输出单个Job的构建日志信息
         *
         * @param host    Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth    Jenkins登录用户名:密码,比如:admin:admin
         * @param jobName Jenkins Job名称
         * @param buildId 构建id,为空时,查询最后一次构建的信息
         * @return {@link ResultDto}
         */
        public static ResultDto console(String host, String auth, String jobName, Integer buildId) {
            String[] baseArgs = new String[]{Operation.CONSOLE.op, jobName};
            String[] finalArgs = ObjectUtils.isEmpty(buildId) ? baseArgs : Stream.of(baseArgs, new String[]{buildId.toString()})
                    .flatMap(Arrays::stream).toArray(String[]::new);
            return operation(host, auth, finalArgs);
        }

        /**
         * 禁用Job
         *
         * @param host    Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth    Jenkins登录用户名:密码,比如:admin:admin
         * @param jobName Jenkins Job名称
         * @return {@link ResultDto}
         */
        public static ResultDto disableJob(String host, String auth, String jobName) {
            return operation(host, auth, new String[]{Operation.DISABLE.op, jobName});
        }

        /**
         * 启用Job
         *
         * @param host    Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth    Jenkins登录用户名:密码,比如:admin:admin
         * @param jobName Jenkins Job名称
         * @return {@link ResultDto}
         */
        public static ResultDto enableJob(String host, String auth, String jobName) {
            return operation(host, auth, new String[]{Operation.ENABLE.op, jobName});
        }

        /**
         * 重新加载Job
         *
         * @param host    Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth    Jenkins登录用户名:密码,比如:admin:admin
         * @param jobName Jenkins Job名称
         * @return {@link ResultDto}
         */
        public static ResultDto reloadJob(String host, String auth, String jobName) {
            return operation(host, auth, new String[]{Operation.RELOAD.op, jobName});
        }

        /**
         * 停止构建Job
         *
         * @param host     Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth     Jenkins登录用户名:密码,比如:admin:admin
         * @param jobNames Jenkins Job名称集合
         * @return {@link ResultDto}
         */
        public static ResultDto stopBuildJob(String host, String auth, String[] jobNames) {
            String[] finalArgs = Stream.of(new String[]{Operation.STOP_BUILDS.op}, jobNames)
                    .flatMap(Arrays::stream).toArray(String[]::new);
            return operation(host, auth, finalArgs);
        }

        /**
         * 获取Jenkins Job的构建信息
         *
         * @param host     Jenkins地址,比如:http://10.100.57.156:8080
         * @param username Jenkins登录用户名
         * @param password Jenkins登录用户密码
         * @param jobName  Jenkins Job名称
         * @param buildId  Jenkins Job构建id
         * @return {@link ResultDto}
         */
        public static ResultDto<BuildInfoDto> getJobBuildInfoDetail(String host, String username, String password,
                                                                    String jobName, Integer buildId) {
            if (ObjectUtils.isEmpty(buildId)) {
                return ResultDto.buildErrorDto().msg("buildId can not empty.");
            }
            JenkinsClient client = JenkinsClient.builder().endPoint(host).credentials(username + ":" + password).build();
            List<QueueItem> list = client.api().queueApi().queue();
            for (QueueItem item : list) {
                System.out.println(item.why() + "   " + item.task().name());
            }
            System.out.println(JsonUtil.toJSONString(client.api().systemApi().systemInfo()));
            BuildInfo data = client.api().jobsApi().buildInfo(null, jobName, buildId);
            System.out.println(data.building());

            String url = JOB_BUILD_DETAIL_URL.replace("@host", host).replace("@jobName", jobName)
                    .replace("@buildId", buildId.toString());
            return doGet(url, username, password, BuildInfoDto.class);
        }

        /**
         * 获取Jenkins Job的构建信息
         *
         * @param host     Jenkins地址,比如:http://10.100.57.156:8080
         * @param username Jenkins登录用户名
         * @param password Jenkins登录用户密码
         * @param jobName  Jenkins Job名称
         * @param buildId  Jenkins Job构建id
         * @return {@link ResultDto}
         */
        public static boolean getJobBuildStatus(String host, String username, String password,
                                                String jobName, Integer buildId) {
            if (ObjectUtils.isEmpty(buildId)) {
                throw new IllegalArgumentException("buildId can not empty.");
            }
            String url = JOB_BUILD_DETAIL_URL.replace("@host", host).replace("@jobName", jobName)
                    .replace("@buildId", buildId.toString());
            BuildInfoDto infoDto = getJobBuildInfoDetail(url, username, password, jobName, buildId).getData();
            return infoDto.getBuilding();
        }

        /**
         * 操作单个Job(查询、启用、禁用、删除)
         *
         * @param host Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth Jenkins登录用户名:密码,比如:admin:admin
         * @param args Jenkins CLI参数
         * @return {@link ResultDto}
         */
        private static ResultDto operation(String host, String auth, String[] args) {
            CLI cli = doWork(host, auth, args, PROTOCOL_HTTP, null);
            int code = cli.code;
            String msg = cli.msg;
            return ResultDto.SUCCESS.equals(String.valueOf(code)) ?
                    ResultDto.buildSuccess(msg) : ResultDto.buildError(msg).msg(msg);
        }

        /**
         * 组织Job的git xml章节内容
         *
         * @param job JobDto
         * @return {@link String}
         */
        private static String buildGit(JobDto job) {
            //TODO:待补充组装git相关xml配置
            String xml = "";
            return xml;
        }

        /**
         * 组织Job的svn xml章节内容
         *
         * @param job JobDto
         * @return {@link String}
         */
        private static String buildSvn(JobDto job) {
            List<JobDto.SvnInfoDto> svnInfoDtoList = job.getSvnInfoList();
            String xml = "    <scm class=\"hudson.scm.SubversionSCM\" >\n" +
                    "        <locations>\n";
            StringBuilder sb = new StringBuilder();
            for (JobDto.SvnInfoDto svnInfoDto : svnInfoDtoList) {
                sb.append("            <hudson.scm.SubversionSCM_-ModuleLocation>\n")
                        .append("                <remote>" + svnInfoDto.getRemote() + "</remote>\n")
                        .append("                <credentialsId>" + svnInfoDto.getCredentialsId() + "</credentialsId>\n")
                        .append("                <local>" + svnInfoDto.getLocal() + "</local>\n")
                        .append("                <depthOption>" + svnInfoDto.getDepthOption() + "</depthOption>\n")
                        .append("                <ignoreExternalsOption>" + svnInfoDto.getIgnoreExternalsOption() + "</ignoreExternalsOption>\n")
                        .append("                <cancelProcessOnExternalsFail>" + svnInfoDto.getCancelProcessOnExternalsFail() + "</cancelProcessOnExternalsFail>\n")
                        .append("            </hudson.scm.SubversionSCM_-ModuleLocation>\n");
            }
            xml += sb.toString();
            return xml;
        }

        /**
         * 组织Job的builders xml章节内容
         *
         * @param job JobDto
         * @return {@link String}
         */
        private static String buildBuilders(JobDto job) {
            String xml = "";
            List<String> shellList = job.getBuildCmdList();
            List<JobDto.SonarRunnerBuilderDto> sonarRunnerBuilderDtoList = job.getSonarRunnerBuilderDtoList();
            boolean checkBuilders = CollectionUtils.isEmpty(shellList) && CollectionUtils.isEmpty(sonarRunnerBuilderDtoList);
            if (checkBuilders) {
                xml += "<builders/>";
            } else {
                xml += "    <builders>\n";
                if (!CollectionUtils.isEmpty(shellList)) {
                    String shells = shellList.stream().collect(Collectors.joining("\n"));
                    xml += "        <hudson.tasks.Shell>\n" +
                            "            <command>" + shells +
                            "            </command>\n" +
                            "            <configuredLocalRules/>\n" +
                            "        </hudson.tasks.Shell>\n";
                }
                if (!CollectionUtils.isEmpty(sonarRunnerBuilderDtoList)) {
                    JobDto.ProjectInfo projectInfo = job.getProjectInfo();
                    String keyInfo = projectInfo.getProjectName() + "_" + job.getJobName() + "_" + projectInfo.getBuildVersion();
                    StringBuilder sb = new StringBuilder();
                    for (JobDto.SonarRunnerBuilderDto sonar : sonarRunnerBuilderDtoList) {
                        sb.append("        <hudson.plugins.sonar.SonarRunnerBuilder>\n")
                                .append("            <project></project>\n")
                                .append("            <properties>sonar.projectKey=" + keyInfo + "\n")
                                .append("sonar.projectName=" + keyInfo + "\n")
                                .append("sonar.projectVersion=" + projectInfo.getBuildVersion() + "\n")
                                .append("sonar.language=" + sonar.getSonarScannerLanguage() + "\n")
                                .append("sonar.sourceEncoding=UTF-8\n")
                                .append("sonar.java.binaries=target/classes\n")
                                .append("sonar.sources=.\n")
                                .append("sonar.login=" + job.getSonarLogin() + "\n")
                                .append("sonar.password=" + job.getSonarPassword() + "\n")
                                .append("sonar.scm.disabled=" + sonar.getSonarScmDisabled())
                                .append("            </properties>\n")
                                .append("            <javaOpts>" + sonar.getJavaOpts() + "</javaOpts>\n")
                                .append("            <additionalArguments>" + sonar.getAdditionalArguments() + "</additionalArguments>\n")
                                .append("            <jdk>" + sonar.getJdk() + "</jdk>\n")
                                .append("            <task>" + sonar.getTask() + "</task>\n")
                                .append("        </hudson.plugins.sonar.SonarRunnerBuilder>\n");
                    }
                    xml += sb.toString();
                }
                xml += "    </builders>\n";
            }
            return xml;
        }

    }


    public static class Views {

        public enum Operation {
            /**
             * 创建Jenkins视图
             */
            CREATE("create-view", "创建Jenkins视图"),
            /**
             * 更新Jenkins视图
             */
            UPDATE("update-view", "更新Jenkins视图"),
            /**
             * 删除Jenkins视图
             */
            DELETE("delete-view", "删除Jenkins视图"),
            /**
             * 查询Jenkins视图
             */
            GET("get-view", "查询Jenkins视图");

            @Setter
            @Getter
            private String op;
            @Setter
            @Getter
            private String desc;

            Operation(String op, String desc) {
                this.setOp(op);
                this.setDesc(desc);
            }
        }

        /**
         * 创建View
         *
         * @param host    Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth    Jenkins登录用户名:密码,比如:admin:admin
         * @param viewDto ViewDto
         * @return {@link ResultDto}
         */
        public static ResultDto createView(String host, String auth, ViewDto viewDto) {
            return operation(host, auth, viewDto, Operation.CREATE);
        }

        /**
         * 更新View
         *
         * @param host    Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth    Jenkins登录用户名:密码,比如:admin:admin
         * @param viewDto ViewDto
         * @return {@link ResultDto}
         */
        public static ResultDto updateView(String host, String auth, ViewDto viewDto) {
            return operation(host, auth, viewDto, Operation.UPDATE);
        }

        /**
         * 删除View
         *
         * @param host    Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth    Jenkins登录用户名:密码,比如:admin:admin
         * @param viewDto ViewDto
         * @return {@link ResultDto}
         */
        public static ResultDto deleteView(String host, String auth, ViewDto viewDto) {
            return operation(host, auth, viewDto, Operation.DELETE);
        }

        /**
         * 重命名View
         *
         * @param host        Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth        Jenkins登录用户名:密码,比如:admin:admin
         * @param oldViewName Jenkins旧视图名称
         * @param newViewName Jenkins新视图名称
         * @return ResultDto
         */
        public static ResultDto rename(String host, String auth, String oldViewName, String newViewName) {
            return copyOrRenameView(host, auth, oldViewName, newViewName, true);
        }

        /**
         * 拷贝View
         *
         * @param host        Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth        Jenkins登录用户名:密码,比如:admin:admin
         * @param oldViewName Jenkins旧视图名称
         * @param newViewName Jenkins新视图名称
         * @return ResultDto
         */
        public static ResultDto copyView(String host, String auth, String oldViewName, String newViewName) {
            return copyOrRenameView(host, auth, oldViewName, newViewName, false);
        }

        /**
         * 根据视图名称查询视图信息
         *
         * @param host             Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth             Jenkins登录用户名:密码,比如:admin:admin
         * @param viewName         Jenkins视图名称
         * @param returnJobDetails 是否返回Job详细信息,默认不返回。
         * @return {@link ResultDto}
         */
        public static ResultDto<ViewDto> getView(String host, String auth, String viewName, Boolean returnJobDetails) {
            if (!StringUtils.hasText(viewName)) {
                return ResultDto.buildErrorDto().msg("视图名称不能为空");
            }
            CLI cli = doWork(host, auth, new String[]{Operation.GET.op, viewName}, PROTOCOL_HTTP, null);
            int code = cli.code;
            String msg = cli.msg;
            boolean ifSuccess = ResultDto.SUCCESS.equals(String.valueOf(code));
            ViewDto viewDto = new ViewDto();
            viewDto.setViewName(viewName);
            //解析xml,获取view名称、描述、关联的job等信息
            if (StringUtils.hasText(msg)) {
                viewDto.parseXmlStr2Dto(msg);
            }
            if (ObjectUtils.isEmpty(returnJobDetails)) {
                returnJobDetails = false;
            }
            if (returnJobDetails) {
                List<JobDto> jobDtoList = viewDto.getJobList();
                for (JobDto jobDto : jobDtoList) {
                    String jobName = jobDto.getJobName();
                    JobDto dto = Jobs.getJob(host, auth, jobName).getData();
                    BeanUtils.copyProperties(dto, jobDto);
                }
            }
            return ifSuccess ? ResultDto.buildSuccess(viewDto) : ResultDto.buildError(viewDto).msg(msg);
        }

        /**
         * 拷贝或重命名View
         *
         * @param host        Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth        Jenkins登录用户名:密码,比如:admin:admin
         * @param oldViewName Jenkins旧视图名称
         * @param newViewName Jenkins新视图名称
         * @return ResultDto
         */
        private static ResultDto copyOrRenameView(String host, String auth, String oldViewName, String newViewName, boolean rename) {
            //1、先查询目标View
            ResultDto<ViewDto> oldViewInfoResult = getView(host, auth, oldViewName, true);
            if (!oldViewInfoResult.ifSuccess()) {
                return oldViewInfoResult;
            }
            ViewDto newViewInfo = new ViewDto();
            ViewDto oldViewInfo = oldViewInfoResult.getData();
            BeanUtils.copyProperties(oldViewInfo, newViewInfo);
            newViewInfo.setViewName(newViewName);
            //2、拷贝目标View内容,并重命名
            ResultDto createResult = operation(host, auth, newViewInfo, Operation.CREATE);
            if (!createResult.ifSuccess()) {
                return createResult;
            }
            List<JobDto> jobDtoList = oldViewInfo.getJobList();
            if (CollectionUtils.isEmpty(jobDtoList)) {
                return createResult;
            }
            String[] jobNames = new String[jobDtoList.size()];
            for (int i = 0; i < jobDtoList.size(); i++) {
                jobNames[i] = jobDtoList.get(i).getJobName();
            }
            ResultDto addJob2ViewResult = Jobs.addJob2View(host, auth, newViewName, jobNames);
            if (!addJob2ViewResult.ifSuccess()) {
                return addJob2ViewResult;
            }
            if (rename) {
                //3、再删除旧View
                return operation(host, auth, oldViewInfo, Operation.DELETE);
            } else {
                return ResultDto.buildSuccessDto();
            }
        }

        /**
         * @param host    Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth    Jenkins登录用户名:密码,比如:admin:admin
         * @param viewDto ViewDto
         * @param op      操作:create-view、update-view、delete-view
         * @return {@link ResultDto}
         */
        private static ResultDto operation(String host, String auth, ViewDto viewDto, Operation op) {
            String operation = op.op;
            String xml = "<?xml version=\"1.1\" encoding=\"UTF-8\"?>\n" +
                    "<hudson.model.ListView>\n" +
                    "    <name>" + viewDto.getViewName() + "</name>\n" +
                    "    <description>" + viewDto.getDescription() + "</description>\n" +
                    "    <filterExecutors>" + viewDto.isFilterExecutors() + "</filterExecutors>\n" +
                    "    <filterQueue>" + viewDto.isFilterQueue() + "</filterQueue>\n" +
                    "    <properties class=\"hudson.model.View$PropertyList\"/>\n" +
                    "    <jobNames>\n" +
                    "        <comparator class=\"java.lang.String$CaseInsensitiveComparator\"/>\n" +
                    "    </jobNames>\n" +
                    "    <jobFilters>\n" +
                    "    </jobFilters>\n" +
                    "    <columns>\n" +
                    "        <hudson.views.StatusColumn/>\n" +
                    "        <hudson.views.WeatherColumn/>\n" +
                    "        <hudson.views.JobColumn/>\n" +
                    "        <hudson.views.LastSuccessColumn/>\n" +
                    "        <hudson.views.LastFailureColumn/>\n" +
                    "        <hudson.views.LastStableColumn/>\n" +
                    "        <hudson.views.LastDurationColumn/>\n" +
                    "        <hudson.views.BuildButtonColumn/>\n" +
                    "        <jenkins.branch.DescriptionColumn />\n" +
                    "    </columns>\n";
            if (StringUtils.hasText(viewDto.getIncludeRegex())) {
                xml += "    <includeRegex>" + viewDto.getIncludeRegex() + "</includeRegex>\n";
            }
            xml += "    <recurse>" + viewDto.isRecurse() + "</recurse>\n" +
                    "</hudson.model.ListView>";
            boolean isUpdateOrDelete = Operation.DELETE.equals(op) || Operation.UPDATE.equals(op);
            String[] finalArgs = isUpdateOrDelete ?
                    new String[]{operation, viewDto.getViewName()} : new String[]{operation};
            CLI cli = doWork(host, auth, finalArgs, PROTOCOL_HTTP, xml);
            int code = cli.code;
            String msg = cli.msg;
            return ResultDto.SUCCESS.equals(String.valueOf(code)) ?
                    ResultDto.buildSuccessDto() : ResultDto.buildErrorDto().msg(msg);
        }
    }

    public static class JenkinsSystem {

        public enum Operation {
            /**
             * 获取Jenkins支持的命令
             */
            HELP("help", "获取Jenkins支持的命令"),
            /**
             * 获取Jenkins版本信息
             */
            VERSION("version", "获取Jenkins版本信息"),
            /**
             * 更新Jenkins全局配置信息
             */
            RELOAD_CONFIGURATION("reload-configuration", "更新Jenkins全局配置信息"),
            /**
             * 重启Jenkins服务
             */
            RESTART("restart", "重启Jenkins服务"),
            /**
             * 安全重启Jenkins服务(Safe Restart Jenkins. Don't start any builds.)
             */
            SAFE_RESTART("safe-restart", "重启Jenkins服务"),
            /**
             * 停止Jenkins服务
             */
            SHUTDOWN("shutdown", "停止Jenkins服务"),
            /**
             * 安全停止Jenkins服务(Puts Jenkins into the quiet mode, wait for existing builds to be completed,
             * and then shut down Jenkins.)
             */
            SAFE_SHUTDOWN("safe-shutdown", "安全停止Jenkins服务"),
            /**
             * 清除Jenkins中的构建队列(Clears the build queue.)
             */
            CLEAR_QUEUE("clear-queue", "清除Jenkins中的构建队列"),
            ;

            @Setter
            @Getter
            private String op;
            @Setter
            @Getter
            private String desc;

            Operation(String op, String desc) {
                this.setOp(op);
                this.setDesc(desc);
            }
        }

        /**
         * 获取Jenkins支持的命令
         *
         * @param host Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth Jenkins登录用户名:密码,比如:admin:admin
         * @return ResultDto
         */
        public static ResultDto help(String host, String auth) {
            return operation(host, auth, Operation.HELP);
        }

        /**
         * 获取Jenkins版本信息
         *
         * @param host Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth Jenkins登录用户名:密码,比如:admin:admin
         * @return ResultDto
         */
        public static ResultDto getVersion(String host, String auth) {
            return operation(host, auth, Operation.VERSION);
        }

        /**
         * 重启Jenkins服务
         *
         * @param host Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth Jenkins登录用户名:密码,比如:admin:admin
         * @return ResultDto
         */
        public static ResultDto restart(String host, String auth) {
            return operation(host, auth, Operation.RESTART);
        }

        /**
         * 安全重启Jenkins服务(Safe Restart Jenkins. Don't start any builds.)
         *
         * @param host Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth Jenkins登录用户名:密码,比如:admin:admin
         * @return ResultDto
         */
        public static ResultDto safeRestart(String host, String auth) {
            return operation(host, auth, Operation.SAFE_RESTART);
        }

        /**
         * 停止Jenkins服务
         *
         * @param host Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth Jenkins登录用户名:密码,比如:admin:admin
         * @return ResultDto
         */
        public static ResultDto shutdown(String host, String auth) {
            return operation(host, auth, Operation.SHUTDOWN);
        }

        /**
         * 安全停止Jenkins服务(Puts Jenkins into the quiet mode, wait for existing builds to be completed,
         * and then shut down Jenkins.)
         *
         * @param host Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth Jenkins登录用户名:密码,比如:admin:admin
         * @return ResultDto
         */
        public static ResultDto safeShutdown(String host, String auth) {
            return operation(host, auth, Operation.SAFE_SHUTDOWN);
        }

        /**
         * 清除Jenkins中的构建队列(Clears the build queue.)
         *
         * @param host Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth Jenkins登录用户名:密码,比如:admin:admin
         * @return ResultDto
         */
        public static ResultDto clearQueue(String host, String auth) {
            return operation(host, auth, Operation.CLEAR_QUEUE);
        }

        /**
         * 重新加载Jenkins配置信息
         * Discard all the loaded data in memory and reload everything from file system.
         * Useful when you modified config files directly on disk.
         *
         * @param host Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth Jenkins登录用户名:密码,比如:admin:admin
         * @return ResultDto
         */
        public static ResultDto reloadConfiguration(String host, String auth) {
            return operation(host, auth, Operation.RELOAD_CONFIGURATION);
        }

        /**
         * 基本操作
         *
         * @param host Jenkins地址,比如:http://10.100.57.156:8080
         * @param auth Jenkins登录用户名:密码,比如:admin:admin
         * @param op   操作
         * @return {@link ResultDto}
         */
        private static ResultDto operation(String host, String auth, Operation op) {
            CLI cli = doWork(host, auth, new String[]{op.op}, PROTOCOL_HTTP, null);
            int code = cli.code;
            String msg = cli.msg;
            String successMsg = StringUtils.hasText(msg) ? msg.replaceAll(END_SYMBOL, "") : "";
            return ResultDto.SUCCESS.equals(String.valueOf(code)) ?
                    ResultDto.buildSuccessDto(successMsg) : ResultDto.buildErrorDto().msg(msg);
        }
    }

    /**
     * 调用Jenkins CLI
     * 注意:查询类时xml为空;涉及到使用xml内容创建、更新操作的,xml不能为空!!!
     *
     * @param host     Jenkins地址,比如:http://10.100.57.156:8080
     * @param auth     Jenkins登录用户名:密码,比如:admin:admin
     * @param args     请求参数
     * @param protocol 请求协议,比如http,webSocket,默认http
     * @param xml      处理XML相关,不使用标准输入流,此时使用HTTP协议调用
     * @return CLI
     */
    private static CLI doWork(String host, String auth, String[] args, String protocol, String xml) {
        if (null == args || args.length == 0) {
            throw new IllegalArgumentException("args cannot be empty");
        }
        if (!StringUtils.hasText(protocol)) {
            protocol = PROTOCOL_HTTP;
        }
        byte[] xmlData = new byte[]{};
        if (StringUtils.hasText(xml)) {
            xmlData = xml.getBytes(StandardCharsets.UTF_8);
        }
        CLI cli = new CLI();
        String[] baseArgs = new String[]{"-auth", auth, "-s", host, protocol};
        String[] finalArgs = Stream.concat(Arrays.stream(baseArgs), Arrays.stream(args)).toArray(String[]::new);
        log.info("executing command: {}", JsonUtil.toJSONString(finalArgs));
        try {
            cli._main(finalArgs, xmlData);
        } catch (Exception e) {
            cli.code = -1;
            cli.msg = e.getMessage();
            log.error("executing command: {} cause error ", JsonUtil.toJSONString(finalArgs), e);
        }
        return cli;
    }

    private static <T> ResultDto<T> doGet(String urlString, String username, String password, Class clz) {
        URI uri = URI.create(urlString);
        HttpHost host = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
        CredentialsProvider credsProvider = new BasicCredentialsProvider();
        credsProvider.setCredentials(new AuthScope(uri.getHost(), uri.getPort()), new UsernamePasswordCredentials(username, password));
        AuthCache authCache = new BasicAuthCache();
        BasicScheme basicAuth = new BasicScheme();
        authCache.put(host, basicAuth);
        CloseableHttpClient httpClient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
        HttpGet httpGet = new HttpGet(uri);
        HttpClientContext localContext = HttpClientContext.create();
        localContext.setAuthCache(authCache);
        if (ObjectUtils.isEmpty(clz)) {
            clz = String.class;
        }
        T data = (T) new Object();
        try {
            CloseableHttpResponse response = httpClient.execute(host, httpGet, localContext);
            String returnMsg = EntityUtils.toString(response.getEntity());
            System.out.println(returnMsg);
            if (StringUtils.hasText(returnMsg)) {
                data = (T) JsonUtil.string2Obj(returnMsg, clz);
                return ResultDto.buildSuccessDto().data(data);
            }
            return ResultDto.buildSuccessDto().data(returnMsg);
        } catch (Exception e) {
            log.error("call {} failed", urlString, e);
            return ResultDto.buildErrorDto().data(data);
        }
    }
}
相关推荐
BruceGerGer4 小时前
flutter开发实战-Webview及dispose关闭背景音
flutter·1024程序员节
BruceGerGer7 天前
flutter开发实战-ListWheelScrollView与自定义TimePicker时间选择器
flutter·1024程序员节
askah664411 天前
电脑提示msvcr120.dll丢失怎样修复
经验分享·microsoft·dll修复·dll丢失·1024程序员节
szpc162114 天前
替代LTC4449高速同步N道沟MOSFET驱动器|具有轨对轨栅极驱动
单片机·嵌入式硬件·开源·集成测试·1024程序员节
sheng12345678rui15 天前
mfc140.dll是什么文件?mfc140.dll文件下载安装办法
windows·电脑·dll文件·dll修复工具·1024程序员节
行十万里人生17 天前
lambda 表达式
开发语言·数据结构·c++·opencv·机器学习·哈希算法·1024程序员节
BruceGerGer21 天前
flutter开发实战-RichText富文本居中对齐
flutter·1024程序员节
BruceGerGer21 天前
flutter开发实战-创建一个微光加载效果
flutter·1024程序员节
sheng12345678rui21 天前
计算机游戏因为d3dcompiler_47.dll丢失无法启动怎么办?解决只要d3dcompiler_47.dll丢失无法启动游戏软件的方法
windows·游戏·dll文件·dll修复工具·1024程序员节
程农25 天前
Hutool有哪些常用方法
api·工具类·hotool