mirror of
https://github.com/slhaf/Partner.git
synced 2026-05-12 08:43:02 +08:00
feat(LocalRunnerClient): support repairing description data while OVERFLOW event happened in DescMcpServer
This commit is contained in:
@@ -36,6 +36,7 @@ import java.io.BufferedReader;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.URI;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.*;
|
import java.nio.file.*;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
@@ -45,8 +46,6 @@ import java.util.concurrent.ExecutorService;
|
|||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@@ -801,7 +800,7 @@ public class LocalRunnerClient extends RunnerClient {
|
|||||||
private static final class Desc extends LocalWatchEventProcessor {
|
private static final class Desc extends LocalWatchEventProcessor {
|
||||||
|
|
||||||
private final McpStatelessAsyncServer mcpDescServer;
|
private final McpStatelessAsyncServer mcpDescServer;
|
||||||
private final HashMap<String, String> descCache = new HashMap<>();
|
private final ConcurrentHashMap<String, String> descCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private Desc(ConcurrentHashMap<String, MetaActionInfo> existedMetaActions, McpStatelessAsyncServer mcpDescServer, WatchContext ctx) {
|
private Desc(ConcurrentHashMap<String, MetaActionInfo> existedMetaActions, McpStatelessAsyncServer mcpDescServer, WatchContext ctx) {
|
||||||
super(existedMetaActions, ctx);
|
super(existedMetaActions, ctx);
|
||||||
@@ -828,14 +827,7 @@ public class LocalRunnerClient extends RunnerClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean normal(String fileName) {
|
private boolean normal(String fileName) {
|
||||||
String pattern = "[a-z][A-Z]+::[a-z][A-Z]+.desk.json";
|
return fileName.endsWith(".desc.json") && fileName.contains("::");
|
||||||
Pattern p = Pattern.compile(pattern);
|
|
||||||
Matcher matcher = p.matcher(fileName);
|
|
||||||
if (!matcher.find()) {
|
|
||||||
log.error("文件名称不符合要求: {}", fileName);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean normal(File file) {
|
private boolean normal(File file) {
|
||||||
@@ -847,7 +839,6 @@ public class LocalRunnerClient extends RunnerClient {
|
|||||||
return normal(path.toFile());
|
return normal(path.toFile());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("UnusedReturnValue")
|
|
||||||
private boolean addResource(File file) {
|
private boolean addResource(File file) {
|
||||||
String name = file.getName();
|
String name = file.getName();
|
||||||
if (!normal(name)) {
|
if (!normal(name)) {
|
||||||
@@ -858,7 +849,11 @@ public class LocalRunnerClient extends RunnerClient {
|
|||||||
MetaActionInfo info = JSONUtil.readJSONObject(file, StandardCharsets.UTF_8).toBean(MetaActionInfo.class);
|
MetaActionInfo info = JSONUtil.readJSONObject(file, StandardCharsets.UTF_8).toBean(MetaActionInfo.class);
|
||||||
String uri = ctx.root.resolve(name).toUri().toString();
|
String uri = ctx.root.resolve(name).toUri().toString();
|
||||||
descCache.put(uri, JSONObject.toJSONString(info));
|
descCache.put(uri, JSONObject.toJSONString(info));
|
||||||
mcpDescServer.addResource(buildAsyncResourceSpecification(name, uri)).subscribe();
|
mcpDescServer.addResource(buildAsyncResourceSpecification(name, uri)).block();
|
||||||
|
String actionKey = name.replace(".desc.json", "");
|
||||||
|
if (existedMetaActions.containsKey(actionKey)) {
|
||||||
|
existedMetaActions.put(actionKey, info);
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("desc.json 解析失败: {}", file.getAbsolutePath());
|
log.error("desc.json 解析失败: {}", file.getAbsolutePath());
|
||||||
return false;
|
return false;
|
||||||
@@ -866,6 +861,17 @@ public class LocalRunnerClient extends RunnerClient {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void removeResource(Path path) {
|
||||||
|
String uri = path.toUri().toString();
|
||||||
|
String actionKey = path.getFileName().toString().replace(".desc.json", "");
|
||||||
|
|
||||||
|
descCache.remove(uri);
|
||||||
|
mcpDescServer.removeResource(uri).block();
|
||||||
|
if (existedMetaActions.containsKey(actionKey)) {
|
||||||
|
resetMetaActionInfo(existedMetaActions.get(actionKey));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NotNull
|
@NotNull
|
||||||
protected LocalWatchServiceBuild.InitLoader buildLoad() {
|
protected LocalWatchServiceBuild.InitLoader buildLoad() {
|
||||||
@@ -892,30 +898,13 @@ public class LocalRunnerClient extends RunnerClient {
|
|||||||
return (thisDir, context) -> {
|
return (thisDir, context) -> {
|
||||||
// 排除目录事件、名称不符合要求的文件
|
// 排除目录事件、名称不符合要求的文件
|
||||||
String fileName = context.getFileName().toString();
|
String fileName = context.getFileName().toString();
|
||||||
if (!Files.isDirectory(context) || !normal(fileName)) {
|
if (!Files.isRegularFile(context) || !normal(fileName)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 先尝试能否正常读取,再决定是否步入更新逻辑
|
if (!addResource(context.toFile())) {
|
||||||
MetaActionInfo info;
|
removeResource(context);
|
||||||
try {
|
|
||||||
info = JSONUtil.readJSONObject(context.toFile(), StandardCharsets.UTF_8).toBean(MetaActionInfo.class);
|
|
||||||
} catch (Exception e) {
|
|
||||||
// 加载失败也需要移除对应的 cache
|
|
||||||
descCache.remove(context.toUri().toString());
|
|
||||||
log.warn("desc.json 加载失败: {}", context);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 要处理的 MODIFY 上下文只有一种
|
|
||||||
// *.desc.json 发生变更时,检查是否存在于 existedMetaActions 内部
|
|
||||||
// 如果存在,则读取并更新对应的 info,同时更新 descCache
|
|
||||||
// 如果不存在,则只更新 descCache
|
|
||||||
String actionKey = fileName.replace(".desc.json", "");
|
|
||||||
if (existedMetaActions.containsKey(actionKey)) {
|
|
||||||
existedMetaActions.put(actionKey, info);
|
|
||||||
}
|
|
||||||
descCache.put(context.toUri().toString(), JSONObject.toJSONString(info));
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -937,11 +926,7 @@ public class LocalRunnerClient extends RunnerClient {
|
|||||||
|
|
||||||
// DELETE 事件发生后,需要移除对应的 descCache 条目;
|
// DELETE 事件发生后,需要移除对应的 descCache 条目;
|
||||||
// 如果存在对应的 info,也需要将其中的额外信息进行重置,只保留 Tools 自身的信息
|
// 如果存在对应的 info,也需要将其中的额外信息进行重置,只保留 Tools 自身的信息
|
||||||
descCache.remove(context.toUri().toString());
|
removeResource(context);
|
||||||
String actionKey = fileName.replace(".desc.json", "");
|
|
||||||
if (existedMetaActions.containsKey(actionKey)) {
|
|
||||||
resetMetaActionInfo(existedMetaActions.get(actionKey));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -956,7 +941,57 @@ public class LocalRunnerClient extends RunnerClient {
|
|||||||
@Override
|
@Override
|
||||||
@NotNull
|
@NotNull
|
||||||
protected LocalWatchServiceBuild.EventHandler buildOverflow() {
|
protected LocalWatchServiceBuild.EventHandler buildOverflow() {
|
||||||
return null;
|
return (thisDir, context) -> {
|
||||||
|
// 对于 OVERFLOW 事件,需要依据当前目录下的所有 *.desc.json 针对现有内容进行修复
|
||||||
|
List<File> files;
|
||||||
|
try (Stream<Path> stream = Files.list(ctx.root)) {
|
||||||
|
files = stream.filter(Files::isRegularFile).filter(this::normal).map(Path::toFile).toList();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("目录无法访问: {}", ctx.root);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<String> currentUriStr = new HashSet<>();
|
||||||
|
|
||||||
|
for (File file : files) {
|
||||||
|
MetaActionInfo info = null;
|
||||||
|
try {
|
||||||
|
info = JSONUtil.readJSONObject(file, StandardCharsets.UTF_8).toBean(MetaActionInfo.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("desc.json 读取失败: {}", file.toPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean available = info != null;
|
||||||
|
|
||||||
|
// 如果读取成功,则更新 descCache
|
||||||
|
// 若在 existedMetaActions 中存在,则更新对应 info
|
||||||
|
String uriStr = file.toURI().toString();
|
||||||
|
currentUriStr.add(uriStr);
|
||||||
|
|
||||||
|
if (available) {
|
||||||
|
// 由于涉及内容均为 map,所以额外判断没有必要,直接进行 add 行为即可
|
||||||
|
addResource(file);
|
||||||
|
} else {
|
||||||
|
// 如果读取失败,则移除对应 descCache 条目
|
||||||
|
// 若在 existedMetaActions 中存在,则重置对应 info
|
||||||
|
removeResource(file.toPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> serverUris = mcpDescServer.listResources()
|
||||||
|
.map(McpSchema.Resource::uri)
|
||||||
|
.collectList()
|
||||||
|
.block();
|
||||||
|
if (serverUris == null) {
|
||||||
|
log.error("无法获取 DescMcpServer 持有的资源列表");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (String uri : serverUris) {
|
||||||
|
if (!currentUriStr.contains(uri)) {
|
||||||
|
removeResource(Paths.get(URI.create(uri)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user