diff --git a/backend/src/main/java/io/metersphere/api/controller/ApiJmeterFileController.java b/backend/src/main/java/io/metersphere/api/controller/ApiJmeterFileController.java index 30b7c2aca1..ecbac9e107 100644 --- a/backend/src/main/java/io/metersphere/api/controller/ApiJmeterFileController.java +++ b/backend/src/main/java/io/metersphere/api/controller/ApiJmeterFileController.java @@ -1,15 +1,15 @@ package io.metersphere.api.controller; +import io.metersphere.api.dto.scenario.request.BodyFile; import io.metersphere.api.service.ApiJmeterFileService; +import org.apache.dubbo.common.utils.CollectionUtils; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; +import java.util.List; import java.util.UUID; @RestController @@ -19,15 +19,23 @@ public class ApiJmeterFileController { @Resource private ApiJmeterFileService apiJmeterFileService; - @GetMapping("download") - public ResponseEntity downloadJmeterFiles(@RequestParam("testId") String testId, @RequestParam("reportId") String reportId, @RequestParam("runMode") String runMode, @RequestParam("testPlanScenarioId") String testPlanScenarioId) { - byte[] bytes = apiJmeterFileService.downloadJmeterFiles(runMode,testId, reportId, testPlanScenarioId); + @PostMapping("download/files") + public ResponseEntity downloadJmeterFiles(@RequestBody List bodyFileList) { + byte[] bytes = new byte[10]; + if (CollectionUtils.isNotEmpty(bodyFileList)) { + bytes = apiJmeterFileService.downloadJmeterFiles(bodyFileList); + } return ResponseEntity.ok() .contentType(MediaType.parseMediaType("application/octet-stream")) - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + testId + ".zip\"") + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + UUID.randomUUID().toString() + ".zip\"") .body(bytes); } + @GetMapping("download") + public byte[] downloadJmx(@RequestParam("testId") String testId, @RequestParam("reportId") String reportId, @RequestParam("runMode") String runMode, @RequestParam("testPlanScenarioId") String testPlanScenarioId) { + return apiJmeterFileService.downloadJmx(runMode, testId, reportId, testPlanScenarioId); + } + @GetMapping("download/jar") public ResponseEntity downloadJmeterFiles() { byte[] bytes = apiJmeterFileService.downloadJmeterJar(); diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/RunModeConfig.java b/backend/src/main/java/io/metersphere/api/dto/automation/RunModeConfig.java index fb8b9b2515..00b85c4c46 100644 --- a/backend/src/main/java/io/metersphere/api/dto/automation/RunModeConfig.java +++ b/backend/src/main/java/io/metersphere/api/dto/automation/RunModeConfig.java @@ -1,8 +1,11 @@ package io.metersphere.api.dto.automation; +import io.metersphere.api.dto.JvmInfoDTO; +import io.metersphere.dto.BaseSystemConfigDTO; import lombok.Data; import java.util.Map; +import java.util.List; @Data public class RunModeConfig { @@ -12,7 +15,8 @@ public class RunModeConfig { private String reportId; private boolean onSampleError; private String resourcePoolId; - + private BaseSystemConfigDTO baseInfo; + private List testResources; /** * 运行环境 */ diff --git a/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerHandler.java b/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerHandler.java index 4b14129810..ce5f9172cc 100644 --- a/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerHandler.java +++ b/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerHandler.java @@ -7,8 +7,6 @@ import io.metersphere.api.service.MsResultService; import io.metersphere.api.service.TestResultService; import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.samplers.SampleResult; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.util.Comparator; @@ -20,8 +18,6 @@ import java.util.Map; * 获取结果和数据库操作分离 * 减少占用的数据库连接 */ -@Service -@Transactional(rollbackFor = Exception.class) public class APIBackendListenerHandler { @Resource diff --git a/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java b/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java index acf3f163b4..32126e8857 100644 --- a/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java +++ b/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java @@ -1,6 +1,7 @@ package io.metersphere.api.jmeter; import com.alibaba.fastjson.JSON; +import io.metersphere.api.dto.JvmInfoDTO; import io.metersphere.api.dto.RunRequest; import io.metersphere.api.dto.automation.ExecuteType; import io.metersphere.api.dto.automation.RunModeConfig; @@ -10,9 +11,9 @@ import io.metersphere.base.domain.TestResourcePool; import io.metersphere.base.mapper.TestResourcePoolMapper; import io.metersphere.commons.constants.ApiRunMode; import io.metersphere.commons.constants.ResourcePoolTypeEnum; +import io.metersphere.commons.constants.TriggerMode; import io.metersphere.commons.exception.MSException; -import io.metersphere.commons.utils.CommonBeanFactory; -import io.metersphere.commons.utils.LogUtil; +import io.metersphere.commons.utils.*; import io.metersphere.config.JmeterProperties; import io.metersphere.dto.BaseSystemConfigDTO; import io.metersphere.dto.NodeDTO; @@ -20,7 +21,15 @@ import io.metersphere.i18n.Translator; import io.metersphere.performance.engine.Engine; import io.metersphere.performance.engine.EngineFactory; import io.metersphere.service.SystemParameterService; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; import org.apache.jmeter.config.Arguments; import org.apache.jmeter.save.SaveService; import org.apache.jmeter.testelement.TestElement; @@ -35,9 +44,9 @@ import org.springframework.web.client.RestTemplate; import javax.annotation.PostConstruct; import javax.annotation.Resource; -import java.io.File; -import java.io.InputStream; +import java.io.*; import java.lang.reflect.Field; +import java.util.List; @Service @Transactional(rollbackFor = Exception.class) @@ -128,17 +137,21 @@ public class JMeterService { init(); FixedTask.tasks.put(testId, System.currentTimeMillis()); addBackendListener(testId, debugReportId, runMode, testPlan); - if (ExecuteType.Debug.name().equals(debugReportId) || ApiRunMode.SCENARIO.name().equals(runMode)) { + if (ExecuteType.Debug.name().equals(debugReportId) || (ApiRunMode.SCENARIO.name().equals(runMode) && !TriggerMode.BATCH.name().equals(debugReportId))) { addResultCollector(testId, testPlan); } LocalRunner runner = new LocalRunner(testPlan); runner.run(testId); } - public void runTest(String testId, String reportId, String runMode, String testPlanScenarioId, RunModeConfig config) { + public void runTest(String testId, String reportId, String runMode, + String testPlanScenarioId, RunModeConfig config) { // 获取可以执行的资源池 String resourcePoolId = config.getResourcePoolId(); - BaseSystemConfigDTO baseInfo = CommonBeanFactory.getBean(SystemParameterService.class).getBaseInfo(); + BaseSystemConfigDTO baseInfo = config.getBaseInfo(); + if (baseInfo == null) { + baseInfo = CommonBeanFactory.getBean(SystemParameterService.class).getBaseInfo(); + } RunRequest runRequest = new RunRequest(); runRequest.setTestId(testId); runRequest.setReportId(reportId); @@ -166,15 +179,24 @@ public class JMeterService { MSException.throwException(e.getMessage()); } } else { - TestResource testResource = resourcePoolCalculation.getPool(resourcePoolId); + TestResource testResource = null; + List testResources = config.getTestResources(); + if (CollectionUtils.isEmpty(testResources)) { + testResource = resourcePoolCalculation.getPool(resourcePoolId); + } else { + int index = (int) (Math.random() * testResources.size()); + JvmInfoDTO jvmInfoDTO = testResources.get(index); + testResource = testResources.get(index).getTestResource(); + } String configuration = testResource.getConfiguration(); NodeDTO node = JSON.parseObject(configuration, NodeDTO.class); String nodeIp = node.getIp(); Integer port = node.getPort(); try { String uri = String.format(BASE_URL + "/jmeter/api/start", nodeIp, port); - ResponseEntity result = restTemplate.postForEntity(uri, runRequest, String.class); - if (result == null || !StringUtils.equals("SUCCESS", result.getBody())) { + ResponseEntity resultEntity = restTemplate.postForEntity(uri, runRequest, String.class); + String result = resultEntity.getBody(); // this.send(uri, runRequest); + if (StringUtils.isEmpty(result) || !StringUtils.equals("SUCCESS", result)) { // 清理零时报告 ApiScenarioReportService apiScenarioReportService = CommonBeanFactory.getBean(ApiScenarioReportService.class); apiScenarioReportService.delete(reportId); @@ -182,7 +204,31 @@ public class JMeterService { } } catch (Exception e) { e.printStackTrace(); - MSException.throwException(e.getMessage()); + MSException.throwException(runRequest.getReportId() + ":" + e.getMessage()); + } + } + } + + public String send(String webhook, RunRequest runRequest) { + CloseableHttpClient httpClient = HttpClients.createDefault(); + CloseableHttpResponse response = null; + try { + // 创建Http Post请求 + HttpPost httpPost = new HttpPost(webhook); + // 创建请求内容 + StringEntity entity = new StringEntity(JSON.toJSONString(runRequest), ContentType.APPLICATION_JSON); + httpPost.setEntity(entity); + // 执行http请求 + response = httpClient.execute(httpPost); + String result = EntityUtils.toString(response.getEntity()); + return result; + } catch (Exception e) { + return e.getMessage(); + } finally { + try { + response.close(); + } catch (IOException e) { + e.printStackTrace(); } } } diff --git a/backend/src/main/java/io/metersphere/api/jmeter/MsKafkaListener.java b/backend/src/main/java/io/metersphere/api/jmeter/MsKafkaListener.java index ebdfe3e4f9..61311e37be 100644 --- a/backend/src/main/java/io/metersphere/api/jmeter/MsKafkaListener.java +++ b/backend/src/main/java/io/metersphere/api/jmeter/MsKafkaListener.java @@ -10,12 +10,10 @@ import org.apache.commons.lang3.StringUtils; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; @Service -@Transactional(rollbackFor = Exception.class) public class MsKafkaListener { public static final String TOPICS = "ms-api-exec-topic"; public static final String CONSUME_ID = "ms-api-exec-consume"; @@ -23,7 +21,11 @@ public class MsKafkaListener { @KafkaListener(id = CONSUME_ID, topics = TOPICS, groupId = "${spring.kafka.consumer.group-id}") public void consume(ConsumerRecord record) { LogUtil.info("接收到执行结果开始存储"); - this.save(record.value()); + try { + this.save(record.value()); + } catch (Exception e) { + LogUtil.error(e.getMessage()); + } LogUtil.info("执行内容存储结束"); } diff --git a/backend/src/main/java/io/metersphere/api/jmeter/ResourcePoolCalculation.java b/backend/src/main/java/io/metersphere/api/jmeter/ResourcePoolCalculation.java index ee10a96d79..365e0aa471 100644 --- a/backend/src/main/java/io/metersphere/api/jmeter/ResourcePoolCalculation.java +++ b/backend/src/main/java/io/metersphere/api/jmeter/ResourcePoolCalculation.java @@ -16,6 +16,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.client.RestTemplate; import javax.annotation.Resource; +import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -41,7 +42,7 @@ public class ResourcePoolCalculation { example.createCriteria().andStatusEqualTo("VALID").andTypeEqualTo("NODE").andIdEqualTo(resourcePoolId); List pools = testResourcePoolMapper.selectByExample(example); // 按照NODE节点的可用内存空间大小排序 - JvmInfoDTO jvmInfoDTO = null; + List availableNodes = new ArrayList<>(); if (CollectionUtils.isNotEmpty(pools)) { List poolIds = pools.stream().map(pool -> pool.getId()).collect(Collectors.toList()); TestResourceExample resourceExample = new TestResourceExample(); @@ -57,19 +58,49 @@ public class ResourcePoolCalculation { if (nodeJvm == null) { continue; } - // 优先取资源充足的节点,如果当前节点资源超过1G就不需要在排序了 - if (nodeJvm.getVmFree() > 1024) { - return testResource; - } - if (jvmInfoDTO == null || jvmInfoDTO.getVmFree() < nodeJvm.getVmFree()) { - jvmInfoDTO = nodeJvm; - jvmInfoDTO.setTestResource(testResource); - } + nodeJvm.setTestResource(testResource); + availableNodes.add(nodeJvm); } } - if (jvmInfoDTO == null || jvmInfoDTO.getTestResource() == null) { + if (CollectionUtils.isEmpty(availableNodes)) { MSException.throwException("未获取到资源池,请检查配置【系统设置-系统-测试资源池】"); } + int index = (int) (Math.random() * availableNodes.size()); + JvmInfoDTO jvmInfoDTO = availableNodes.get(index); return jvmInfoDTO.getTestResource(); } + + + public List getPools(String resourcePoolId) { + // 获取可以执行的资源池 + TestResourcePoolExample example = new TestResourcePoolExample(); + example.createCriteria().andStatusEqualTo("VALID").andTypeEqualTo("NODE").andIdEqualTo(resourcePoolId); + List pools = testResourcePoolMapper.selectByExample(example); + + // 按照NODE节点的可用内存空间大小排序 + List availableNodes = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(pools)) { + List poolIds = pools.stream().map(pool -> pool.getId()).collect(Collectors.toList()); + TestResourceExample resourceExample = new TestResourceExample(); + resourceExample.createCriteria().andTestResourcePoolIdIn(poolIds); + List testResources = testResourceMapper.selectByExampleWithBLOBs(resourceExample); + for (TestResource testResource : testResources) { + String configuration = testResource.getConfiguration(); + NodeDTO node = JSON.parseObject(configuration, NodeDTO.class); + String nodeIp = node.getIp(); + Integer port = node.getPort(); + String uri = String.format(BASE_URL + "/jmeter/getJvmInfo", nodeIp, port); + JvmInfoDTO nodeJvm = this.getNodeJvmInfo(uri); + if (nodeJvm == null) { + continue; + } + nodeJvm.setTestResource(testResource); + availableNodes.add(nodeJvm); + } + } + if (CollectionUtils.isEmpty(availableNodes)) { + MSException.throwException("未获取到资源池,请检查配置【系统设置-系统-测试资源池】"); + } + return availableNodes; + } } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java index 0fb69691d7..f41249a524 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java @@ -23,6 +23,7 @@ import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import io.metersphere.api.jmeter.JMeterService; import io.metersphere.api.jmeter.MessageCache; import io.metersphere.api.jmeter.ReportCounter; +import io.metersphere.api.jmeter.ResourcePoolCalculation; import io.metersphere.api.parse.ApiImportParser; import io.metersphere.api.service.task.ParallelScenarioExecTask; import io.metersphere.api.service.task.SerialScenarioExecTask; @@ -129,6 +130,8 @@ public class ApiAutomationService { private TcpApiParamService tcpApiParamService; @Resource private ApiScenarioReferenceIdService apiScenarioReferenceIdService; + @Resource + private ResourcePoolCalculation resourcePoolCalculation; public ApiScenarioWithBLOBs getDto(String id) { return apiScenarioMapper.selectByPrimaryKey(id); @@ -813,7 +816,7 @@ public class ApiAutomationService { return null; } - public APIScenarioReportResult createScenarioReport(String id, String scenarioId, String scenarioName, String triggerMode, String execType, String projectId, String userID, RunModeConfig config,String desc) { + public APIScenarioReportResult createScenarioReport(String id, String scenarioId, String scenarioName, String triggerMode, String execType, String projectId, String userID, RunModeConfig config, String desc) { APIScenarioReportResult report = new APIScenarioReportResult(); if (triggerMode.equals(ApiRunMode.SCENARIO.name()) || triggerMode.equals(ApiRunMode.DEFINITION.name())) { triggerMode = ReportTriggerMode.MANUAL.name(); @@ -989,6 +992,11 @@ public class ApiAutomationService { if (apiScenarios != null && apiScenarios.size() == 1 && (apiScenarios.get(0).getStepTotal() == null || apiScenarios.get(0).getStepTotal() == 0)) { MSException.throwException((apiScenarios.get(0).getName() + "," + Translator.get("automation_exec_info"))); } + // 资源池 + if (request.getConfig() != null && StringUtils.isNotEmpty(request.getConfig().getResourcePoolId())) { + List testResources = resourcePoolCalculation.getPools(request.getConfig().getResourcePoolId()); + request.getConfig().setTestResources(testResources); + } // 环境检查 this.checkEnv(request, apiScenarios); // 集合报告设置 @@ -1026,21 +1034,21 @@ public class ApiAutomationService { planEnvMap = JSON.parseObject(environment, Map.class); } } - String projectId = testPlanScenarioCaseService.getProjectIdById(testPlanScenarioId); - if(StringUtils.isEmpty(projectId)){ + String projectId = testPlanScenarioCaseService.getProjectIdById(testPlanScenarioId); + if (StringUtils.isEmpty(projectId)) { projectId = item.getProjectId(); } if (request.isTestPlanScheduleJob()) { String savedScenarioId = testPlanScenarioId + ":" + request.getTestPlanReportId(); report = createScenarioReport(reportId, savedScenarioId, item.getName(), request.getTriggerMode(), - request.getExecuteType(), projectId, request.getReportUserID(), request.getConfig(),item.getId()); + request.getExecuteType(), projectId, request.getReportUserID(), request.getConfig(), item.getId()); } else { report = createScenarioReport(reportId, testPlanScenarioId, item.getName(), request.getTriggerMode(), - request.getExecuteType(), projectId, request.getReportUserID(), request.getConfig(),item.getId()); + request.getExecuteType(), projectId, request.getReportUserID(), request.getConfig(), item.getId()); } } else { report = createScenarioReport(reportId, ExecuteType.Marge.name().equals(request.getExecuteType()) ? serialReportId : item.getId(), item.getName(), request.getTriggerMode(), - request.getExecuteType(), item.getProjectId(), request.getReportUserID(), request.getConfig(),item.getId()); + request.getExecuteType(), item.getProjectId(), request.getReportUserID(), request.getConfig(), item.getId()); } try { if (request.getConfig() != null && StringUtils.isNotBlank(request.getConfig().getResourcePoolId())) { @@ -1064,7 +1072,7 @@ public class ApiAutomationService { APIScenarioReportResult report = createScenarioReport(request.getConfig().getReportId(), JSON.toJSONString(CollectionUtils.isNotEmpty(scenarioIds) && scenarioIds.size() > 50 ? scenarioIds.subList(0, 50) : scenarioIds), scenarioNames.length() >= 3000 ? scenarioNames.substring(0, 2000) : scenarioNames.deleteCharAt(scenarioNames.toString().length() - 1).toString(), - ReportTriggerMode.MANUAL.name(), ExecuteType.Saved.name(), request.getProjectId(), request.getReportUserID(), request.getConfig(),JSON.toJSONString(scenarioIds)); + ReportTriggerMode.MANUAL.name(), ExecuteType.Saved.name(), request.getProjectId(), request.getReportUserID(), request.getConfig(), JSON.toJSONString(scenarioIds)); report.setName(request.getConfig().getReportName()); report.setId(serialReportId); @@ -1087,7 +1095,7 @@ public class ApiAutomationService { private void run(Map executeQueue, RunScenarioRequest request, String serialReportId) { // 开始选择执行模式 if (executeQueue != null && executeQueue.size() > 0) { - ExecutorService executorService = Executors.newFixedThreadPool(executeQueue.size()); + ExecutorService executorService = Executors.newFixedThreadPool(6); SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); ApiScenarioReportMapper batchMapper = sqlSession.getMapper(ApiScenarioReportMapper.class); if (request.getConfig() != null && request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString())) { @@ -1175,9 +1183,11 @@ public class ApiAutomationService { //存储报告 APIScenarioReportResult report = executeQueue.get(reportId).getReport(); batchMapper.insert(report); - executorService.submit(new ParallelScenarioExecTask(jMeterService, executeQueue.get(reportId), request)); } sqlSession.flushStatements(); + for (String reportId : executeQueue.keySet()) { + executorService.submit(new ParallelScenarioExecTask(jMeterService, executeQueue.get(reportId), request)); + } } } } @@ -1256,22 +1266,22 @@ public class ApiAutomationService { } } - String projectId = testPlanScenarioCaseService.getProjectIdById(testPlanScenarioId); - if(StringUtils.isEmpty(projectId)){ + String projectId = testPlanScenarioCaseService.getProjectIdById(testPlanScenarioId); + if (StringUtils.isEmpty(projectId)) { projectId = item.getProjectId(); } if (request.isTestPlanScheduleJob()) { String savedScenarioId = testPlanScenarioId + ":" + request.getTestPlanReportId(); report = createScenarioReport(group.getName(), savedScenarioId, item.getName(), request.getTriggerMode(), - request.getExecuteType(), projectId, request.getReportUserID(), request.getConfig(),item.getId()); + request.getExecuteType(), projectId, request.getReportUserID(), request.getConfig(), item.getId()); } else { report = createScenarioReport(group.getName(), testPlanScenarioId, item.getName(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(), - request.getExecuteType(), projectId, request.getReportUserID(), request.getConfig(),item.getId()); + request.getExecuteType(), projectId, request.getReportUserID(), request.getConfig(), item.getId()); } } else { report = createScenarioReport(group.getName(), item.getId(), item.getName(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(), - request.getExecuteType(), item.getProjectId(), request.getReportUserID(), request.getConfig(),item.getId()); + request.getExecuteType(), item.getProjectId(), request.getReportUserID(), request.getConfig(), item.getId()); } batchMapper.insert(report); reportIds.add(group.getName()); @@ -1380,7 +1390,7 @@ public class ApiAutomationService { String runMode = ApiRunMode.SCENARIO.name(); if (StringUtils.isNotBlank(request.getRunMode()) && StringUtils.equals(request.getRunMode(), ApiRunMode.SCENARIO_PLAN.name())) { runMode = ApiRunMode.SCENARIO_PLAN.name(); - }else if (StringUtils.isNotBlank(request.getRunMode()) && StringUtils.equals(request.getRunMode(), ApiRunMode.SCHEDULE_SCENARIO.name())) { + } else if (StringUtils.isNotBlank(request.getRunMode()) && StringUtils.equals(request.getRunMode(), ApiRunMode.SCHEDULE_SCENARIO.name())) { runMode = ApiRunMode.SCHEDULE_SCENARIO.name(); } if (StringUtils.isNotBlank(request.getRunMode()) && StringUtils.equals(request.getRunMode(), ApiRunMode.DEFINITION.name())) { @@ -1490,7 +1500,7 @@ public class ApiAutomationService { } APIScenarioReportResult report = createScenarioReport(request.getId(), request.getScenarioId(), request.getScenarioName(), ReportTriggerMode.MANUAL.name(), request.getExecuteType(), request.getProjectId(), - SessionUtils.getUserId(), request.getConfig(),request.getId()); + SessionUtils.getUserId(), request.getConfig(), request.getId()); apiScenarioReportMapper.insert(report); uploadBodyFiles(request.getBodyFileRequestIds(), bodyFiles); @@ -2393,7 +2403,7 @@ public class ApiAutomationService { example.createCriteria().andNameEqualTo(newModel.getName()). andProjectIdEqualTo(newModel.getProjectId()).andStatusNotEqualTo("Trash").andIdNotEqualTo(newModel.getId()); if (apiScenarioMapper.countByExample(example) > 0) { - stringBuffer.append(newModel.getName()+";"); + stringBuffer.append(newModel.getName() + ";"); continue; } else { boolean insertFlag = true; @@ -2426,11 +2436,11 @@ public class ApiAutomationService { } BatchOperaResponse result = new BatchOperaResponse(); - if(stringBuffer.length() == 0){ + if (stringBuffer.length() == 0) { result.result = true; - }else { + } else { result.result = false; - result.errorMsg = stringBuffer.substring(0,stringBuffer.length()-1); + result.errorMsg = stringBuffer.substring(0, stringBuffer.length() - 1); } return result; } @@ -2497,16 +2507,16 @@ public class ApiAutomationService { public void initExecuteTimes() { List apiScenarioIds = extApiScenarioMapper.selectIdsByExecuteTimeIsNull(); - Map scenarioIdMap = new HashMap<>(); + Map scenarioIdMap = new HashMap<>(); List reportCount = apiScenarioReportService.countByApiScenarioId(); for (ApiReportCountDTO dto : reportCount) { - scenarioIdMap.put(dto.getId(),dto.getCountNum()); + scenarioIdMap.put(dto.getId(), dto.getCountNum()); } - for (String id:apiScenarioIds) { + for (String id : apiScenarioIds) { int count = 0; - if(scenarioIdMap.containsKey(id)){ + if (scenarioIdMap.containsKey(id)) { Long countNum = scenarioIdMap.get(id); - if(countNum != null){ + if (countNum != null) { count = countNum.intValue(); } } @@ -2519,9 +2529,9 @@ public class ApiAutomationService { public long countExecuteTimesByProjectID(String projectId) { Long result = extApiScenarioMapper.countExecuteTimesByProjectID(projectId); - if(result == null){ + if (result == null) { return 0; - }else { + } else { return result.longValue(); } } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java index 6c5cf78779..ff24cf8ac6 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java @@ -6,6 +6,7 @@ import io.metersphere.api.dto.APIReportResult; import io.metersphere.api.dto.ApiTestImportRequest; import io.metersphere.api.dto.automation.ApiScenarioRequest; import io.metersphere.api.dto.automation.ReferenceDTO; +import io.metersphere.api.dto.automation.RunModeConfig; import io.metersphere.api.dto.datacount.ApiDataCountResult; import io.metersphere.api.dto.definition.*; import io.metersphere.api.dto.definition.parse.ApiDefinitionImport; @@ -686,7 +687,9 @@ public class ApiDefinitionService { // 调用执行方法 if (request.getConfig() != null && StringUtils.isNotBlank(request.getConfig().getResourcePoolId())) { - jMeterService.runTest(request.getId(), request.getId(), runMode, null, request.getConfig()); + RunModeConfig configs = request.getConfig(); + configs.setBaseInfo(CommonBeanFactory.getBean(SystemParameterService.class).getBaseInfo()); + jMeterService.runTest(request.getId(), request.getId(), runMode, null, configs); } else { jMeterService.runLocal(request.getId(), hashTree, request.getReportId(), runMode); } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiJmeterFileService.java b/backend/src/main/java/io/metersphere/api/service/ApiJmeterFileService.java index f8e2f5294f..6d0bf8662f 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiJmeterFileService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiJmeterFileService.java @@ -3,7 +3,6 @@ package io.metersphere.api.service; import com.alibaba.fastjson.JSON; import io.metersphere.api.dto.definition.request.MsTestPlan; import io.metersphere.api.dto.scenario.request.BodyFile; -import io.metersphere.api.jmeter.JMeterService; import io.metersphere.base.domain.ApiScenarioWithBLOBs; import io.metersphere.base.domain.JarConfig; import io.metersphere.base.domain.TestPlanApiScenario; @@ -20,7 +19,6 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.jorphan.collections.HashTree; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.io.ByteArrayOutputStream; @@ -31,7 +29,6 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @Service -@Transactional(rollbackFor = Exception.class) public class ApiJmeterFileService { @Resource @@ -42,10 +39,20 @@ public class ApiJmeterFileService { private TestPlanApiScenarioMapper testPlanApiScenarioMapper; @Resource private ApiScenarioMapper apiScenarioMapper; - @Resource - private JMeterService jMeterService; - public byte[] downloadJmeterFiles(String runMode, String testId, String reportId, String testPlanScenarioId) { + public byte[] downloadJmeterFiles(List bodyFileList) { + Map files = new LinkedHashMap<>(); + Map multipartFiles = this.getMultipartFiles(bodyFileList); + if (!com.alibaba.excel.util.CollectionUtils.isEmpty(multipartFiles)) { + for (String k : multipartFiles.keySet()) { + byte[] v = multipartFiles.get(k); + files.put(k, v); + } + } + return listBytesToZip(files); + } + + public byte[] downloadJmx(String runMode, String testId, String reportId, String testPlanScenarioId) { Map planEnvMap = new HashMap<>(); if (StringUtils.isNotEmpty(testPlanScenarioId)) { // 获取场景用例单独的执行环境 @@ -66,7 +73,8 @@ public class ApiJmeterFileService { hashTree = apiAutomationService.generateHashTree(item, reportId, planEnvMap); } //jMeterService.addBackendListener(reportId, hashTree); - return zipFilesToByteArray(testId, hashTree); + String jmx = new MsTestPlan().getJmx(hashTree); + return jmx.getBytes(StandardCharsets.UTF_8); } public byte[] downloadJmeterJar() { @@ -124,6 +132,23 @@ public class ApiJmeterFileService { return multipartFiles; } + private Map getMultipartFiles(List files) { + Map multipartFiles = new LinkedHashMap<>(); + // 获取附件 + if (CollectionUtils.isNotEmpty(files)) { + for (BodyFile bodyFile : files) { + File file = new File(bodyFile.getName()); + if (file != null && !file.exists()) { + byte[] fileByte = FileUtils.fileToByte(file); + if (fileByte != null) { + multipartFiles.put(file.getName(), fileByte); + } + } + } + } + return multipartFiles; + } + private byte[] zipFilesToByteArray(String testId, HashTree hashTree) { String fileName = testId + ".jmx"; String jmx = new MsTestPlan().getJmx(hashTree); @@ -141,6 +166,11 @@ public class ApiJmeterFileService { return listBytesToZip(files); } + private byte[] fileToByteArray(HashTree hashTree) { + String jmx = new MsTestPlan().getJmx(hashTree); + return jmx.getBytes(StandardCharsets.UTF_8); + } + private byte[] listBytesToZip(Map mapReport) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); diff --git a/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java b/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java index 5e446f254a..865382027b 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java @@ -296,7 +296,7 @@ public class ApiScenarioReportService { List reportIds = new ArrayList<>(); List scenarioIdList = new ArrayList<>(); Map scenarioAndErrorMap = new HashMap<>(); - Map caseReportMap = new HashMap<>(); + Map caseReportMap = new HashMap<>(); for (ScenarioResult scenarioResult : scenarioResultList) { // 存储场景报告 @@ -387,7 +387,7 @@ public class ApiScenarioReportService { lastReport = report; APIScenarioReportResult reportResult = this.get(report.getId()); - caseReportMap.put(testPlanApiScenario.getApiScenarioId(),reportResult); + caseReportMap.put(testPlanApiScenario.getApiScenarioId(), reportResult); reportIds.add(report.getId()); } TestPlanReportService testPlanReportService = CommonBeanFactory.getBean(TestPlanReportService.class); @@ -395,8 +395,8 @@ public class ApiScenarioReportService { testPlanLog.info("TestPlanReportId" + JSONArray.toJSONString(testPlanReportIdList) + " EXECUTE OVER. SCENARIO STATUS : " + JSONObject.toJSONString(scenarioAndErrorMap)); for (String reportId : testPlanReportIdList) { - TestPlanReportExecuteCatch.updateApiTestPlanExecuteInfo(reportId,null,scenarioAndErrorMap,null); - TestPlanReportExecuteCatch.updateTestPlanExecuteResultInfo(reportId,null,caseReportMap,null); + TestPlanReportExecuteCatch.updateApiTestPlanExecuteInfo(reportId, null, scenarioAndErrorMap, null); + TestPlanReportExecuteCatch.updateTestPlanExecuteResultInfo(reportId, null, caseReportMap, null); } return lastReport; @@ -550,7 +550,6 @@ public class ApiScenarioReportService { public ApiScenarioReport updateScenario(TestResult result) { // 针对未正常返回结果的报告计数 counter(result); - ApiScenarioReport lastReport = null; for (ScenarioResult item : result.getScenarios()) { // 更新报告状态 @@ -599,6 +598,7 @@ public class ApiScenarioReportService { if (obj != null) { ReportCounter counter = (ReportCounter) obj; counter.setNumber(counter.getNumber() + 1); + System.out.println("得到统计数量:" + counter.getNumber()); MessageCache.cache.put(report.getScenarioId(), counter); } } @@ -623,7 +623,9 @@ public class ApiScenarioReportService { } Map paramMap = new HashMap<>(beanMap); - paramMap.put("operator", SessionUtils.getUser().getName()); + if (SessionUtils.getUser() != null) { + paramMap.put("operator", SessionUtils.getUser().getName()); + } paramMap.put("status", result.getLastResult()); String context = "${operator}执行接口自动化" + status + ": ${name}"; NoticeModel noticeModel = NoticeModel.builder() diff --git a/backend/src/main/java/io/metersphere/api/service/TestResultService.java b/backend/src/main/java/io/metersphere/api/service/TestResultService.java index c3fe7955b8..5d5a73629f 100644 --- a/backend/src/main/java/io/metersphere/api/service/TestResultService.java +++ b/backend/src/main/java/io/metersphere/api/service/TestResultService.java @@ -16,7 +16,6 @@ import io.metersphere.notice.service.NoticeSendService; import io.metersphere.service.SystemParameterService; import io.metersphere.track.request.testcase.TrackCount; import io.metersphere.track.service.TestPlanApiCaseService; -import io.metersphere.track.service.TestPlanReportService; import io.metersphere.track.service.TestPlanScenarioCaseService; import io.metersphere.track.service.TestPlanTestCaseService; import org.apache.commons.collections4.CollectionUtils; @@ -42,8 +41,6 @@ public class TestResultService { @Resource private ApiDefinitionExecResultService apiDefinitionExecResultService; @Resource - private TestPlanReportService testPlanReportService; - @Resource private ApiScenarioReportService apiScenarioReportService; @Resource private ApiTestCaseService apiTestCaseService; @@ -158,6 +155,7 @@ public class TestResultService { } } } catch (Exception e) { + e.printStackTrace(); LogUtil.error(e.getMessage(), e); } } diff --git a/backend/src/main/java/io/metersphere/api/service/task/ParallelScenarioExecTask.java b/backend/src/main/java/io/metersphere/api/service/task/ParallelScenarioExecTask.java index ecb34a1618..d280c25178 100644 --- a/backend/src/main/java/io/metersphere/api/service/task/ParallelScenarioExecTask.java +++ b/backend/src/main/java/io/metersphere/api/service/task/ParallelScenarioExecTask.java @@ -6,6 +6,7 @@ package io.metersphere.api.service.task; import io.metersphere.api.dto.RunModeDataDTO; import io.metersphere.api.dto.automation.RunScenarioRequest; import io.metersphere.api.jmeter.JMeterService; +import io.metersphere.commons.constants.TriggerMode; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.LogUtil; import org.apache.commons.lang3.StringUtils; @@ -26,10 +27,10 @@ public class ParallelScenarioExecTask implements Callable { @Override public T call() { try { - if (request.getConfig() != null && StringUtils.isNotBlank(request.getConfig().getResourcePoolId())) { + if (request.getConfig() != null && StringUtils.isNotEmpty(request.getConfig().getResourcePoolId())) { jMeterService.runTest(runModeDataDTO.getTestId(), runModeDataDTO.getReport().getId(), request.getRunMode(), request.getPlanScenarioId(), request.getConfig()); } else { - jMeterService.runLocal(runModeDataDTO.getReport().getId(), runModeDataDTO.getHashTree(), request.getReportId(), request.getRunMode()); + jMeterService.runLocal(runModeDataDTO.getReport().getId(), runModeDataDTO.getHashTree(), TriggerMode.BATCH.name().equals(request.getTriggerMode()) ? TriggerMode.BATCH.name() : request.getReportId(), request.getRunMode()); } return null; } catch (Exception ex) { diff --git a/backend/src/main/java/io/metersphere/api/service/task/SerialScenarioExecTask.java b/backend/src/main/java/io/metersphere/api/service/task/SerialScenarioExecTask.java index d97f9a8547..48cb70bcf2 100644 --- a/backend/src/main/java/io/metersphere/api/service/task/SerialScenarioExecTask.java +++ b/backend/src/main/java/io/metersphere/api/service/task/SerialScenarioExecTask.java @@ -9,6 +9,7 @@ import io.metersphere.api.jmeter.JMeterService; import io.metersphere.base.domain.ApiScenarioReport; import io.metersphere.base.mapper.ApiScenarioReportMapper; import io.metersphere.commons.constants.APITestStatus; +import io.metersphere.commons.constants.TriggerMode; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.LogUtil; import org.apache.commons.lang3.StringUtils; @@ -35,7 +36,7 @@ public class SerialScenarioExecTask implements Callable { if (request.getConfig() != null && StringUtils.isNotBlank(request.getConfig().getResourcePoolId())) { jMeterService.runTest(runModeDataDTO.getTestId(), runModeDataDTO.getReport().getId(), request.getRunMode(), request.getPlanScenarioId(), request.getConfig()); } else { - jMeterService.runLocal(runModeDataDTO.getReport().getId(), runModeDataDTO.getHashTree(), request.getReportId(), request.getRunMode()); + jMeterService.runLocal(runModeDataDTO.getReport().getId(), runModeDataDTO.getHashTree(), TriggerMode.BATCH.name().equals(request.getTriggerMode()) ? TriggerMode.BATCH.name() : request.getReportId(), request.getRunMode()); } // 轮询查看报告状态,最多200次,防止死循环 int index = 1; diff --git a/backend/src/main/java/io/metersphere/commons/utils/ShiroUtils.java b/backend/src/main/java/io/metersphere/commons/utils/ShiroUtils.java index 89e64ec227..82452159ee 100644 --- a/backend/src/main/java/io/metersphere/commons/utils/ShiroUtils.java +++ b/backend/src/main/java/io/metersphere/commons/utils/ShiroUtils.java @@ -38,6 +38,7 @@ public class ShiroUtils { filterChainDefinitionMap.put("/sso/callback", "anon"); filterChainDefinitionMap.put("/license/valid", "anon"); filterChainDefinitionMap.put("/api/jmeter/download", "anon"); + filterChainDefinitionMap.put("/api/jmeter/download/files", "anon"); filterChainDefinitionMap.put("/api/jmeter/download/jar", "anon"); // for swagger diff --git a/backend/src/main/java/io/metersphere/track/service/task/ParallelApiExecTask.java b/backend/src/main/java/io/metersphere/track/service/task/ParallelApiExecTask.java index ec376a6af1..13f68ce6f2 100644 --- a/backend/src/main/java/io/metersphere/track/service/task/ParallelApiExecTask.java +++ b/backend/src/main/java/io/metersphere/track/service/task/ParallelApiExecTask.java @@ -34,7 +34,7 @@ public class ParallelApiExecTask implements Callable { if (config != null && StringUtils.isNotBlank(config.getResourcePoolId())) { jMeterService.runTest(runModeDataDTO.getTestId(), runModeDataDTO.getApiCaseId(), runMode, null, config); } else { - jMeterService.runLocal(runModeDataDTO.getApiCaseId(), runModeDataDTO.getHashTree(), null, runMode); + jMeterService.runLocal(runModeDataDTO.getApiCaseId(), runModeDataDTO.getHashTree(), runModeDataDTO.getReport() != null ? runModeDataDTO.getReport().getTriggerMode() : null, runMode); } return null; } catch (Exception ex) { diff --git a/backend/src/main/java/io/metersphere/track/service/task/SerialApiExecTask.java b/backend/src/main/java/io/metersphere/track/service/task/SerialApiExecTask.java index 0e5e51a2cc..9665fa7d9d 100644 --- a/backend/src/main/java/io/metersphere/track/service/task/SerialApiExecTask.java +++ b/backend/src/main/java/io/metersphere/track/service/task/SerialApiExecTask.java @@ -36,7 +36,7 @@ public class SerialApiExecTask implements Callable { if (config != null && StringUtils.isNotBlank(config.getResourcePoolId())) { jMeterService.runTest(runModeDataDTO.getTestId(), runModeDataDTO.getApiCaseId(), runMode, null, config); } else { - jMeterService.runLocal(runModeDataDTO.getApiCaseId(), runModeDataDTO.getHashTree(), null, runMode); + jMeterService.runLocal(runModeDataDTO.getApiCaseId(), runModeDataDTO.getHashTree(), runModeDataDTO.getReport() != null ? runModeDataDTO.getReport().getTriggerMode() : null, runMode); } // 轮询查看报告状态,最多200次,防止死循环 ApiDefinitionExecResult report = null; diff --git a/backend/src/main/java/org/apache/jmeter/threads/JMeterThread.java b/backend/src/main/java/org/apache/jmeter/threads/JMeterThread.java index 885f4de185..8bfc19f6d4 100644 --- a/backend/src/main/java/org/apache/jmeter/threads/JMeterThread.java +++ b/backend/src/main/java/org/apache/jmeter/threads/JMeterThread.java @@ -18,6 +18,7 @@ package org.apache.jmeter.threads; import io.metersphere.api.jmeter.MsResultCollector; +import org.apache.commons.collections.CollectionUtils; import org.apache.jmeter.assertions.Assertion; import org.apache.jmeter.assertions.AssertionResult; import org.apache.jmeter.control.Controller; @@ -67,7 +68,9 @@ public class JMeterThread implements Runnable, Interruptible { private static final String TRUE = Boolean.toString(true); // i.e. "true" - /** How often to check for shutdown during ramp-up, default 1000ms */ + /** + * How often to check for shutdown during ramp-up, default 1000ms + */ private static final int RAMPUP_GRANULARITY = JMeterUtils.getPropDefault("jmeterthread.rampup.granularity", 1000); // $NON-NLS-1$ @@ -77,7 +80,7 @@ public class JMeterThread implements Runnable, Interruptible { private static final float ONE_AS_FLOAT = 1.0f; - private static final boolean APPLY_TIMER_FACTOR = Float.compare(TIMER_FACTOR,ONE_AS_FLOAT) != 0; + private static final boolean APPLY_TIMER_FACTOR = Float.compare(TIMER_FACTOR, ONE_AS_FLOAT) != 0; private final Controller threadGroupLoopController; @@ -169,8 +172,7 @@ public class JMeterThread implements Runnable, Interruptible { /** * Enable the scheduler for this JMeterThread. * - * @param sche - * flag whether the scheduler should be enabled + * @param sche flag whether the scheduler should be enabled */ public void setScheduled(boolean sche) { this.scheduler = sche; @@ -197,8 +199,7 @@ public class JMeterThread implements Runnable, Interruptible { /** * Set the EndTime for this Thread. * - * @param etime - * the EndTime value. + * @param etime the EndTime value. */ public void setEndTime(long etime) { endTime = etime; @@ -235,6 +236,7 @@ public class JMeterThread implements Runnable, Interruptible { public void setThreadName(String threadName) { this.threadName = threadName; } + @Override public void run() { // threadContext is not thread-safe, so keep within thread @@ -258,7 +260,7 @@ public class JMeterThread implements Runnable, Interruptible { && threadContext.getTestLogicalAction() != TestLogicalAction.CONTINUE) { log.debug("Start Next Thread Loop option is on, Last sample failed, starting next thread loop"); } - if(onErrorStartNextLoop && !lastSampleOk){ + if (onErrorStartNextLoop && !lastSampleOk) { triggerLoopLogicalActionOnParentControllers(sam, threadContext, JMeterThread::continueOnThreadLoop); } else { switch (threadContext.getTestLogicalAction()) { @@ -278,8 +280,7 @@ public class JMeterThread implements Runnable, Interruptible { threadContext.setTestLogicalAction(TestLogicalAction.CONTINUE); sam = null; setLastSampleOk(threadContext.getVariables(), true); - } - else { + } else { sam = threadGroupLoopController.next(); } } @@ -297,8 +298,7 @@ public class JMeterThread implements Runnable, Interruptible { log.info("Stopping Test: {}", e.toString()); } shutdownTest(); - } - catch (JMeterStopTestNowException e) { // NOSONAR + } catch (JMeterStopTestNowException e) { // NOSONAR if (log.isInfoEnabled()) { log.info("Stopping Test Now: {}", e.toString()); } @@ -328,12 +328,13 @@ public class JMeterThread implements Runnable, Interruptible { /** * Trigger break/continue/switch to next thread Loop depending on consumer implementation - * @param sampler Sampler Base sampler + * + * @param sampler Sampler Base sampler * @param threadContext - * @param consumer Consumer that will process the tree of elements up to root node + * @param consumer Consumer that will process the tree of elements up to root node */ private void triggerLoopLogicalActionOnParentControllers(Sampler sampler, JMeterContext threadContext, - Consumer consumer) { + Consumer consumer) { TransactionSampler transactionSampler = null; if (sampler instanceof TransactionSampler) { transactionSampler = (TransactionSampler) sampler; @@ -343,7 +344,7 @@ public class JMeterThread implements Runnable, Interruptible { if (realSampler == null) { throw new IllegalStateException( "Got null subSampler calling findRealSampler for:" + - (sampler != null ? sampler.getName() : "null") + ", sampler:" + sampler); + (sampler != null ? sampler.getName() : "null") + ", sampler:" + sampler); } // Find parent controllers of current sampler FindTestElementsUpToRootTraverser pathToRootTraverser = new FindTestElementsUpToRootTraverser(realSampler); @@ -364,6 +365,7 @@ public class JMeterThread implements Runnable, Interruptible { /** * Executes a continue of current loop, equivalent of "continue" in algorithm. * As a consequence it ends the first loop it finds on the path to root + * * @param pathToRootTraverser {@link FindTestElementsUpToRootTraverser} */ private static void continueOnCurrentLoop(FindTestElementsUpToRootTraverser pathToRootTraverser) { @@ -384,6 +386,7 @@ public class JMeterThread implements Runnable, Interruptible { /** * Executes a break of current loop, equivalent of "break" in algorithm. * As a consequence it ends the first loop it finds on the path to root + * * @param pathToRootTraverser {@link FindTestElementsUpToRootTraverser} */ private static void breakOnCurrentLoop(FindTestElementsUpToRootTraverser pathToRootTraverser) { @@ -404,6 +407,7 @@ public class JMeterThread implements Runnable, Interruptible { /** * Executes a restart of Thread loop, equivalent of "continue" in algorithm but on Thread Loop. * As a consequence it ends all loop on the path to root + * * @param pathToRootTraverser {@link FindTestElementsUpToRootTraverser} */ private static void continueOnThreadLoop(FindTestElementsUpToRootTraverser pathToRootTraverser) { @@ -424,6 +428,7 @@ public class JMeterThread implements Runnable, Interruptible { * if there are some other controllers (SimpleController or other implementations) between this TransactionSampler and the real sampler, * triggerEndOfLoop will not be called for those controllers leaving them in "ugly" state. * the following method will try to find the sampler that really generate an error + * * @return {@link Sampler} */ private Sampler findRealSampler(Sampler sampler) { @@ -437,8 +442,8 @@ public class JMeterThread implements Runnable, Interruptible { /** * Process the current sampler, handling transaction samplers. * - * @param current sampler - * @param parent sampler + * @param current sampler + * @param parent sampler * @param threadContext * @return SampleResult if a transaction was processed */ @@ -519,8 +524,8 @@ public class JMeterThread implements Runnable, Interruptible { } private void fillThreadInformation(SampleResult result, - int nbActiveThreadsInThreadGroup, - int nbTotalActiveThreads) { + int nbActiveThreadsInThreadGroup, + int nbTotalActiveThreads) { result.setGroupThreads(nbActiveThreadsInThreadGroup); result.setAllThreads(nbTotalActiveThreads); result.setThreadName(threadName); @@ -531,9 +536,9 @@ public class JMeterThread implements Runnable, Interruptible { * Broadcast the result to the sample listeners */ private void executeSamplePackage(Sampler current, - TransactionSampler transactionSampler, - SamplePackage transactionPack, - JMeterContext threadContext) { + TransactionSampler transactionSampler, + SamplePackage transactionPack, + JMeterContext threadContext) { threadContext.setCurrentSampler(current); // Get the sampler ready to sample @@ -548,17 +553,18 @@ public class JMeterThread implements Runnable, Interruptible { if (running) { Sampler sampler = pack.getSampler(); // 执行前发给监听 - List sampleListeners = getSampleListeners(pack, transactionPack, transactionSampler); - SampleEvent event = new SampleEvent(null, current.getPropertyAsString("MS-RESOURCE-ID"), threadVars); - for (SampleListener sampleListener : sampleListeners) { - try { - if(sampleListener instanceof MsResultCollector) { - TestBeanHelper.prepare((TestElement) sampleListener); - sampleListener.sampleStarted(event); - break; + List sampleListeners = pack.getSampleListeners(); + if (CollectionUtils.isNotEmpty(sampleListeners)) { + for (SampleListener sampleListener : sampleListeners) { + try { + if (sampleListener instanceof MsResultCollector) { + SampleEvent event = new SampleEvent(null, current.getPropertyAsString("MS-RESOURCE-ID"), threadVars); + sampleListener.sampleStarted(event); + break; + } + } catch (RuntimeException e) { + log.error("自定义提前发送监听失败.", e); } - } catch (RuntimeException e) { - log.error("自定义提前发送监听失败.", e); } } //====== @@ -622,8 +628,9 @@ public class JMeterThread implements Runnable, Interruptible { *
  • Playing SampleMonitor before and after sampling
  • *
  • resetting currentSamplerForInterruption
  • * + * * @param threadContext {@link JMeterContext} - * @param sampler {@link Sampler} + * @param sampler {@link Sampler} * @return {@link SampleResult} */ private SampleResult doSampling(JMeterContext threadContext, Sampler sampler) { @@ -635,7 +642,7 @@ public class JMeterThread implements Runnable, Interruptible { currentSamplerForInterruption = sampler; if (!sampleMonitors.isEmpty()) { for (SampleMonitor sampleMonitor : sampleMonitors) { - if(sampleMonitor instanceof TestElement) { + if (sampleMonitor instanceof TestElement) { TestBeanHelper.prepare((TestElement) sampleMonitor); } sampleMonitor.sampleStarting(sampler); @@ -682,20 +689,20 @@ public class JMeterThread implements Runnable, Interruptible { private List getSampleListeners(SamplePackage samplePack, SamplePackage transactionPack, TransactionSampler transactionSampler) { List sampleListeners = samplePack.getSampleListeners(); // Do not send subsamples to listeners which receive the transaction sample - if(transactionSampler != null) { + if (transactionSampler != null) { List onlySubSamplerListeners = new ArrayList<>(); List transListeners = transactionPack.getSampleListeners(); - for(SampleListener listener : sampleListeners) { + for (SampleListener listener : sampleListeners) { // Check if this instance is present in transaction listener list boolean found = false; - for(SampleListener trans : transListeners) { + for (SampleListener trans : transListeners) { // Check for the same instance - if(trans == listener) { + if (trans == listener) { found = true; break; } } - if(!found) { + if (!found) { onlySubSamplerListeners.add(listener); } } @@ -751,7 +758,7 @@ public class JMeterThread implements Runnable, Interruptible { private void threadStarted() { JMeterContextService.incrNumberOfThreads(); threadGroup.incrNumberOfThreads(); - GuiPackage gp =GuiPackage.getInstance(); + GuiPackage gp = GuiPackage.getInstance(); if (gp != null) {// check there is a GUI gp.getMainFrame().updateCounts(); } @@ -765,7 +772,7 @@ public class JMeterThread implements Runnable, Interruptible { JMeterContextService.decrNumberOfThreads(); threadGroup.decrNumberOfThreads(); GuiPackage gp = GuiPackage.getInstance(); - if (gp != null){// check there is a GUI + if (gp != null) {// check there is a GUI gp.getMainFrame().updateCounts(); } if (iterationListener != null) { // probably not possible, but check anyway @@ -826,18 +833,20 @@ public class JMeterThread implements Runnable, Interruptible { log.info("Stopping: {}", threadName); } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override - public boolean interrupt(){ + public boolean interrupt() { interruptLock.lock(); try { Sampler samp = currentSamplerForInterruption; // fetch once; must be done under lock - if (samp instanceof Interruptible){ // (also protects against null) + if (samp instanceof Interruptible) { // (also protects against null) if (log.isWarnEnabled()) { log.warn("Interrupting: {} sampler: {}", threadName, samp.getName()); } try { - boolean found = ((Interruptible)samp).interrupt(); + boolean found = ((Interruptible) samp).interrupt(); if (!found) { log.warn("No operation pending"); }