VersionUtils

/**
 * 1:获取文件后缀名
 * 2:获取版本号
 * 3:获取的字符串是否为空
 * @author Administrator
 *
 */
public class VersionUtils {
//判断对象是否为空
public static String notNUll(String str) {
if (str==null||str==""||str.equals("")) {
return "";
}

return str;
}
//得到文件名.之后的后缀名
public static String getExtensionName(String fileName) {
if (fileName!=null&&fileName.length()>0) {
//获取.所在的索引
int pointIndex = fileName.lastIndexOf(".");
if (pointIndex>-1&&(pointIndex<(fileName.length()-1))) {
//获取后缀名
return fileName.substring(pointIndex+1);
}

}

return fileName;
}
//获取文件版本号
public static String getVersion(String version) {
//获取.之后的版本后缀名
String suffixValue = getExtensionName(version);
//获取.之前的版本名称
String preValue = version.substring(0, version.lastIndexOf("."));
Integer value = Integer.valueOf(suffixValue);
value++;
return preValue+"."+value;
}

public static void main(String[] args) {
String fileName="abc.txt";
String version="1.4.2";
String name = VersionUtils.getExtensionName(fileName);
System.out.println(name);
String version1 = VersionUtils.getVersion(version);
System.out.println(version);
String name2 = VersionUtils.getExtensionName(version);
System.err.println(name2);
}

}

测试结果


我现在正在写单元测试,其中,我发现源代码的 private OperationResponse<AddDeviceResult> addIpcDeviceByDeviceDiscovery(DiscoveredDeviceDTO discoveredDeviceDTO, DiscoverType discoverType, AddDiscoveredDeviceDTO request, String vmsId, Long siteId, String stok, Long rootSiteId) { // 获取IPC设备module_spec Optional<FunctionModuleSpecSection> moduleSpecSectionOptional; if (discoverType == DiscoverType.REMOTE) { moduleSpecSectionOptional = passThroughService.getModuleSpecInfo(discoveredDeviceDTO.getDevId()); } else { moduleSpecSectionOptional = getModuleSpecInfoByAutoDiscovery(stok); } if (!moduleSpecSectionOptional.isPresent()) { return OperationResponse.error(ResponseCode.PASSTHOUGH_TIME_OUT); } FunctionModuleSpecSection functionModuleSpecSection = moduleSpecSectionOptional.get(); // 解析capability Long devCapability = ipcInfoService.getCapabilityByIpcModuleSpec(functionModuleSpecSection); CapabilityObjectDO capabilityObject = ipcInfoService.getCapabilityObjectByIpcModuleSpec( functionModuleSpecSection); SmartDataCapability smartDataCapability; if (discoverType == DiscoverType.REMOTE) { smartDataCapability = deviceSmartDataCapabilityService .getSmartDataCapabilityByIpcModuleSpec(functionModuleSpecSection, discoveredDeviceDTO.getDevId(), null); } else { smartDataCapability = getSmartDataCapabilityByAutoDiscovery(stok, functionModuleSpecSection); } // 获取智能分析开关状态 SmartAnalysisModeDO smartAnalysisModeDO; if (discoverType == DiscoverType.REMOTE) { smartAnalysisModeDO = ipcSmartAnalysisModeService.getDirectIpcSmartAnalysisMode( discoveredDeviceDTO.getDevId(), smartDataCapability).getResult(); } else { smartAnalysisModeDO = getSmartAnalysisModeByAutoDiscovery(stok, smartDataCapability); } // 添加至数据库 Optional<DeviceDO> optionalDevice = deviceInternalRepository.findByDevId(discoveredDeviceDTO.getDevId()); Integer onlineStatusInt = deviceConnectorService.getOnlineStatus(discoveredDeviceDTO.getDevId()); OnlineStatus onlineStatus = OnlineStatus.findByCode(onlineStatusInt); onlineStatus = OnlineStatus.ONLINE.equals(onlineStatus) ? onlineStatus : OnlineStatus.ADDING; log.debug("add ipc get onlineStatus {}", onlineStatus); // Ipc不存在,则直接保存该Ipc进数据库中 DeviceDO deviceDO = new DeviceDO().setDevId(discoveredDeviceDTO.getDevId()) .setId(optionalDevice.map(DeviceDO::getId).orElse(null)) .setDeviceName(discoveredDeviceDTO.getDeviceName()) .setDeviceModel(discoveredDeviceDTO.getDeviceModel()) .setDeviceType(discoveredDeviceDTO.getDeviceType()) .setFirmwareVersion(discoveredDeviceDTO.getFirmwareVersion()) .setHardwareVersion(discoveredDeviceDTO.getHardwareVersion()) .setMacAddress(discoveredDeviceDTO.getMacAddress()) .setMacAddressList( getMacAddressList(discoveredDeviceDTO.getMacAddress(), functionModuleSpecSection)) .setIpAddressString(discoveredDeviceDTO.getIpAddressString()) .setOnlineStatus(discoverType == DiscoverType.REMOTE ? OnlineStatus.ONLINE : onlineStatus) .setLicenseStatus(LicenseStatusEnum.UNACTIVE) .setVmsId(vmsId) .setSiteId(siteId) .setRootSiteId(rootSiteId) .setChannel(Constant.DEFAULT_IPC_CHANNEL_NUM) .setUsername(request.getUsername()) .setPassword(request.getPassword()) .setCapability(devCapability) .setCapabilityObject(capabilityObject) .setSmartDataCapability(smartDataCapability) .setSmartAnalysisMode(smartAnalysisModeDO) .setSupportFirmwareVersion(discoveredDeviceDTO.getSupportDeviceFirmwareVersion()) // 默认为我司设备 .setVender(DeviceConstant.TP_LINK_VENDER) // 默认开启离线报警 .setOfflineAlarm(true) // 默认同步site的时区夏令时 .setFollowSiteTime(request.getFollowSiteTime()) .setBarCode(DeviceInfoUtils.getFormatBarCode(discoveredDeviceDTO.getQrCode())); SmartDetectionCapability smartDetectionCapability; if (discoverType == DiscoverType.REMOTE) { smartDetectionCapability = smartDetectionCapabilityService.getSmartDetectionCapability( discoveredDeviceDTO.getDevId()); } else { smartDetectionCapability = getSmartDetectionCapability(stok); } if (log.isDebugEnabled()) { log.debug("get device smart detection capability, devId {}, capability {}.", CustomPIIMaskUtil.encrypt(discoveredDeviceDTO.getDevId()), smartDetectionCapability); } if (!Objects.isNull(smartDetectionCapability)) { deviceDO.setSmartDetectionCapability(smartDetectionCapability.getCapabilityList()); } Long id = ipcRepository.save(deviceDO); deviceDO.setId(id); log.info("system->device->add ipc device {} to site {}", discoveredDeviceDTO.getDeviceName(), siteId); if (OnlineStatus.ONLINE.equals(onlineStatus)) { // 先连接再添加,发布上线事件 deviceStatusChangePublisher.publish(deviceDO); } return OperationResponse.ok(new AddDeviceResult().setOnlineStatus(onlineStatus).setDeviceId(id) .setDeviceType(DeviceType.IPC).setChannelNum((short) 1).setAddedDevice(optionalDevice.isPresent())); }这个方法的 if (discoverType == DiscoverType.REMOTE) { smartAnalysisModeDO = ipcSmartAnalysisModeService.getDirectIpcSmartAnalysisMode( discoveredDeviceDTO.getDevId(), smartDataCapability).getResult(); }会在我的单测代码中报错,单测代码为:/* * Copyright (c) 2025, TP-Link. All rights reserved. */ package com.tplink.cdd.vms.manager.local.domain.device.manage; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.function.Supplier; import cn.hutool.core.map.MapUtil; import com.github.rholder.retry.Retryer; import org.jetbrains.annotations.NotNull; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor; import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.modules.junit4.PowerMockRunnerDelegate; import org.powermock.reflect.Whitebox; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import com.tplink.cdd.component.datatrack.common.util.JsonUtil; import com.tplink.cdd.device.gateway.connector.api.DeviceConnectorInternalApiService; import com.tplink.cdd.device.gateway.connector.api.dto.CheckDeviceDTO; import com.tplink.cdd.device.gateway.connector.api.dto.ConnectorConfigDTO; import com.tplink.cdd.vms.common.constants.DeviceType; import com.tplink.cdd.vms.common.constants.DiscoverDeviceType; import com.tplink.cdd.vms.common.constants.DiscoverType; import com.tplink.cdd.vms.common.constants.OnlineStatus; import com.tplink.cdd.vms.common.constants.RedisKey; import com.tplink.cdd.vms.common.constants.ResponseCode; import com.tplink.cdd.vms.common.constants.websocket.StompStandardConstant; import com.tplink.cdd.vms.common.dto.OperationResponse; import com.tplink.cdd.vms.common.dto.UserDTO; import com.tplink.cdd.vms.common.util.DeviceUtil; import com.tplink.cdd.vms.common.util.VersionUtils; import com.tplink.cdd.vms.excutor.UniqueTaskExecutor; import com.tplink.cdd.vms.manager.api.external.internal.device.dto.SmartDataCapability; import com.tplink.cdd.vms.manager.api.web.device.dto.AddDeviceDTO; import com.tplink.cdd.vms.manager.api.web.device.dto.AddDiscoveredDeviceDTO; import com.tplink.cdd.vms.manager.api.web.device.dto.BatchAddDeviceV2DTO; import com.tplink.cdd.vms.manager.api.web.device.dto.SmartAnalysisRespDTO; import com.tplink.cdd.vms.manager.api.web.result.dto.WebSocketResultDTO; import com.tplink.cdd.vms.manager.core.domain.device.event.publisher.DeviceAddPublisher; import com.tplink.cdd.vms.manager.core.domain.device.event.publisher.DeviceStatusChangePublisher; import com.tplink.cdd.vms.manager.core.domain.device.manage.DeviceSmartDataCapabilityService; import com.tplink.cdd.vms.manager.core.domain.device.manage.IpcInfoService; import com.tplink.cdd.vms.manager.core.domain.device.manage.IpcSmartAnalysisModeService; import com.tplink.cdd.vms.manager.core.domain.device.manage.NvrInfoService; import com.tplink.cdd.vms.manager.core.domain.device.manage.NvrLldpService; import com.tplink.cdd.vms.manager.core.domain.device.manage.PassThroughService; import com.tplink.cdd.vms.manager.core.domain.device.manage.SiteChannelLimitService; import com.tplink.cdd.vms.manager.core.domain.device.manage.SmartDetectionCapabilityService; import com.tplink.cdd.vms.manager.core.domain.device.manage.sync.NvrChildSyncBasicService; import com.tplink.cdd.vms.manager.core.domain.device.passthrough.SolarPassthroughService; import com.tplink.cdd.vms.manager.core.domain.retry.RetryBuilder; import com.tplink.cdd.vms.manager.core.domain.site.manage.QuerySiteService; import com.tplink.cdd.vms.manager.core.port.cache.DeviceAddMacConflictResultCache; import com.tplink.cdd.vms.manager.core.port.cache.WebSocketResultCacheService; import com.tplink.cdd.vms.manager.core.port.dto.passthrough.FunctionModuleSpecSection; import com.tplink.cdd.vms.manager.core.port.dto.passthrough.LldpResponse; import com.tplink.cdd.vms.manager.core.port.dto.passthrough.RetFunctionIncludingModuleSpec; import com.tplink.cdd.vms.manager.core.port.dto.passthrough.capability.SmartDetectionCapability; import com.tplink.cdd.vms.manager.core.port.dto.passthrough.capability.SmartDetectionCapabilityDTO; import com.tplink.cdd.vms.manager.core.port.dto.passthrough.capability.SmartDetectionCapabilityResponse; import com.tplink.cdd.vms.manager.core.port.dto.passthrough.solar.SolarResponse; import com.tplink.cdd.vms.manager.core.port.dto.passthrough.solar.response.SolarFwResponse; import com.tplink.cdd.vms.manager.core.port.dto.passthrough.solar.response.SolarGetSystemStatusResponse; import com.tplink.cdd.vms.manager.core.port.dto.passthrough.solar.response.SystemStatusDTO; import com.tplink.cdd.vms.manager.core.port.eventcenter.device.DeviceBatchProcessPublisher; import com.tplink.cdd.vms.manager.core.port.eventcenter.device.DeviceBatchProcessPublisherV2; import com.tplink.cdd.vms.manager.core.port.slp.DeviceSlpInterface; import com.tplink.cdd.vms.manager.core.port.slp.SolarMessageManager; import com.tplink.cdd.vms.manager.core.port.util.DeviceInfoUtils; import com.tplink.cdd.vms.manager.discover.cache.AutoDiscoverCache; import com.tplink.cdd.vms.manager.discover.cache.ManualDiscoverCache; import com.tplink.cdd.vms.manager.discover.cache.RemoteDiscoverCache; import com.tplink.cdd.vms.manager.discover.constant.FactoryDefine; import com.tplink.cdd.vms.manager.discover.dto.AuthAndAccessResponse; import com.tplink.cdd.vms.manager.discover.dto.DiscoveredDeviceDTO; import com.tplink.cdd.vms.manager.discover.service.AuthenticationService; import com.tplink.cdd.vms.manager.discover.util.HttpRequestUtil; import com.tplink.cdd.vms.manager.discover.util.SolarInterfaceUtil; import com.tplink.cdd.vms.manager.repository.api.device.DeviceInternalRepository; import com.tplink.cdd.vms.manager.repository.api.device.DeviceRepository; import com.tplink.cdd.vms.manager.repository.api.device.IpcRepository; import com.tplink.cdd.vms.manager.repository.api.device.NvdRepository; import com.tplink.cdd.vms.manager.repository.api.device.NvrRepository; import com.tplink.cdd.vms.manager.repository.api.device.SolarRepository; import com.tplink.cdd.vms.manager.repository.api.device.domain.CapabilityObjectDO; import com.tplink.cdd.vms.manager.repository.api.device.domain.DeviceDO; import com.tplink.cdd.vms.manager.repository.api.device.domain.LldpDO; import com.tplink.smb.component.cache.api.CacheService; import com.tplink.smb.component.lock.api.LockService; import com.tplink.smb.log.component.client.api.LogApiService; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.powermock.api.mockito.PowerMockito.doNothing; import static org.powermock.api.mockito.PowerMockito.when; /** * Description of this file * * @author Li Tailong * @version 1.0 * @since 2025/10/21 */ @DirtiesContext @RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(SpringRunner.class) @PrepareForTest({VersionUtils.class, AuthenticationService.class, DeviceUtil.class, OnlineStatus.class, SolarMessageManager.class, DeviceInfoUtils.class, DeviceSlpInterface.class, SolarInterfaceUtil.class, HttpRequestUtil.class}) @SuppressStaticInitializationFor("com.example.DeviceUtil") @PowerMockIgnore({"javax.*.*", "com.sun.org.apache.xerces.*", "org.w3c.dom.*", "org.xml.*"}) public class AddDeviceV2ServiceTest { @InjectMocks private AddDeviceV2Service addDeviceV2Service; @Mock private LockService lockService; @Mock private RetryBuilder retryBuilder; @Mock private ThreadPoolTaskExecutor executor; @Mock private UniqueTaskExecutor iframeExecutor; @Mock private DiscoverService discoverService; @Mock private DeviceRepository deviceRepository; @Mock private AutoDiscoverCache autoDiscoverCache; @Mock private PassThroughService passThroughService; @Mock private DeviceBatchProcessPublisherV2 publisher; @Mock private ManualDiscoverCache manualDiscoverCache; @Mock private RemoteDiscoverCache remoteDiscoverCache; @Mock private DeviceConnectorInternalApiService deviceConnectorService; @Mock private DeviceAddMacConflictResultCache deviceAddMacConflictResultCache; @Mock private IpcRepository ipcRepository; @Mock private NvrRepository nvrRepository; @Mock private NvdRepository nvdRepository; @Mock private SolarRepository solarRepository; @Mock private LogApiService logApiService; @Mock private IpcInfoService ipcInfoService; @Mock private NvrInfoService nvrInfoService; @Mock private DeviceSmartDataCapabilityService deviceSmartDataCapabilityService; @Mock private SmartDetectionCapabilityService smartDetectionCapabilityService; @Mock private DeviceInternalRepository deviceInternalRepository; @Mock private SolarPassthroughService solarPassthroughService; @Mock private DeviceBatchProcessPublisher deviceBatchProcessPublisher; @Mock private CacheService cacheService; @Mock private QuerySiteService querySiteService; @Mock private SiteChannelLimitService siteChannelLimitService; @Mock private DeviceStatusChangePublisher deviceStatusChangePublisher; @Mock private DeviceAddPublisher deviceAddPublisher; @Mock private NvrChildSyncBasicService nvrChildSyncBasicService; @Mock private IpcSmartAnalysisModeService ipcSmartAnalysisModeService; @Mock private NvrLldpService nvrLldpService; @Mock private WebSocketResultCacheService webSocketResultCacheService; private Retryer<Object> statusCheckRetry; @Before public void setUp() { Whitebox.setInternalState(addDeviceV2Service, "webSocketResultCacheService", webSocketResultCacheService); Mockito.doAnswer(invocationOnMock -> ((Supplier<?>) invocationOnMock.getArgument(2)).get()) .when(lockService).doWithTryLock(anyString(), any(), any(), any()); Mockito.doAnswer(invocationOnMock -> ((Supplier<?>) invocationOnMock.getArgument(4)).get()) .when(lockService).doWithTryLock(anyString(), any(), anyLong(), any(), any(), any()); Mockito.doAnswer(invocationOnMock -> { ((Runnable) invocationOnMock.getArgument(0)).run(); return null; }).when(executor).execute(Mockito.any(Runnable.class)); Mockito.doAnswer(invocationOnMock -> { ((Runnable) invocationOnMock.getArgument(1)).run(); return null; }).when(iframeExecutor).execute(anyString(), Mockito.any(Runnable.class)); statusCheckRetry = new RetryBuilder().buildWithMilliseconds(20, 500); PowerMockito.mockStatic(VersionUtils.class); PowerMockito.mockStatic(AuthenticationService.class); PowerMockito.mockStatic(DeviceUtil.class); PowerMockito.mockStatic(OnlineStatus.class); PowerMockito.mockStatic(SolarMessageManager.class); PowerMockito.mockStatic(DeviceInfoUtils.class); PowerMockito.mockStatic(DeviceSlpInterface.class); PowerMockito.mockStatic(SolarInterfaceUtil.class); PowerMockito.mockStatic(HttpRequestUtil.class); } @Test public void testInit() { addDeviceV2Service.init(); } @Test public void testAddDiscoveredDevices() { AddDiscoveredDeviceDTO request = new AddDiscoveredDeviceDTO(); request.setUserId("userId"); request.setUuid("uuid"); request.setVmsId("vmsId"); request.setDiscoverType(DiscoverType.AUTO); request.setSelectAll(true); request.setSearchValue("model"); request.setUsername("username"); request.setPassword("password"); DiscoveredDeviceDTO discoveredDeviceDTO = getDiscoveredDeviceDTO(); Map<String, DiscoveredDeviceDTO> discoveredMap = new HashMap<>(); discoveredMap.put("devId", discoveredDeviceDTO); Mockito.when(autoDiscoverCache.getAutoDiscoveredResult()).thenReturn(discoveredMap); Mockito.when(querySiteService.getRootSiteId(any(), any())).thenReturn(1L); Mockito.when((cacheService.get(anyString(), any(), any()))).thenReturn("test"); Mockito.when(VersionUtils.compareDeviceVersion(any(), any())).thenReturn(false); addDeviceV2Service.addDiscoveredDevices(request); Mockito.when(VersionUtils.compareDeviceVersion(any(), any())).thenReturn(true); Mockito.when(discoverService.checkDeviceIsInDb(any(), any())).thenReturn(true); addDeviceV2Service.addDiscoveredDevices(request); Mockito.when(discoverService.checkDeviceIsInDb(any(), any())).thenReturn(false); // addSingleDevice ConnectorConfigDTO connectorConfigDTO = new ConnectorConfigDTO(); connectorConfigDTO.setDeviceConnectPort(80); AuthAndAccessResponse authAndAccessResponse = new AuthAndAccessResponse(0, "stok", "secLeft", 1000); AddDeviceResult addDeviceResult = new AddDeviceResult(); Mockito.when(deviceConnectorService.getConnectorConfig()).thenReturn(OperationResponse.ok(connectorConfigDTO)); Mockito.when(AuthenticationService.deviceAuthAndAccess(any())).thenReturn(authAndAccessResponse); Mockito.when(DeviceUtil.typeSwitch(any(), any(), any(), any(), any(), any())) .thenReturn(OperationResponse.ok(addDeviceResult)); Mockito.doNothing().when(webSocketResultCacheService).delete(anyString(), anyString()); Mockito.doNothing().when(webSocketResultCacheService).setValue(anyString(), anyString(), any()); addDeviceV2Service.addDiscoveredDevices(request); } @Test public void testBuildInitDeviceParam() throws InvocationTargetException, IllegalAccessException { AddDiscoveredDeviceDTO request = new AddDiscoveredDeviceDTO(); request.setInitUsername("initUsername"); request.setInitPassword("initPassword"); request.setResetPwdEmail("resetPwdEmail"); request.setLanguage("language"); request.setCountry("country"); request.setPowerLineFre("powerLineFre"); request.setTimeZone("timeZone"); request.setZoneId("zoneId"); request.setQuestion1("question1"); request.setQuestion2("question2"); request.setQuestion3("question3"); request.setAnswer1("answer1"); request.setAnswer2("answer2"); request.setAnswer3("answer3"); Method buildInitDeviceParam = Whitebox.getMethod(AddDeviceV2Service.class, "buildInitDeviceParam", AddDiscoveredDeviceDTO.class); buildInitDeviceParam.setAccessible(true); buildInitDeviceParam.invoke(addDeviceV2Service, request); } @Test public void testAddNvrDeviceByDeviceDiscovery() throws InvocationTargetException, IllegalAccessException { DiscoveredDeviceDTO discoveredDeviceDTO = getDiscoveredDeviceDTO(); AddDiscoveredDeviceDTO request = getAddDiscoveredDeviceDTO(); FunctionModuleSpecSection functionModuleSpecSection = new FunctionModuleSpecSection(); functionModuleSpecSection.setMaxChannels("1"); Mockito.when(passThroughService.getModuleSpecInfo(any())).thenReturn(Optional.of(functionModuleSpecSection)); Mockito.when(nvrInfoService.getCapability(any())).thenReturn(1L); Mockito.when(nvrInfoService.getCapabilityObject(any())).thenReturn(new CapabilityObjectDO()); LldpDO lldpInfo = new LldpDO(); Mockito.when(nvrLldpService.getLldpInfo(any(), any())).thenReturn(lldpInfo); DeviceDO deviceDO = new DeviceDO(); deviceDO.setId(1L); deviceDO.setOnlineStatus(OnlineStatus.OFFLINE); Mockito.when(deviceInternalRepository.findByDevId(any())).thenReturn(Optional.of(deviceDO)); Mockito.when(nvrInfoService.getSmartDataCapability(any())).thenReturn(new SmartDataCapability()); Mockito.when(deviceConnectorService.getOnlineStatus(any())).thenReturn(1); Mockito.when(OnlineStatus.findByCode(anyInt())).thenReturn(OnlineStatus.ONLINE); Mockito.when(nvrRepository.save(any())).thenReturn(1L); Method addNvrDeviceByDeviceDiscovery = Whitebox.getMethod(AddDeviceV2Service.class, "addNvrDeviceByDeviceDiscovery", DiscoveredDeviceDTO.class, DiscoverType.class, AddDiscoveredDeviceDTO.class , String.class, Long.class, String.class, Long.class, Long.class); addNvrDeviceByDeviceDiscovery.setAccessible(true); addNvrDeviceByDeviceDiscovery.invoke(addDeviceV2Service, discoveredDeviceDTO, DiscoverType.REMOTE, request, "username", 1L, "password", 1L, 1L); } @Test public void testAddNvdDeviceByDeviceDiscovery() throws InvocationTargetException, IllegalAccessException { DiscoveredDeviceDTO discoveredDeviceDTO = getDiscoveredDeviceDTO(); AddDiscoveredDeviceDTO request = getAddDiscoveredDeviceDTO(); DeviceDO deviceDO = new DeviceDO(); deviceDO.setId(1L); deviceDO.setOnlineStatus(OnlineStatus.OFFLINE); Mockito.when(deviceInternalRepository.findByDevId(any())).thenReturn(Optional.of(deviceDO)); Mockito.when(deviceConnectorService.getOnlineStatus(any())).thenReturn(1); Mockito.when(OnlineStatus.findByCode(anyInt())).thenReturn(OnlineStatus.ONLINE); Mockito.when(nvdRepository.save(any())).thenReturn(1L); Method addNvdDeviceByDeviceDiscovery = Whitebox.getMethod(AddDeviceV2Service.class, "addNvdDeviceByDeviceDiscovery", DiscoveredDeviceDTO.class, DiscoverType.class, AddDiscoveredDeviceDTO.class , String.class, Long.class, Long.class); addNvdDeviceByDeviceDiscovery.setAccessible(true); addNvdDeviceByDeviceDiscovery.invoke(addDeviceV2Service, discoveredDeviceDTO, DiscoverType.REMOTE, request, "username", 1L, 1L); } @Test public void testAddSolarDeviceByDeviceDiscovery() throws InvocationTargetException, IllegalAccessException { DiscoveredDeviceDTO discoveredDeviceDTO = getDiscoveredDeviceDTO(); AddDiscoveredDeviceDTO request = getAddDiscoveredDeviceDTO(); Mockito.when(solarRepository.countByVmsId(any())).thenReturn(0L); SystemStatusDTO systemStatusDTO = new SystemStatusDTO(); Mockito.when(solarPassthroughService.getSystemStatus(any(), any())) .thenReturn(OperationResponse.ok(systemStatusDTO)); DeviceDO deviceDO = new DeviceDO(); deviceDO.setId(1L); deviceDO.setOnlineStatus(OnlineStatus.OFFLINE); Mockito.when(deviceInternalRepository.findByDevId(any())).thenReturn(Optional.of(deviceDO)); Mockito.when(solarRepository.save(any())).thenReturn(1L); Mockito.when(SolarMessageManager.getElectricNum(any())).thenReturn(1); Mockito.when((SolarMessageManager.getChargeStatus(any()))).thenReturn(true); Mockito.when(SolarMessageManager.getLowBattery(any())).thenReturn(true); Mockito.when(SolarMessageManager.getLowTemperature(any())).thenReturn(true); Mockito.when(SolarMessageManager.getHighTemperature(any())).thenReturn(true); Mockito.when(DeviceInfoUtils.getFormatBarCode(any())).thenReturn("test"); Method addSolarDeviceByDeviceDiscovery = Whitebox.getMethod(AddDeviceV2Service.class, "addSolarDeviceByDeviceDiscovery", DiscoveredDeviceDTO.class, DiscoverType.class, AddDiscoveredDeviceDTO.class , String.class, Long.class, AuthAndAccessResponse.class, Long.class); addSolarDeviceByDeviceDiscovery.setAccessible(true); addSolarDeviceByDeviceDiscovery.invoke(addDeviceV2Service, discoveredDeviceDTO, DiscoverType.REMOTE, request, "username", 1L , new AuthAndAccessResponse(0, "stok", "secLeft", 1000), 1L); } @Test public void testGetSmartDetectionCapability() throws InvocationTargetException, IllegalAccessException { UserDTO userDTO = new UserDTO(); userDTO.setUsername("username"); Mockito.when(DeviceSlpInterface.getSmartDetectionCapability()).thenReturn(userDTO); SmartDetectionCapabilityDTO smartDetectionCapabilityDTO = new SmartDetectionCapabilityDTO(); SmartDetectionCapabilityResponse smartDetectionCapabilityResponse = new SmartDetectionCapabilityResponse(); smartDetectionCapabilityResponse.setSmartDetectionCapabilityDTO(smartDetectionCapabilityDTO); String json = JsonUtil.bean2json(smartDetectionCapabilityResponse); Mockito.when(AuthenticationService.httpPostProcess(any(), any())).thenReturn(json); Method getSmartDetectionCapability = Whitebox.getMethod(AddDeviceV2Service.class, "getSmartDetectionCapability", String.class); getSmartDetectionCapability.setAccessible(true); getSmartDetectionCapability.invoke(addDeviceV2Service, "username"); } @Test public void testGetModuleSpecInfoByAutoDiscovery() throws InvocationTargetException, IllegalAccessException { Mockito.when(DeviceSlpInterface.getSlpModuleSpecInfo()).thenReturn("test"); RetFunctionIncludingModuleSpec retFunctionIncludingModuleSpec = new RetFunctionIncludingModuleSpec(); retFunctionIncludingModuleSpec.setErrorCode(0); String json = JsonUtil.bean2json(retFunctionIncludingModuleSpec); Mockito.when(AuthenticationService.httpPostProcess(any(), any())).thenReturn(json); Method getModuleSpecInfoByAutoDiscovery = Whitebox.getMethod(AddDeviceV2Service.class, "getModuleSpecInfoByAutoDiscovery", String.class); getModuleSpecInfoByAutoDiscovery.setAccessible(true); getModuleSpecInfoByAutoDiscovery.invoke(addDeviceV2Service, "username"); } @Test public void testGetBarCodeByAutoDiscovery() throws InvocationTargetException, IllegalAccessException { Mockito.when(DeviceSlpInterface.getSlpModuleSpecInfo()).thenReturn("test"); Object object = MapUtil.builder() .put("device_id", MapUtil.builder() .put(("basic_info"), MapUtil.builder().put("barcode", "test").build()).build()).build(); String json = JsonUtil.bean2json(object); Mockito.when(AuthenticationService.httpPostProcess(any(), any())).thenReturn(json); Method getBarCodeByAutoDiscovery = Whitebox.getMethod(AddDeviceV2Service.class, "getBarCodeByAutoDiscovery", String.class); getBarCodeByAutoDiscovery.setAccessible(true); getBarCodeByAutoDiscovery.invoke(addDeviceV2Service, "username"); } @Test public void testGetSmartDataCapabilityByAutoDiscovery() throws InvocationTargetException, IllegalAccessException { FunctionModuleSpecSection param = new FunctionModuleSpecSection(); param.setHttpSmartDataVersion(null); param.setTdcpVersion("tdcp"); param.setSmartAnalysis(1); SmartAnalysisRespDTO smartAnalysisRespDTO = new SmartAnalysisRespDTO(); smartAnalysisRespDTO.setErrorCode(0); String json = JsonUtil.bean2json(smartAnalysisRespDTO); Mockito.when(AuthenticationService.httpPostProcess(any(), any())).thenReturn(json); Method getSmartDataCapabilityByAutoDiscovery = Whitebox.getMethod(AddDeviceV2Service.class, "getSmartDataCapabilityByAutoDiscovery", String.class, FunctionModuleSpecSection.class); getSmartDataCapabilityByAutoDiscovery.setAccessible(true); getSmartDataCapabilityByAutoDiscovery.invoke(addDeviceV2Service, "username", param); } private static @NotNull DiscoveredDeviceDTO getDiscoveredDeviceDTO() { DiscoveredDeviceDTO discoveredDeviceDTO = new DiscoveredDeviceDTO(); discoveredDeviceDTO.setDeviceModel("model"); discoveredDeviceDTO.setSysFactoryDefine(FactoryDefine.NOT_ON_FACTORY_STATUS); discoveredDeviceDTO.setDevId("devId"); discoveredDeviceDTO.setSupportDeviceFirmwareVersion("1.0"); discoveredDeviceDTO.setIpAddressString("ipAddressString"); discoveredDeviceDTO.setDeviceName("deviceName"); discoveredDeviceDTO.setDiscoverDeviceType(DiscoverDeviceType.TP_DISCOVERY_DEVICE_TYPE_ACCESSCONTROLDEVICE); discoveredDeviceDTO.setMacAddress("macAddress"); return discoveredDeviceDTO; } private static @NotNull AddDiscoveredDeviceDTO getAddDiscoveredDeviceDTO() { AddDiscoveredDeviceDTO request = new AddDiscoveredDeviceDTO(); request.setUserId("userId"); request.setUuid("uuid"); request.setVmsId("vmsId"); request.setDiscoverType(DiscoverType.AUTO); request.setSelectAll(true); request.setSearchValue("model"); request.setUsername("username"); request.setPassword("password"); return request; } @Test public void testWebSocketResultCacheOperations() { AddDiscoveredDeviceDTO request = new AddDiscoveredDeviceDTO(); request.setUserId("userId"); request.setUuid("uuid"); request.setVmsId("vmsId"); request.setDiscoverType(DiscoverType.AUTO); request.setSelectAll(true); request.setSearchValue("model"); request.setUsername("username"); request.setPassword("password"); // 测试delete操作 addDeviceV2Service.handleAddDiscoveredDevices(request); verify(webSocketResultCacheService).delete(eq(request.getVmsId()), eq(request.getUuid())); // 测试setValue操作 verify(webSocketResultCacheService).setValue( eq(request.getVmsId()), eq(request.getUuid()), any(WebSocketResultDTO.class) ); } @Test public void testAddIpcDeviceByDeviceDiscovery() throws InvocationTargetException, IllegalAccessException { DiscoveredDeviceDTO discoveredDeviceDTO = getDiscoveredDeviceDTO(); AddDiscoveredDeviceDTO request = getAddDiscoveredDeviceDTO(); FunctionModuleSpecSection functionModuleSpecSection = new FunctionModuleSpecSection(); functionModuleSpecSection.setMaxChannels("1"); Mockito.when(passThroughService.getModuleSpecInfo(any())).thenReturn(Optional.of(functionModuleSpecSection)); Mockito.when(ipcInfoService.getCapabilityByIpcModuleSpec(any())).thenReturn(1L); Mockito.when(ipcInfoService.getCapabilityObjectByIpcModuleSpec(any())).thenReturn(new CapabilityObjectDO()); Mockito.when(deviceSmartDataCapabilityService.getSmartDataCapabilityByIpcModuleSpec(any(), any(), any())) .thenReturn(new SmartDataCapability()); DeviceDO deviceDO = new DeviceDO(); deviceDO.setId(1L); deviceDO.setOnlineStatus(OnlineStatus.OFFLINE); Mockito.when(deviceInternalRepository.findByDevId(any())).thenReturn(Optional.of(deviceDO)); Mockito.when(deviceConnectorService.getOnlineStatus(any())).thenReturn(1); Mockito.when(OnlineStatus.findByCode(anyInt())).thenReturn(OnlineStatus.ONLINE); Mockito.when(smartDetectionCapabilityService.getSmartDetectionCapability(any())) .thenReturn(new SmartDetectionCapability()); Mockito.when(ipcRepository.save(any())).thenReturn(1L); Method addIpcDeviceByDeviceDiscovery = Whitebox.getMethod(AddDeviceV2Service.class, "addIpcDeviceByDeviceDiscovery", DiscoveredDeviceDTO.class, DiscoverType.class, AddDiscoveredDeviceDTO.class , String.class, Long.class, String.class, Long.class); addIpcDeviceByDeviceDiscovery.setAccessible(true); addIpcDeviceByDeviceDiscovery.invoke(addDeviceV2Service, discoveredDeviceDTO, DiscoverType.REMOTE, request, "username", 1L, "password", 1L); } }
最新发布
11-01
CloudAccessWebApiServiceImpl中实现了多实例转发: public OperationResponse<StartLaunchResponse> startLaunch(StartLaunchRequest startLaunchRequest) { if (log.isDebugEnabled()) { log.debug("startLaunchRequest is {}", CustomPIIMaskUtil.parseToStr(startLaunchRequest)); } String siteKey = startLaunchRequest.getSiteKey(); String serverKey = startLaunchRequest.getServerKey(); String email = startLaunchRequest.getEmail(); String username = startLaunchRequest.getUsername(); String userId = startLaunchRequest.getUserId(); String accountId = getAccountIdByUserName(username); String sessionId = startLaunchRequest.getSessionId(); String tplinkIdToken = startLaunchRequest.getTplinkIdToken(); if (StringUtil.isEmpty(serverKey)) { log.warn("Invalid codedDeviceId, is empty"); return OperationResponse.error(ResponseCode.INVALID_REQUEST_PARAMS); } if (Objects.isNull(accountId)) { log.error("tenant not fount, userId = {}", userId); return OperationResponse.error(ResponseCode.TENANT_NOT_EXIST); } String deviceId = AESUtils.decrypt(serverKey); if (log.isDebugEnabled()) { log.debug("{} start launch to {}", CustomPIIMaskUtil.hash(startLaunchRequest.getEmail()), CustomPIIMaskUtil.encrypt(deviceId)); } DeviceDTO device; try { OperationResponse<DeviceDTO> deviceResponse = getDeviceBasicInfoWithOutCache(accountId, deviceId, platformCloudProps.getHost()); device = deviceResponse.getResult(); if (deviceResponse.isError() || Objects.isNull(device)) { log.warn("Failed get deviceInfo of {} for {} , err is {}:{}", CustomPIIMaskUtil.encrypt(deviceId), CustomPIIMaskUtil.hash(email), deviceResponse.getErrorCode(), deviceResponse.getMessage()); if (deviceResponse.getErrorCode() == ResponseCode.CLOUD_ACCOUNT_NOT_BOUND.getCode()) { return OperationResponse.error(ResponseCode.LAUNCH_FAILED_DEVICE_NOT_BOUND); } else { return OperationResponse.error(ResponseCode.LAUNCH_FAILED); } } } catch (Exception ex) { log.error(ex.toString(), ex); log.warn("Failed get deviceInfo of {} for {}", CustomPIIMaskUtil.encrypt(deviceId), CustomPIIMaskUtil.hash(email)); return OperationResponse.error(ResponseCode.LAUNCH_FAILED); } if (device.getStatus() != Constant.ONLINE) { log.warn("Launch failed, device {} is offline with {} ", CustomPIIMaskUtil.encrypt(deviceId), device.getAppServerUrl()); return OperationResponse.error(ResponseCode.CLOUD_DEVICE_OFFLINE); } int controllerVerInt; try { controllerVerInt = VersionUtils.parseFwVersion(device.getControllerVersion()); if (controllerVerInt < 0) { log.error("Invalid vms version {}", device.getControllerVersion()); return OperationResponse.error(ResponseCode.INTERNAL_SERVER_ERROR); } } catch (Exception ex) { log.error(ex.toString(), ex); return OperationResponse.error(ResponseCode.INTERNAL_SERVER_ERROR); } if (!proxyHttpServer.isDeviceConnectedLocal(deviceId)) { log.debug("Device: {} not connected to this server, need to forward.", deviceId); // 获取可用serverIP列表 List<String> serverIps = proxyHttpServer.findServerIpsByServerKey(ProxyUtils.generateServerKey(deviceId)); if (CollectionUtils.isEmpty(serverIps)) { log.warn("failed to get serverIps from cache for {}, going to reconnect", CustomPIIMaskUtil.encrypt(deviceId)); return OperationResponse.error(ResponseCode.GENERAL_ERROR); } // 构建跨实例请求 InternalForwardLaunchRequest forwardLaunchRequest = InternalForwardLaunchRequest.builder() .serverKey(serverKey) .email(email) .username(username) .userId(userId) .sessionId(sessionId) .tplinkIdToken(tplinkIdToken) .build(); return httpForwardService.forwardLaunchRequest(serverIps, forwardLaunchRequest); } // 检查状态,防止同一用户同时点击多次launch造成并发冲突 synchronized (ProxyClientsCache.getConnectionLock(deviceId)) { DevLaunchInfo devLaunchStatus = cloudAccessCache.getDevLaunchInfo(deviceId); if (devLaunchStatus != null && Objects.equals(devLaunchStatus.getLaunchStatus(), LaunchStatus.CONNECTING) && TimeUtil.isNotTimeoutS(devLaunchStatus.getUpdateTime(), Constant.PROXY_CONNECTING_TIMEOUT_S)) { if (log.isDebugEnabled()) { log.debug("Is connecting now, please wait {}.", CustomPIIMaskUtil.hash(email)); } // 直接返回成功 return OperationResponse.ok(); } } return launchToController(serverKey, email, userId, accountId, sessionId, tplinkIdToken, siteKey, device, controllerVerInt); } private OperationResponse launchToController(String serverKey, String email, String userId, String accountId, String sessionId, String tplinkIdToken, String siteKey, DeviceDTO device, int controllerVerInt) { try { launchThreadPool.execute(() -> { // 检查状态,防止同一用户同时点击多次launch造成并发冲突 synchronized (ProxyClientsCache.getConnectionLock(device.getDeviceId())) { DevLaunchInfo devLaunchStatus = cloudAccessCache.getDevLaunchInfo(device.getDeviceId()); if (Objects.isNull(devLaunchStatus)) { devLaunchStatus = new DevLaunchInfo(LaunchStatus.CONNECTING, TimeUtil.getCurrentTimeS(), controllerVerInt); } else { if (Objects.equals(devLaunchStatus.getLaunchStatus(), LaunchStatus.CONNECTING) && TimeUtil.isNotTimeoutS(devLaunchStatus.getUpdateTime(), Constant.PROXY_CONNECTING_TIMEOUT_S)) { if (log.isDebugEnabled()) { log.debug("Is connecting now, please wait {}.", CustomPIIMaskUtil.hash(email)); } launchStatusProcessPublisher.publishLaunchStatusProcess(userId, serverKey, LaunchStatusProgress.builder() .status(LaunchStatus.CONNECTING.getValue()) .build()); return; } devLaunchStatus.setControllerVer(controllerVerInt); // 不管当前连接是何种状态,均重连 devLaunchStatus.setLaunchStatus(LaunchStatus.CONNECTING); devLaunchStatus.setUpdateTime(TimeUtil.getCurrentTimeS()); } cloudAccessCache.saveDevLaunchInfo(device.getDeviceId(), devLaunchStatus); } // OperationResponse optRes = createProxyChannel(email, tplinkIdToken, accountId, siteKey, device, // sessionId); com.tplink.smb.cloud.common.response.OperationResponse optRes = proxyHttpServer.handleLaunchRequest(tplinkIdToken, accountId, sessionId, device.getDeviceId(), Constant.LAUNCH_TIMEOUT_S); // 对于LAUNCH_FAILED_CHECK_DEVICE场景,自动重试一次 // 后续会在VMS上增加自动尝试逻辑,增加后应该基本不会返回此错误 if (optRes.getErrorCode() == ResponseCode.LAUNCH_FAILED_CHECK_DEVICE.getCode()) { log.info("Failed to createProxyChannel to {} as LAUNCH_FAILED_CHECK_DEVICE, try again!", CustomPIIMaskUtil.encrypt(device.getDeviceId())); launchStatusProcessPublisher.publishLaunchStatusProcess(userId, serverKey, LaunchStatusProgress.builder() .status(LaunchStatus.CONNECTING.getValue()) .build()); // optRes = createProxyChannel(email, tplinkIdToken, accountId, siteKey, device, sessionId); optRes = proxyHttpServer.handleLaunchRequest(tplinkIdToken, accountId, sessionId, device.getDeviceId(), Constant.LAUNCH_TIMEOUT_S); } log.debug("Launch response is {}: {}, resData: {}", optRes.getErrorCode(), optRes.getMsg(), optRes.getResult()); synchronized (ProxyClientsCache.getConnectionLock(device.getDeviceId())) { DevLaunchInfo devLaunchStatus = cloudAccessCache.getDevLaunchInfo(device.getDeviceId()); if (Objects.isNull(devLaunchStatus)) { log.error("Failed to find devLaunchStatus {} for {} in session {}", CustomPIIMaskUtil.encrypt(device.getDeviceId()), CustomPIIMaskUtil.hash(email), sessionId); launchStatusProcessPublisher.publishLaunchStatusProcess(userId, serverKey, LaunchStatusProgress.builder() .status(LaunchStatus.CONNECT_FAILED.getValue()) .failedCode(ResponseCode.GENERAL_ERROR.getCode()) .build()); return; } if (optRes.isOk()) { if (log.isDebugEnabled()) { log.debug("launch success for {}", CustomPIIMaskUtil.hash(email)); } Map<String, Object> resultMap = (Map<String, Object>) optRes.getResult(); String serverUrl = (String) resultMap.get(ProxyConstVal.SERVER_URL); String[] split = serverUrl.split(";"); String vmsCloudUrl; String internalIP = null; if (split.length == 2) { vmsCloudUrl = split[0]; internalIP = split[1]; } else { vmsCloudUrl = serverUrl; } RegionEnum region = RegionUtils.getRegionByServiceUrl(vmsCloudUrl); log.debug("vmsCloudUrl is {}, internalIP is {}, region is {}", vmsCloudUrl, internalIP, region); if (Objects.nonNull(region)) { devLaunchStatus.setLaunchStatus(LaunchStatus.CONNECTED); devLaunchStatus.setVmsRegion(region.getIndex()); devLaunchStatus.setVmsCloudUrl(vmsCloudUrl); // 当internalIP不为空时设置internalIP,否则设为空 devLaunchStatus.setInternalIP(StringUtil.isEmpty(internalIP) ? null : internalIP); devLaunchStatus.setUpdateTime(TimeUtil.getCurrentTimeS()); devLaunchStatus.setErrCode(ResponseCode.OK.getCode()); Region regionEnum = Region.parse(region.getRegion()); Boolean regionPrefix = selfServiceProps.getRegionPrefix().getOrDefault(regionEnum, null); String webRegionUrl; if (Boolean.TRUE.equals(regionPrefix)) { webRegionUrl = "https://" + region.getRegion() + "-" + selfServiceProps.getDefaultBusinessUrl(); } else { webRegionUrl = "https://" + selfServiceProps.getDefaultBusinessUrl(); } launchStatusProcessPublisher.publishLaunchStatusProcess(userId, serverKey, LaunchStatusProgress.builder() .status(LaunchStatus.CONNECTED.getValue()) .vmsCloudUrl(vmsCloudUrl) .webRegionUrl(webRegionUrl) .build()); } else { devLaunchStatus.setLaunchStatus(LaunchStatus.CONNECT_FAILED); devLaunchStatus.setUpdateTime(TimeUtil.getCurrentTimeS()); devLaunchStatus.setErrCode(ResponseCode.INVALID_VMS_CLOUD_URL.getCode()); log.warn("Invalid vms cloud url {} for {}", vmsCloudUrl, CustomPIIMaskUtil.hash(email)); launchStatusProcessPublisher.publishLaunchStatusProcess(userId, serverKey, LaunchStatusProgress.builder() .status(LaunchStatus.CONNECT_FAILED.getValue()) .failedCode(ResponseCode.INVALID_VMS_CLOUD_URL.getCode()) .build()); } } else { log.warn("{} launch to vms {} with deviceId {} failed, err is {}", CustomPIIMaskUtil.hash(email), device.getControllerVersion(), CustomPIIMaskUtil.encrypt(device.getDeviceId()), optRes.getMsg()); // 需要判断一下当前LaunchStatus的状态,只有是CONNECTING时才覆盖。 if (Objects.equals(devLaunchStatus.getLaunchStatus(), LaunchStatus.CONNECTING)) { devLaunchStatus.setLaunchStatus(LaunchStatus.CONNECT_FAILED); devLaunchStatus.setUpdateTime(TimeUtil.getCurrentTimeS()); devLaunchStatus.setErrCode(optRes.getErrorCode()); } launchStatusProcessPublisher.publishLaunchStatusProcess(userId, serverKey, LaunchStatusProgress.builder() .status(LaunchStatus.CONNECT_FAILED.getValue()) .failedCode(optRes.getErrorCode()) .build()); } cloudAccessCache.saveDevLaunchInfo(device.getDeviceId(), devLaunchStatus); } }); } catch (RejectedExecutionException ex) { log.error(ex.toString(), ex); return OperationResponse.error(ResponseCode.LAUNCH_THREAD_INSUFFICIENT); } return OperationResponse.ok(); } public OperationResponse<StartLaunchResponse> handleInternalForwardLaunch(HandleInternalForwardLaunchRequest request) { if (log.isDebugEnabled()) { log.debug("HandleInternalForwardLaunchRequest is {}", CustomPIIMaskUtil.parseToStr(request)); } String siteKey = request.getSiteKey(); String serverKey = request.getServerKey(); String email = request.getEmail(); String username = request.getUsername(); String userId = request.getUserId(); String accountId = getAccountIdByUserName(username); String sessionId = request.getSessionId(); String tplinkIdToken = request.getTplinkIdToken(); if (StringUtil.isEmpty(serverKey)) { log.warn("Invalid codedDeviceId, is empty"); return OperationResponse.error(ResponseCode.INVALID_REQUEST_PARAMS); } if (Objects.isNull(accountId)) { log.error("tenant not fount, userId = {}", userId); return OperationResponse.error(ResponseCode.TENANT_NOT_EXIST); } String deviceId = AESUtils.decrypt(serverKey); if (log.isDebugEnabled()) { log.debug("{} start launch to {}", CustomPIIMaskUtil.hash(request.getEmail()), CustomPIIMaskUtil.encrypt(deviceId)); } DeviceDTO device; try { OperationResponse<DeviceDTO> deviceResponse = getDeviceBasicInfoWithOutCache(accountId, deviceId, platformCloudProps.getHost()); device = deviceResponse.getResult(); if (deviceResponse.isError() || Objects.isNull(device)) { log.warn("Failed get deviceInfo of {} for {} , err is {}:{}", CustomPIIMaskUtil.encrypt(deviceId), CustomPIIMaskUtil.hash(email), deviceResponse.getErrorCode(), deviceResponse.getMessage()); if (deviceResponse.getErrorCode() == ResponseCode.CLOUD_ACCOUNT_NOT_BOUND.getCode()) { return OperationResponse.error(ResponseCode.LAUNCH_FAILED_DEVICE_NOT_BOUND); } else { return OperationResponse.error(ResponseCode.LAUNCH_FAILED); } } } catch (Exception ex) { log.error(ex.toString(), ex); log.warn("Failed get deviceInfo of {} for {}", CustomPIIMaskUtil.encrypt(deviceId), CustomPIIMaskUtil.hash(email)); return OperationResponse.error(ResponseCode.LAUNCH_FAILED); } if (device.getStatus() != Constant.ONLINE) { log.warn("Launch failed, device {} is offline with {} ", CustomPIIMaskUtil.encrypt(deviceId), device.getAppServerUrl()); return OperationResponse.error(ResponseCode.CLOUD_DEVICE_OFFLINE); } int controllerVerInt; try { controllerVerInt = VersionUtils.parseFwVersion(device.getControllerVersion()); if (controllerVerInt < 0) { log.error("Invalid vms version {}", device.getControllerVersion()); return OperationResponse.error(ResponseCode.INTERNAL_SERVER_ERROR); } } catch (Exception ex) { log.error(ex.toString(), ex); return OperationResponse.error(ResponseCode.INTERNAL_SERVER_ERROR); } if (proxyHttpServer.isDeviceConnectedLocal(deviceId)) { synchronized (ProxyClientsCache.getConnectionLock(deviceId)) { DevLaunchInfo devLaunchStatus = cloudAccessCache.getDevLaunchInfo(deviceId); if (devLaunchStatus != null && Objects.equals(devLaunchStatus.getLaunchStatus(), LaunchStatus.CONNECTING) && TimeUtil.isNotTimeoutS(devLaunchStatus.getUpdateTime(), Constant.PROXY_CONNECTING_TIMEOUT_S)) { if (log.isDebugEnabled()) { log.debug("Is connecting now, please wait {}.", CustomPIIMaskUtil.hash(email)); } // 直接返回成功 return OperationResponse.ok(); } } return launchToController(serverKey, email, userId, accountId, sessionId, tplinkIdToken, siteKey, device, controllerVerInt); } else { log.debug("device {} belongs to this server, but not connected, please reconnect", deviceId); // 通知原服务器重连 request.getResponse().setHeader(Constant.ERROR_HEADER_NAME, Constant.NEED_RECONNECT); return OperationResponse.error(ResponseCode.PROXY_DISCONNECTED_NEED_RECONNECT); } } HttpForwardServiceImpl实现了httpForwardService.forwardLaunchRequest @Override public OperationResponse forwardLaunchRequest(List<String> serverIps, InternalForwardLaunchRequest request) { try { int internalForwardPort = cloudAccessProps.getPortFilter().getInternalLocalPort(); String url = String.format(Constant.LAUNCH_INTERNAL_FORWARD_URL, serverIps.get(0), internalForwardPort, request.getServerKey(), request.getEmail(), request.getUsername(), request.getUserId(), request.getSessionId(), request.getTplinkIdToken()); log.debug("forward launch disconnect request to same region other server : {}", url); HttpClientExecutor executor = buildLaunchExecutor(url); executor.addHeaderParam(Constant.HEADER_CLOUD_REQUEST, "true"); return forwardLaunchReq2OtherServer(executor); } catch (Exception ex) { log.warn(ex.toString(), ex); return OperationResponse.error(ResponseCode.GENERAL_ERROR); } } @Nonnull private HttpClientExecutor buildLaunchExecutor(String urlStr) { HttpClientExecutor executor = nbuCloudExecutorFactory.getPostExecutor(urlStr); executor.maxConnectionSeconds(Constant.INTERNAL_FORWARD_MAX_CONN_SECONDS); executor.maxSocketSeconds(Constant.INTERNAL_FORWARD_MAX_SOCKET_SECONDS); return executor; } private OperationResponse forwardLaunchReq2OtherServer(HttpClientExecutor executor) { HttpForwardWsResponseHandler responseHandler = new HttpForwardWsResponseHandler(); HttpClientErrCode errCode = executor.execute(responseHandler); if (!Objects.equals(errCode, HttpClientErrCode.OK)) { log.warn("execute forwardLaunchReq2OtherServer failed, err is {}", errCode.getMsg()); if (Objects.equals(errCode, HttpClientErrCode.CONNECTION_TIMEOUT)) { return OperationResponse.error(ResponseCode.LAUNCH_FAILED_NETWORK_TIMEOUT); } else { return OperationResponse.error(ResponseCode.LAUNCH_FAILED); } } ResErrorCode errorCode = responseHandler.getResErrorCode(); if (Objects.nonNull(errorCode)) { log.warn("forwardLaunchReq2OtherServer failed, err is {}", errorCode.getErrMsg()); return OperationResponse.error(errorCode.getErrCode(), errorCode.getErrMsg()); } return OperationResponse.ok(); } public static final String LAUNCH_INTERNAL_FORWARD_URL = "http://%s:%s/v1/internal/internalForwardLaunchReq?serverKey=%s&email=%s&username=%s&userId=%s&sessionId=%s&tplinkIdToken=%s"; 然后InternalController中接收请求: @PostMapping("/v1/internal/internalForwardLaunchReq") public OperationResponse internalForwardLaunch(HttpServletResponse response, String serverKey, String email, String username, String userId, String sessionId, String tplinkIdToken) { HandleInternalForwardLaunchRequest request = HandleInternalForwardLaunchRequest.builder() .response(response) .serverKey(serverKey) .email(email) .username(username) .userId(userId) .sessionId(sessionId) .tplinkIdToken(tplinkIdToken) .build(); return cloudAccessWebApiService.handleInternalForwardLaunch(request); } ProxyHttpServerImpl中实现了proxyHttpServer.handleLaunchRequest @Override public OperationResponse handleLaunchRequest(String tplinkIdToken, String accountId, String sessionId, String deviceId, int timeOutS) { log.debug("handleLaunchRequest"); if (StringUtils.isEmpty(tplinkIdToken) || StringUtils.isEmpty(accountId) || StringUtils.isEmpty(sessionId) || StringUtils.isEmpty(deviceId)) { log.warn("Invalid Launch request"); return OperationResponse.error(ResErrorCode.INVALID_REQUEST_PARAMS); } String launchReqId = ProxyUtils.generateLaunchReqId(); long currentStage; try{ synchronized (ProxyClientsCache.getConnectionLock(deviceId)) { ProxyClient proxyClient = ProxyClientsCache.getProxyConnection(deviceId); if (Objects.isNull(proxyClient) || !proxyClient.isConnected()) { // 测试日志 if (Objects.isNull(proxyClient)) { log.debug("proxyClient {} is null!", deviceId); } else { log.debug("The connection status of proxyClient {} is {}", deviceId, proxyClient.isConnected()); } log.debug(PROXY_CLIENT_DISCONNECTED, deviceId); return OperationResponse.error(ResErrorCode.PROXY_DISCONNECTED_NEED_RECONNECT); } log.debug("current launch request, proxyClient is {}", proxyClient); ProxyRequestsCache.cacheLaunchReqInfo(launchReqId); // 构建Launch请求 DefaultFullHttpRequest request = ProxyRequestBuilder.buildRestfulLaunchRequest(tplinkIdToken, accountId, sessionId, deviceId, launchReqId); ProxyClientsCache.sendProxyMessage(proxyClient.getCtx(), request); // 更新 proxy 连接的 updateTime proxyClient.setUpdateTime(TimeUtils.getCurrentTimeS()); ProxyClientsCache.cacheProxyConnection(deviceId, proxyClient); currentStage = proxyClient.getStage(); } OperationResponse operationResponse = waitAndGetLaunchResult(deviceId, timeOutS, currentStage, launchReqId); ProxyRequestsCache.removeLaunchReqInfo(launchReqId); log.debug("launch response is {}: {}", operationResponse.getErrorCode(), operationResponse.getMsg()); return operationResponse; } catch (URISyntaxException e) { log.error("Build launch request URI failed: {}", e.getMessage()); return OperationResponse.error(ResErrorCode.GENERAL_ERROR); } catch (Exception e) { log.error("Launch request failed: {}", e.getMessage()); return OperationResponse.error(ResErrorCode.GENERAL_ERROR); } } private OperationResponse waitAndGetLaunchResult(String deviceId, int timeOutS, long stage, String launchReqId) { log.debug("waitAndGetLaunchResult"); int waitCount = 0; // 轮询间隔 2ms int sleepInternalMs = 2; int waitCountMax = timeOutS * 500; ResErrorCode resErrorCode = ResErrorCode.LAUNCH_TIMEOUT; while(!ProxyRequestsCache.launchReqIsFinished(launchReqId) && waitCount < waitCountMax) { synchronized (ProxyClientsCache.getConnectionLock(deviceId)) { ProxyClient proxyClient = ProxyClientsCache.getProxyConnection(deviceId); if (proxyClient == null || !proxyClient.isConnected() || stage != proxyClient.getStage()) { log.debug(PROXY_CLIENT_DISCONNECTED, deviceId); resErrorCode = ResErrorCode.PROXY_DISCONNECTED_NEED_RECONNECT; break; } } try { Thread.sleep(sleepInternalMs); } catch (InterruptedException e) { log.warn("get an exception while waiting connect result {}", e.toString(), e); Thread.currentThread().interrupt(); } waitCount++; } if (ProxyRequestsCache.launchReqIsFinished(launchReqId)) { OperationResponse optResp = ProxyRequestsCache.getLaunchReqResult(launchReqId); if (optResp == null) { log.error("Failed to find LaunchReqInfo for {}", deviceId); return OperationResponse.error(ResErrorCode.GENERAL_ERROR, "Failed to find LaunchReqInfo"); } else if (optResp.isError()) { log.warn("Failed to launch for {}, err is {}: {}", launchReqId, optResp.getErrorCode(), optResp.getMsg()); } return optResp; } else { ProxyRequestsCache.recordLaunchReqInfo(launchReqId, OperationResponse.error(resErrorCode)); return OperationResponse.error(resErrorCode); } } ProxyUtils静态类用于生成launchReqId: private static final AtomicLong ATOMIC_LONG_LAUNCH = new AtomicLong(0L); @Nonnull public static String generateLaunchReqId() { return String.valueOf(ATOMIC_LONG_LAUNCH.incrementAndGet()); } ProxyRequestCache用于缓存launchReqId与对应的信息: private static final Map<String, LaunchReqInfo> LAUNCH_MAP = new ConcurrentHashMap<>(); public static void cacheLaunchReqInfo(String launchReqId) { log.debug("cache LaunchReqInfo: {}", launchReqId); LAUNCH_MAP.put(launchReqId, new LaunchReqInfo()); } public static void removeLaunchReqInfo(String launchReqId) { log.debug("remove LaunchReqInfo: {}", launchReqId); LAUNCH_MAP.remove(launchReqId); } public static boolean recordLaunchReqInfo(String launchReqId, OperationResponse operationResponse) { LaunchReqInfo launchReqInfo = LAUNCH_MAP.get(launchReqId); if (launchReqInfo != null && !launchReqInfo.isFinished()) { launchReqInfo.setErrCode(operationResponse.getErrorCode()); launchReqInfo.setErrMsg(operationResponse.getMsg()); launchReqInfo.setResult(operationResponse.getResult()); launchReqInfo.setFinished(true); return true; } else { log.debug("LaunchReqInfo for {} is {}", launchReqId, launchReqInfo); } return false; } public static boolean launchReqIsFinished(String launchReqId) { LaunchReqInfo launchReqInfo = LAUNCH_MAP.get(launchReqId); if (launchReqInfo != null) { return launchReqInfo.isFinished(); }else { return true; } } @Nullable public static OperationResponse getLaunchReqResult(String launchReqId) { LaunchReqInfo launchReqInfo = LAUNCH_MAP.get(launchReqId); if (launchReqInfo != null) { return OperationResponse.error(ResErrorCode.valueOf(launchReqInfo.getErrCode()), launchReqInfo.getResult()); } else { return null; } } @Getter @Setter private static class LaunchReqInfo<T> { private int errCode; private String errMsg; /** * 请求是否完成(失败或接收到响应) */ private boolean finished; private T result; public LaunchReqInfo() { this.finished = false; this.errCode = 0; this.errMsg = null; this.result = null; } } ProxyRequestBuilder构造请求: /** * 构造Launch请求 */ public static DefaultFullHttpRequest buildRestfulLaunchRequest(String tplinkIdToken, String accountId, String sessionId, String deviceId, String launchReqId) throws URISyntaxException { JSONObject reqJson = new JSONObject(); reqJson.put(ProxyConstVal.TPLINK_ID_TOKEN, tplinkIdToken); reqJson.put(ProxyConstVal.ACCOUNT_ID, accountId); reqJson.put(ProxyConstVal.SESSION_ID, sessionId); String serverKey = ProxyUtils.generateServerKey(deviceId); String strUri = String.format(ProxyUriEnum.LAUNCH_URI.getUri(), launchReqId); URI uri = new URI(strUri); DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uri.toASCIIString(), Unpooled.wrappedBuffer(reqJson.toString().getBytes(StandardCharsets.UTF_8))); request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); request.headers().set(HttpHeaderNames.CONTENT_LENGTH, request.content().readableBytes()); request.headers().set(HttpHeaderNames.COOKIE, ProxyConstVal.DT_TOKEN + serverKey); log.debug("request is {}, requestBody is {}", request, request.content().toString(CharsetUtil.UTF_8)); return request; } 目前出现的问题:有两个实例A与B,假设连接在实例A上,实例B能够转发launch请求给A,也能够得到响应,但是会出现重复的launchReqId,比如直接发送到实例A的launch请求的launchReqId为1、2、3、4、5、6,但是由实例B转发给实例A的launch请求的launchReqId却是1、2、6,理想情况launchReqId应该是单调递增的,为什么会这样?
10-31
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值