增加采集内存详情、健康状态方法
This commit is contained in:
@@ -77,6 +77,8 @@ public enum MsgEnum {
|
|||||||
|
|
||||||
tcpdump结果上报("TCPDUMP_RESULT"),
|
tcpdump结果上报("TCPDUMP_RESULT"),
|
||||||
|
|
||||||
|
内存详情上报("MEMORY_DETAILS"),
|
||||||
|
|
||||||
多网IP探测上报("NETWORK_DETECT");
|
多网IP探测上报("NETWORK_DETECT");
|
||||||
|
|
||||||
private String value;
|
private String value;
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.tongran.agent.client.core.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class MemoryDetailsVO {
|
||||||
|
private String name; // 名称(如 DIMM000)
|
||||||
|
private String location; // 位置(mainboard)
|
||||||
|
private String manufacturer; // 厂商(Samsung)
|
||||||
|
private Long capacity; // 容量(MB,如 32768)
|
||||||
|
private Integer frequency; // 主频(MHz,如 2133)
|
||||||
|
private String serialNumber; // 序列号(如 0x32A1EA07)
|
||||||
|
private String type; // 类型(DDR4)
|
||||||
|
private Integer minVoltage; // 最小电压(mV,如 1200)
|
||||||
|
private String rank; // RANK(列)(如 4 rank)
|
||||||
|
private String bitWidth; // 位宽(如 72 bit)
|
||||||
|
private String technology; // 技术(Synchronous|Registered (B N/A)
|
||||||
|
private String partNumber; // 部件编码(如 M386A4G40DM0-CPB)
|
||||||
|
private String healthStatus; // 健康状态(1正常/0配置错误)
|
||||||
|
}
|
||||||
@@ -378,8 +378,22 @@ public class BusinessTasks {
|
|||||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.系统其他上报.getValue()).data(jsonObject.toString()).build();
|
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.系统其他上报.getValue()).data(jsonObject.toString()).build();
|
||||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||||
AssertLog.info("发送系统其他信息-内存利用率信息包={}",JSON.toJSONString(message));
|
AssertLog.info("发送系统其他信息-内存利用率信息包={}",JSON.toJSONString(message));
|
||||||
|
// 执行内存详情包
|
||||||
|
List<MemoryDetailsVO> memDetailsList = memoryService.getMemoryDetails();
|
||||||
|
Integer total = memoryService.getMemorySlotsNum();
|
||||||
|
JSONObject object = new JSONObject();
|
||||||
|
if(memDetailsList != null && !memDetailsList.isEmpty()){
|
||||||
|
object.put("details", memDetailsList);
|
||||||
|
if(total != null){
|
||||||
|
object.put("total", total);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object.put("timestamp", timestamp);
|
||||||
|
Message memDetails = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.内存详情上报.getValue()).data(object.toString()).build();
|
||||||
|
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), memDetails);
|
||||||
|
AssertLog.info("发送内存详情信息包={}",JSON.toJSONString(memDetails));
|
||||||
}
|
}
|
||||||
AssertLog.info("系统其他信息-内存利用率采集定时任务执行 - task #{} completed", count);
|
AssertLog.info("系统其他信息-内存利用率采集/内存详情定时任务执行 - task #{} completed", count);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
package com.tongran.agent.client.service;
|
package com.tongran.agent.client.service;
|
||||||
|
|
||||||
|
import com.tongran.agent.client.core.vo.MemoryDetailsVO;
|
||||||
import com.tongran.agent.client.core.vo.MemoryVO;
|
import com.tongran.agent.client.core.vo.MemoryVO;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public interface MemoryService {
|
public interface MemoryService {
|
||||||
MemoryVO get();
|
MemoryVO get();
|
||||||
|
|
||||||
|
List<MemoryDetailsVO> getMemoryDetails();
|
||||||
|
|
||||||
|
Integer getMemorySlotsNum();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ public class DiskServiceImpl implements DiskService {
|
|||||||
"/dev/ram", // RAM磁盘
|
"/dev/ram", // RAM磁盘
|
||||||
"/dev/drbd", // DRBD
|
"/dev/drbd", // DRBD
|
||||||
"/dev/vd", // VirtIO (KVM)
|
"/dev/vd", // VirtIO (KVM)
|
||||||
"/dev/xvd" // Xen
|
"/dev/xvd", // Xen
|
||||||
|
"/dev/bcache"
|
||||||
};
|
};
|
||||||
@Override
|
@Override
|
||||||
public List<DiskVO> diskList(long timestamp) {
|
public List<DiskVO> diskList(long timestamp) {
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
package com.tongran.agent.client.service.impl;
|
package com.tongran.agent.client.service.impl;
|
||||||
|
|
||||||
|
import com.tongran.agent.client.core.vo.MemoryDetailsVO;
|
||||||
import com.tongran.agent.client.core.vo.MemoryVO;
|
import com.tongran.agent.client.core.vo.MemoryVO;
|
||||||
import com.tongran.agent.client.service.MemoryService;
|
import com.tongran.agent.client.service.MemoryService;
|
||||||
import com.tongran.agent.client.utils.AgentDataUtil;
|
import com.tongran.agent.client.utils.AgentDataUtil;
|
||||||
import com.tongran.agent.client.utils.AssertLog;
|
import com.tongran.agent.client.utils.AssertLog;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class MemoryServiceImpl implements MemoryService {
|
public class MemoryServiceImpl implements MemoryService {
|
||||||
@@ -72,5 +79,332 @@ public class MemoryServiceImpl implements MemoryService {
|
|||||||
return memoryVO;
|
return memoryVO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<MemoryDetailsVO> getMemoryDetails() {
|
||||||
|
List<MemoryDetailsVO> memoryList = new ArrayList<>();
|
||||||
|
String command = "dmidecode -t memory";
|
||||||
|
Process process = null;
|
||||||
|
BufferedReader reader = null;
|
||||||
|
// 获取 ipmitool sdr type "Memory" 的输出,用于后续匹配
|
||||||
|
List<String> sdrLines = getIpmitoolSdrLines();
|
||||||
|
try {
|
||||||
|
// 执行过滤命令
|
||||||
|
ProcessBuilder processBuilder = new ProcessBuilder(
|
||||||
|
"bash", "-c", command + " | awk '/^Handle 0x/ {if (buffer && buffer ~ /Size: [0-9]/) print buffer; buffer=$0; next} {buffer=buffer ORS $0} END {if (buffer && buffer ~ /Size: [0-9]/) print buffer}'"
|
||||||
|
);
|
||||||
|
processBuilder.redirectErrorStream(true);
|
||||||
|
|
||||||
|
process = processBuilder.start();
|
||||||
|
reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||||
|
|
||||||
|
StringBuilder block = new StringBuilder();
|
||||||
|
String line;
|
||||||
|
int totalMemoryCount = 0;
|
||||||
|
|
||||||
|
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
block.append(line).append("\n");
|
||||||
|
|
||||||
|
if (line.trim().isEmpty() && block.length() > 0) {
|
||||||
|
MemoryDetailsVO vo = parseMemoryBlock(block.toString(), sdrLines);
|
||||||
|
if (vo != null) {
|
||||||
|
memoryList.add(vo);
|
||||||
|
totalMemoryCount++;
|
||||||
|
}
|
||||||
|
block.setLength(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理最后一个块
|
||||||
|
if (block.length() > 0) {
|
||||||
|
MemoryDetailsVO vo = parseMemoryBlock(block.toString(), sdrLines);
|
||||||
|
if (vo != null) {
|
||||||
|
memoryList.add(vo);
|
||||||
|
totalMemoryCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待命令完成
|
||||||
|
int exitCode = process.waitFor();
|
||||||
|
if (exitCode != 0) {
|
||||||
|
AssertLog.warn("dmidecode命令执行异常, 退出码: {}", exitCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录统计日志
|
||||||
|
if (totalMemoryCount > 0) {
|
||||||
|
AssertLog.info("成功获取内存信息, 共发现 {} 根内存条", totalMemoryCount);
|
||||||
|
|
||||||
|
// 计算总容量
|
||||||
|
long totalCapacityGB = memoryList.stream()
|
||||||
|
.mapToLong(MemoryDetailsVO::getCapacity)
|
||||||
|
.sum() / 1024L;
|
||||||
|
AssertLog.info("总内存容量: {} GB", totalCapacityGB);
|
||||||
|
} else {
|
||||||
|
AssertLog.warn("未检测到已安装的内存条");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
AssertLog.error("执行内存信息获取命令失败: {}", e.getMessage(), e);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
AssertLog.error("命令执行被中断: {}", e.getMessage(), e);
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
} catch (Exception e) {
|
||||||
|
AssertLog.error("解析内存信息时发生未知错误: {}", e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
// 清理资源
|
||||||
|
if (reader != null) {
|
||||||
|
try {
|
||||||
|
reader.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
AssertLog.warn("关闭流时发生错误: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (process != null) {
|
||||||
|
process.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 处理 dmidecode 未监测到但 ipmitool 检测到异常的插槽
|
||||||
|
if (!sdrLines.isEmpty()) {
|
||||||
|
for (String sdrLine : sdrLines) {
|
||||||
|
// 提取 DIMM 名称,如 "DIMM110"
|
||||||
|
String dimmName = sdrLine.split("\\|")[0].trim();
|
||||||
|
boolean alreadyExists = memoryList.stream()
|
||||||
|
.anyMatch(mem -> dimmName.equals(mem.getName()));
|
||||||
|
|
||||||
|
// 如果 dmidecode 中没有这个插槽,但 ipmitool 检测到异常
|
||||||
|
if (!alreadyExists) {
|
||||||
|
MemoryDetailsVO errorSlot = new MemoryDetailsVO();
|
||||||
|
errorSlot.setName(dimmName);
|
||||||
|
errorSlot.setLocation("Unknown");
|
||||||
|
errorSlot.setManufacturer("Unknown");
|
||||||
|
errorSlot.setCapacity(0L); // 没有安装内存,容量为0
|
||||||
|
errorSlot.setFrequency(0);
|
||||||
|
errorSlot.setSerialNumber("Unknown");
|
||||||
|
errorSlot.setType("Unknown");
|
||||||
|
errorSlot.setMinVoltage(0);
|
||||||
|
errorSlot.setRank("Unknown");
|
||||||
|
errorSlot.setBitWidth("Unknown");
|
||||||
|
errorSlot.setTechnology("Unknown");
|
||||||
|
errorSlot.setPartNumber("Unknown");
|
||||||
|
errorSlot.setHealthStatus("0"); // 异常状态
|
||||||
|
|
||||||
|
memoryList.add(errorSlot);
|
||||||
|
AssertLog.debug("添加未在dmidecode中监测到的异常插槽: {}", dimmName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return memoryList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getMemorySlotsNum() {
|
||||||
|
ProcessBuilder processBuilder = null;
|
||||||
|
Process process = null;
|
||||||
|
BufferedReader reader = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用bash执行管道命令
|
||||||
|
processBuilder = new ProcessBuilder(
|
||||||
|
"bash", "-c",
|
||||||
|
"dmidecode -t memory | grep 'Number Of Devices:' | awk '{print $4}'"
|
||||||
|
);
|
||||||
|
process = processBuilder.start();
|
||||||
|
|
||||||
|
reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||||
|
String line;
|
||||||
|
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
if (!line.trim().isEmpty()) {
|
||||||
|
try {
|
||||||
|
Integer slots = Integer.parseInt(line.trim());
|
||||||
|
AssertLog.info("成功获取内存插槽数量: " + slots);
|
||||||
|
return slots;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
AssertLog.info("解析内存插槽数量失败,输出内容: " + line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AssertLog.info("未找到内存插槽数量信息");
|
||||||
|
return null;
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
AssertLog.info("执行命令时发生IO异常: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
AssertLog.info("获取内存插槽数量时发生未知异常: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (reader != null) {
|
||||||
|
reader.close();
|
||||||
|
}
|
||||||
|
if (process != null) {
|
||||||
|
process.destroy();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
AssertLog.info("关闭资源时发生异常: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 获取 ipmitool sdr type "Memory" 的输出行列表
|
||||||
|
private List<String> getIpmitoolSdrLines() {
|
||||||
|
List<String> lines = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
ProcessBuilder processBuilder = new ProcessBuilder(
|
||||||
|
"bash", "-c", "ipmitool sdr type \"Memory\" | grep -E \"DIMM.*Configuration Error\""
|
||||||
|
);
|
||||||
|
processBuilder.redirectErrorStream(true);
|
||||||
|
|
||||||
|
Process process = processBuilder.start();
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||||
|
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
lines.add(line.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
int exitCode = process.waitFor();
|
||||||
|
if (exitCode != 0) {
|
||||||
|
AssertLog.warn("ipmitool命令执行异常, 退出码: {}", exitCode);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
AssertLog.error("获取ipmitool sdr信息失败: {}", e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析内存块,并注入健康状态
|
||||||
|
private MemoryDetailsVO parseMemoryBlock(String block, List<String> sdrLines) {
|
||||||
|
try {
|
||||||
|
// 跳过空块
|
||||||
|
if (block.trim().isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryDetailsVO vo = new MemoryDetailsVO();
|
||||||
|
|
||||||
|
// 正则提取字段
|
||||||
|
Pattern namePattern = Pattern.compile("Locator:\\s*(\\w+)"); // 提取 DIMM000
|
||||||
|
Pattern locationPattern = Pattern.compile("Bank Locator:\\s*(.+)");
|
||||||
|
Pattern manufacturerPattern = Pattern.compile("Manufacturer:\\s*(.+)");
|
||||||
|
Pattern capacityPattern = Pattern.compile("Size:\\s*(\\d+)\\s*GB");
|
||||||
|
Pattern frequencyPattern = Pattern.compile("Speed:\\s*(\\d+)", Pattern.CASE_INSENSITIVE);
|
||||||
|
Pattern serialNumberPattern = Pattern.compile("Serial Number:\\s*(.+)");
|
||||||
|
Pattern typePattern = Pattern.compile("Type:\\s*(.+)");
|
||||||
|
Pattern minVoltagePattern = Pattern.compile("Minimum Voltage:\\s*(\\d+\\.?\\d*)\\s*V");
|
||||||
|
Pattern rankPattern = Pattern.compile("Rank:\\s*(.+)");
|
||||||
|
Pattern bitWidthPattern = Pattern.compile("Total Width:\\s*(.+) bit");
|
||||||
|
Pattern technologyPattern = Pattern.compile("Type Detail:\\s*(.+)");
|
||||||
|
Pattern partNumberPattern = Pattern.compile("Part Number:\\s*(.+)");
|
||||||
|
|
||||||
|
Matcher nameMatcher = namePattern.matcher(block);
|
||||||
|
if (nameMatcher.find()) {
|
||||||
|
vo.setName(nameMatcher.group(1).trim());
|
||||||
|
} else {
|
||||||
|
AssertLog.debug("未找到内存名称字段: {}", block);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 位置固定为 mainboard
|
||||||
|
vo.setLocation("mainboard");
|
||||||
|
|
||||||
|
Matcher manufacturerMatcher = manufacturerPattern.matcher(block);
|
||||||
|
if (manufacturerMatcher.find()) {
|
||||||
|
vo.setManufacturer(manufacturerMatcher.group(1).trim());
|
||||||
|
} else {
|
||||||
|
vo.setManufacturer("Unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher capacityMatcher = capacityPattern.matcher(block);
|
||||||
|
if (capacityMatcher.find()) {
|
||||||
|
long gb = Long.parseLong(capacityMatcher.group(1));
|
||||||
|
vo.setCapacity(gb * 1024L); // GB 转 MB
|
||||||
|
} else {
|
||||||
|
AssertLog.debug("未找到内存容量字段: {}", block);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher frequencyMatcher = frequencyPattern.matcher(block);
|
||||||
|
if (frequencyMatcher.find()) {
|
||||||
|
vo.setFrequency(Integer.parseInt(frequencyMatcher.group(1)));
|
||||||
|
} else {
|
||||||
|
vo.setFrequency(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher serialNumberMatcher = serialNumberPattern.matcher(block);
|
||||||
|
if (serialNumberMatcher.find()) {
|
||||||
|
vo.setSerialNumber(serialNumberMatcher.group(1).trim());
|
||||||
|
} else {
|
||||||
|
vo.setSerialNumber("Unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher typeMatcher = typePattern.matcher(block);
|
||||||
|
if (typeMatcher.find()) {
|
||||||
|
vo.setType(typeMatcher.group(1).trim());
|
||||||
|
} else {
|
||||||
|
vo.setType("Unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher minVoltageMatcher = minVoltagePattern.matcher(block);
|
||||||
|
if (minVoltageMatcher.find()) {
|
||||||
|
String voltageStr = minVoltageMatcher.group(1);
|
||||||
|
// 处理 1.2 V -> 1200 mV
|
||||||
|
double voltage = Double.parseDouble(voltageStr);
|
||||||
|
vo.setMinVoltage((int)(voltage * 1000));
|
||||||
|
} else {
|
||||||
|
vo.setMinVoltage(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher rankMatcher = rankPattern.matcher(block);
|
||||||
|
if (rankMatcher.find()) {
|
||||||
|
vo.setRank(rankMatcher.group(1).trim() + " rank");
|
||||||
|
} else {
|
||||||
|
vo.setRank("Unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher bitWidthMatcher = bitWidthPattern.matcher(block);
|
||||||
|
if (bitWidthMatcher.find()) {
|
||||||
|
vo.setBitWidth(bitWidthMatcher.group(1).trim() + " bit");
|
||||||
|
} else {
|
||||||
|
vo.setBitWidth("Unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher technologyMatcher = technologyPattern.matcher(block);
|
||||||
|
if (technologyMatcher.find()) {
|
||||||
|
vo.setTechnology(technologyMatcher.group(1).trim());
|
||||||
|
} else {
|
||||||
|
vo.setTechnology("Unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher partNumberMatcher = partNumberPattern.matcher(block);
|
||||||
|
if (partNumberMatcher.find()) {
|
||||||
|
vo.setPartNumber(partNumberMatcher.group(1).trim());
|
||||||
|
} else {
|
||||||
|
vo.setPartNumber("Unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查健康状态
|
||||||
|
String memoryName = vo.getName(); // 例如 "DIMM000"
|
||||||
|
boolean hasConfigError = sdrLines.stream()
|
||||||
|
.anyMatch(line -> line.startsWith(memoryName));
|
||||||
|
|
||||||
|
if (hasConfigError) {
|
||||||
|
vo.setHealthStatus("0");
|
||||||
|
} else {
|
||||||
|
vo.setHealthStatus("1");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调试日志
|
||||||
|
AssertLog.debug("解析内存信息: {} - {}GB - {}MHz - 健康状态: {}",
|
||||||
|
vo.getName(), vo.getCapacity()/1024L, vo.getFrequency(), vo.getHealthStatus());
|
||||||
|
|
||||||
|
return vo;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
AssertLog.error("解析内存块时发生错误: {}", e.getMessage(), e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user