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的源码修改

在这里插入图片描述

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@Getterprivate String op;@Setter@Getterprivate 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@Getterprivate String op;@Setter@Getterprivate 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@Getterprivate String op;@Setter@Getterprivate 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、先查询目标ViewResultDto<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、再删除旧Viewreturn 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@Getterprivate String op;@Setter@Getterprivate 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);}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/116401.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

arco-disign vue + vue3 封装一个“下拉多组单选组件”

手搓一个下拉框多组单选组件, 实现采用框架 arco-disign vue + vue3 组件采用:a-popover、a-input-tag、a-radio-group、a-radio 实现效果: 调用组件 <SelectGroupRadiov-model="searchModel.indicatorScreening":options="dict.indicatorScreening&qu…

USB协议学习(二)设备枚举过程分析

USB协议学习&#xff08;二&#xff09;设备枚举过程分析 笔者来聊聊设备枚举过程分析. 这里写自定义目录标题 USB协议学习&#xff08;二&#xff09;设备枚举过程分析USB设备运行过程USB设备枚举USB设备通信构成USB设备请求USB枚举过程分析如何改变文本的样式插入链接与图片如…

软硬件架构分层总结

一、前言 软件系统很多架构图我们经常看到是这样的三段 就是这三段就可以演化出很多层 二、硬件架构分层 硬件层&#xff0c;基本是计算机硬件的体系结构&#xff0c;包括硬盘设备&#xff0c;cpu&#xff0c;内存&#xff0c;控制器&#xff0c;运算器&#xff0c;寄存器&am…

Stream流式处理

Stream流式处理&#xff1a; 建立在Lambda表达式基础上的多数据处理技术。 可以对集合进行迭代、去重、筛选、排序、聚合等处理&#xff0c;极大的简化了代码量。 Stream常用方法 Stream流对象的五种创建方式 //基于数组 String[] arr {"a","b","c…

吉利高端品牌领克汽车携手体验家,重塑智能创新的汽车服务体验

浙江吉利控股集团&#xff08;以下简称“吉利集团”&#xff09;始建于1986年&#xff0c;1997年进入汽车行业&#xff0c;一直专注实业&#xff0c;专注技术创新和人才培养&#xff0c;坚定不移地推动企业转型升级和可持续发展。现资产总值超5100亿元&#xff0c;员工总数超过…

k8s集群镜像下载加gradana监控加elk日志收集加devops加秒杀项目

展示 1.配套资料2.devops 3.elk日志收集 4.grafana监控 5.dashboard![在这里插入图片描述](https://img-blog.csdnimg.cn/bf294f9fd98e4c038858a6bf5c34dbdc.png 目的 学习k8s来来回回折腾很久了&#xff0c;光搭个环境就能折腾几天。这次工作需要终于静下心来好好学习了一…

ES6中的Set集合与Map映射

文章目录 一、Set集合1.Set的基本使用2.Set的常见方法3.WeakSet使用4.WeakSet的应用 二、Map映射1.Map的基本使用2.Map的常见方法3.WeakMap使用4.WeakMap的应用 三、ES6的其它知识点说明 一、Set集合 1.Set的基本使用 在ES6之前&#xff0c;我们存储数据的结构主要有两种&…

v-for列表渲染

一、v-for迭代数组 <li v-for"(e,index) in emp" :key"e.id">编号{{index1}} 名字{{e.name}} 年龄{{e.age}} </li> e 是循环数组中的每个元素的别名index 是当前循环的下表&#xff0c;从0开始:key 的作用&#xff1a; 是为了给 Vue 一个提示…

网络协议--动态选路协议

10.1 引言 在前面各章中&#xff0c;我们讨论了静态选路。在配置接口时&#xff0c;以默认方式生成路由表项&#xff08;对于直接连接的接口&#xff09;&#xff0c;并通过route命令增加表项&#xff08;通常从系统自引导程序文件&#xff09;&#xff0c;或是通过ICMP重定向…

清除excel中换行符方法

1、选择要删除或替换换行符的单元格。 2、按 Ctrl H 以打开“查找和替换”对话框。 3、在“查找内容”栏中输入Ctrl J 或 Ctrl Enter 这时会出现一个闪烁的小点。如下图所示&#xff0c;然后点击全部替换即可。

Linux环境下C++配置Json库

一、准备好压缩包 1、下载Jsoncpp https://sourceforge.net/projects/jsoncpp/files/ 2、下载scons https://sourceforge.net/projects/scons/files/scons/2.1.0/scons-2.1.0.tar.gz/download 二.配置 1.创建一个json文件夹&#xff0c;使用Xftp将压缩包导入文件夹 2.使用 …

memmove内存拷贝函数

目录 一、memmove内存拷贝函数 二、memmove与memcpy的区别 三、模拟实现memmove函数 一、memmove内存拷贝函数 1.头文件&#xff1a;string.h 2.函数原型&#xff1a;void *(void* destination , const void* source , size_t num) 3.函数功能&#xff1a;将源地址空间中的…

物联网_01_物理设备的网络接入

设备的网络接入及物理层使用到的传输协议 现在物理设备有两种接入方式,一种是直接接入另一种是网关接入 直接接入 给物理设备添加NB-IOT通信模组等设备使之具有直接接入网络的能力 网关接入 物理设备在本地组网后通过统一的网关接入到网络(ZigBee无线组网网关).网关是处在本地…

Flutter页面滑动回调处理解决方法

文章目录 TabBarViewTabBarView简介TabBarView详细介绍 TabBarView滑动时如何处理事务例子 PageControllerPageController介绍PageController 的详细介绍 TabBarView TabBarView简介 TabBarView 是 Flutter 中的一个用于显示选项卡视图的小部件。它通常与 TabBar 一起使用&am…

分布式事务 学习

分布式事务 关系型数据库事务&#xff08;本地事务&#xff09; 原子性&#xff1a;构成事务的所有操作&#xff0c;要么都执行完成&#xff0c;要么都不执行/一致性&#xff1a;在事务执行前后&#xff0c;数据库的一致性约束没有被破坏。隔离性&#xff1a;并发的两个事务的…

【Java小知识点】类加载器的区别

&#x1f384;欢迎来到边境矢梦的csdn博文&#x1f384; &#x1f384;本文主要梳理Java类加载器的区别&#x1f384; &#x1f308;我是边境矢梦&#xff0c;一个正在为秋招和算法竞赛做准备的学生&#x1f308; &#x1f386;喜欢的朋友可以关注一下&#x1faf0;&#x1faf…

【SpringCloud微服务项目实战-mall4cloud项目(5)】——mall4cloud-leaf

mall4cloud-leaf 基于美团leaf的生成id服务 分布式id介绍具体代码及使用项目中的生成id模式具体代码分布式id生成使用 分布式id介绍 分布式ID&#xff08;Distributed ID&#xff09;是在分布式计算环境中生成的唯一标识符或标识号。在分布式系统中&#xff0c;通常需要唯一标…

Node学习笔记之path模块

path 模块提供了 操作路径 的功能&#xff0c;我们将介绍如下几个较为常用的几个 API&#xff1a; API 说明 path.resolve 拼接规范的绝对路径常用 path.sep 获取操作系统的路径分隔符 path.parse 解析路径并返回对象 path.basename 获取路径的基础名称 path.dirname…

【深度学习】数据集最常见的问题及其解决方案

简介 如果您还没有听过&#xff0c;请告诉您一个事实&#xff0c;作为一名数据科学家&#xff0c;您应该始终站在一个角落跟你说&#xff1a;“你的结果与你的数据一样好。” 尝试通过提高模型能力来弥补糟糕的数据是许多人会犯的错误。这相当于你因为原来的汽车使用了劣质汽…

SL8541 android系统环境+编译

1.Ubuntu系统的安装 最好使用ubuntu18.0.4 2.工具环境包的安装 // 安装Android8.1源码编译环境 sudo apt-get install openjdk-8-jdk --------------ok sudo apt-get install libx11-dev:i386 libreadline6-dev:i386 libgl1-mesa-dev g-multilib --------------ok sudo…