mirror of
https://github.com/slhaf/Partner.git
synced 2026-05-12 16:53:04 +08:00
refactor(agent): introduce AgentBootstrap for startup wiring and simplify app launch
This commit is contained in:
@@ -1,15 +1,10 @@
|
|||||||
package work.slhaf.partner;
|
package work.slhaf.partner;
|
||||||
|
|
||||||
import work.slhaf.partner.common.vector.VectorClientRegistry;
|
|
||||||
import work.slhaf.partner.framework.agent.Agent;
|
import work.slhaf.partner.framework.agent.Agent;
|
||||||
import work.slhaf.partner.runtime.gateway.WebSocketGatewayRegistration;
|
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
boolean launched = Agent.newAgent(Main.class)
|
boolean launched = Agent.newAgent(Main.class).launch();
|
||||||
.addGatewayRegistration(WebSocketGatewayRegistration.INSTANCE)
|
|
||||||
.addConfigurable(new VectorClientRegistry())
|
|
||||||
.launch();
|
|
||||||
if (!launched) {
|
if (!launched) {
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package work.slhaf.partner.runtime;
|
||||||
|
|
||||||
|
import work.slhaf.partner.common.vector.VectorClientRegistry;
|
||||||
|
import work.slhaf.partner.framework.agent.Agent;
|
||||||
|
import work.slhaf.partner.runtime.gateway.WebSocketGatewayRegistration;
|
||||||
|
|
||||||
|
public final class PartnerAgentBootstrap extends Agent.AgentBootstrap {
|
||||||
|
|
||||||
|
public PartnerAgentBootstrap(Agent.AgentApp agentApp) {
|
||||||
|
super(agentApp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void bootstrap() {
|
||||||
|
addGatewayRegistration(WebSocketGatewayRegistration.INSTANCE);
|
||||||
|
addConfigurable(new VectorClientRegistry());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package work.slhaf.partner.framework.agent;
|
|||||||
|
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.reflections.util.ClasspathHelper;
|
||||||
import work.slhaf.partner.framework.agent.config.ConfigCenter;
|
import work.slhaf.partner.framework.agent.config.ConfigCenter;
|
||||||
import work.slhaf.partner.framework.agent.config.Configurable;
|
import work.slhaf.partner.framework.agent.config.Configurable;
|
||||||
import work.slhaf.partner.framework.agent.exception.AgentStartupException;
|
import work.slhaf.partner.framework.agent.exception.AgentStartupException;
|
||||||
@@ -9,6 +10,7 @@ import work.slhaf.partner.framework.agent.exception.ExceptionReporter;
|
|||||||
import work.slhaf.partner.framework.agent.exception.ExceptionReporterHandler;
|
import work.slhaf.partner.framework.agent.exception.ExceptionReporterHandler;
|
||||||
import work.slhaf.partner.framework.agent.factory.AgentRegisterFactory;
|
import work.slhaf.partner.framework.agent.factory.AgentRegisterFactory;
|
||||||
import work.slhaf.partner.framework.agent.factory.context.AgentContext;
|
import work.slhaf.partner.framework.agent.factory.context.AgentContext;
|
||||||
|
import work.slhaf.partner.framework.agent.factory.context.AgentRegisterContext;
|
||||||
import work.slhaf.partner.framework.agent.interaction.AgentGatewayRegistration;
|
import work.slhaf.partner.framework.agent.interaction.AgentGatewayRegistration;
|
||||||
import work.slhaf.partner.framework.agent.interaction.AgentGatewayRegistry;
|
import work.slhaf.partner.framework.agent.interaction.AgentGatewayRegistry;
|
||||||
import work.slhaf.partner.framework.agent.log.LogAdviceProvider;
|
import work.slhaf.partner.framework.agent.log.LogAdviceProvider;
|
||||||
@@ -16,9 +18,12 @@ import work.slhaf.partner.framework.agent.log.TraceSinkRegistry;
|
|||||||
import work.slhaf.partner.framework.agent.model.ModelRuntimeRegistry;
|
import work.slhaf.partner.framework.agent.model.ModelRuntimeRegistry;
|
||||||
import work.slhaf.partner.framework.agent.state.StateCenter;
|
import work.slhaf.partner.framework.agent.state.StateCenter;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.net.URL;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.*;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <h2>Agent 启动入口</h2>
|
* <h2>Agent 启动入口</h2>
|
||||||
@@ -33,7 +38,8 @@ public final class Agent {
|
|||||||
|
|
||||||
public static class AgentApp {
|
public static class AgentApp {
|
||||||
|
|
||||||
private final Class<?> applicationClass;
|
private final Set<String> scanPackages = new LinkedHashSet<>();
|
||||||
|
private final Set<String> scanDirs = new LinkedHashSet<>();
|
||||||
private final Set<AgentGatewayRegistration> gatewayRegistrations = new LinkedHashSet<>();
|
private final Set<AgentGatewayRegistration> gatewayRegistrations = new LinkedHashSet<>();
|
||||||
private final Set<ExceptionReporter> exceptionReporters = new LinkedHashSet<>();
|
private final Set<ExceptionReporter> exceptionReporters = new LinkedHashSet<>();
|
||||||
private final Set<Configurable> configurables = new LinkedHashSet<>();
|
private final Set<Configurable> configurables = new LinkedHashSet<>();
|
||||||
@@ -41,40 +47,47 @@ public final class Agent {
|
|||||||
private final Set<LifecycleHook> postShutdownHooks = new LinkedHashSet<>();
|
private final Set<LifecycleHook> postShutdownHooks = new LinkedHashSet<>();
|
||||||
|
|
||||||
private AgentApp(Class<?> clazz) {
|
private AgentApp(Class<?> clazz) {
|
||||||
this.applicationClass = clazz;
|
this.scanPackages.add(clazz.getPackageName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public AgentApp addGatewayRegistration(AgentGatewayRegistration... registrations) {
|
private void addScanPackage(String packageName) {
|
||||||
|
if (packageName != null && !packageName.isBlank()) {
|
||||||
|
this.scanPackages.add(packageName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addScanDir(String scanDir) {
|
||||||
|
if (scanDir != null && !scanDir.isBlank()) {
|
||||||
|
this.scanDirs.add(scanDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addGatewayRegistration(AgentGatewayRegistration... registrations) {
|
||||||
this.gatewayRegistrations.addAll(Set.of(registrations));
|
this.gatewayRegistrations.addAll(Set.of(registrations));
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AgentApp addConfigurable(Configurable configurable) {
|
private void addConfigurable(Configurable configurable) {
|
||||||
this.configurables.add(configurable);
|
this.configurables.add(configurable);
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AgentApp addExceptionReporter(ExceptionReporter... exceptionReporters) {
|
private void addExceptionReporter(ExceptionReporter... exceptionReporters) {
|
||||||
this.exceptionReporters.addAll(Set.of(exceptionReporters));
|
this.exceptionReporters.addAll(Set.of(exceptionReporters));
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AgentApp addPreShutdownHook(String name, Runnable action) {
|
private void addPreShutdownHook(String name, Runnable action) {
|
||||||
return addPreShutdownHook(name, 0, action);
|
addPreShutdownHook(name, 0, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AgentApp addPreShutdownHook(String name, int order, Runnable action) {
|
private void addPreShutdownHook(String name, int order, Runnable action) {
|
||||||
this.preShutdownHooks.add(new LifecycleHook(name, order, action));
|
this.preShutdownHooks.add(new LifecycleHook(name, order, action));
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AgentApp addPostShutdownHook(String name, Runnable action) {
|
private void addPostShutdownHook(String name, Runnable action) {
|
||||||
return addPostShutdownHook(name, 0, action);
|
addPostShutdownHook(name, 0, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AgentApp addPostShutdownHook(String name, int order, Runnable action) {
|
private void addPostShutdownHook(String name, int order, Runnable action) {
|
||||||
this.postShutdownHooks.add(new LifecycleHook(name, order, action));
|
this.postShutdownHooks.add(new LifecycleHook(name, order, action));
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean launch() {
|
public boolean launch() {
|
||||||
@@ -83,6 +96,13 @@ public final class Agent {
|
|||||||
ConfigCenter.INSTANCE.toString();
|
ConfigCenter.INSTANCE.toString();
|
||||||
StateCenter.INSTANCE.toString();
|
StateCenter.INSTANCE.toString();
|
||||||
|
|
||||||
|
Path externalModuleDir = ConfigCenter.INSTANCE.getPaths().getResourcesDir().resolve("module");
|
||||||
|
addScanDir(externalModuleDir.toString());
|
||||||
|
|
||||||
|
AgentRegisterContext bootstrapContext = buildRegisterContext();
|
||||||
|
runBootstraps(bootstrapContext);
|
||||||
|
AgentRegisterContext registerContext = buildRegisterContext();
|
||||||
|
|
||||||
// Keep startup order explicit so registries are ready before component scanning.
|
// Keep startup order explicit so registries are ready before component scanning.
|
||||||
for (ExceptionReporter exceptionReporter : exceptionReporters) {
|
for (ExceptionReporter exceptionReporter : exceptionReporters) {
|
||||||
exceptionReporter.register();
|
exceptionReporter.register();
|
||||||
@@ -102,9 +122,7 @@ public final class Agent {
|
|||||||
|
|
||||||
registerShutdownHooks();
|
registerShutdownHooks();
|
||||||
|
|
||||||
Path externalModuleDir = ConfigCenter.INSTANCE.getPaths().getResourcesDir().resolve("module");
|
AgentRegisterFactory.launch(registerContext);
|
||||||
AgentRegisterFactory.addScanDir(externalModuleDir.toString());
|
|
||||||
AgentRegisterFactory.launch(applicationClass.getPackageName());
|
|
||||||
|
|
||||||
// Try to init configurable, and start config listening
|
// Try to init configurable, and start config listening
|
||||||
ConfigCenter.INSTANCE.initAll();
|
ConfigCenter.INSTANCE.initAll();
|
||||||
@@ -120,6 +138,78 @@ public final class Agent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AgentRegisterContext buildRegisterContext() {
|
||||||
|
return new AgentRegisterContext(new ArrayList<>(buildScanUrls()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<URL> buildScanUrls() {
|
||||||
|
Set<URL> urls = new LinkedHashSet<>();
|
||||||
|
for (String packageName : scanPackages) {
|
||||||
|
urls.addAll(ClasspathHelper.forPackage(packageName));
|
||||||
|
}
|
||||||
|
for (String scanDir : scanDirs) {
|
||||||
|
urls.addAll(scanDirToUrls(scanDir));
|
||||||
|
}
|
||||||
|
return urls;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<URL> scanDirToUrls(String scanDir) {
|
||||||
|
Set<URL> urls = new LinkedHashSet<>();
|
||||||
|
File file = new File(scanDir);
|
||||||
|
if (!file.exists() || !file.isDirectory()) {
|
||||||
|
return urls;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
File[] files = file.listFiles();
|
||||||
|
if (files == null) {
|
||||||
|
return urls;
|
||||||
|
}
|
||||||
|
for (File item : files) {
|
||||||
|
if (item.getName().endsWith(".jar")) {
|
||||||
|
urls.add(item.toURI().toURL());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return urls;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new AgentStartupException("Failed to load scan dir URLs from: " + scanDir, "agent-bootstrap", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runBootstraps(AgentRegisterContext context) {
|
||||||
|
context.getReflections().getSubTypesOf(AgentBootstrap.class).stream()
|
||||||
|
.filter(this::isConcreteBootstrap)
|
||||||
|
.map(this::instantiateBootstrap)
|
||||||
|
.sorted(Comparator.comparingInt(AgentBootstrap::order))
|
||||||
|
.forEach(AgentBootstrap::bootstrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isConcreteBootstrap(Class<? extends AgentBootstrap> bootstrapClass) {
|
||||||
|
int modifiers = bootstrapClass.getModifiers();
|
||||||
|
return !bootstrapClass.isInterface()
|
||||||
|
&& !bootstrapClass.isAnnotation()
|
||||||
|
&& !bootstrapClass.isEnum()
|
||||||
|
&& !bootstrapClass.isArray()
|
||||||
|
&& !bootstrapClass.isPrimitive()
|
||||||
|
&& !Modifier.isAbstract(modifiers)
|
||||||
|
&& !bootstrapClass.isSynthetic()
|
||||||
|
&& !bootstrapClass.isAnonymousClass()
|
||||||
|
&& !bootstrapClass.isLocalClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
private AgentBootstrap instantiateBootstrap(Class<? extends AgentBootstrap> bootstrapClass) {
|
||||||
|
try {
|
||||||
|
Constructor<? extends AgentBootstrap> constructor = bootstrapClass.getDeclaredConstructor(AgentApp.class);
|
||||||
|
constructor.setAccessible(true);
|
||||||
|
return constructor.newInstance(this);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new AgentStartupException(
|
||||||
|
"Failed to instantiate AgentBootstrap: " + bootstrapClass.getName(),
|
||||||
|
"agent-bootstrap",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void registerShutdownHooks() {
|
private void registerShutdownHooks() {
|
||||||
AgentContext.INSTANCE.addPreShutdownHook(
|
AgentContext.INSTANCE.addPreShutdownHook(
|
||||||
"agent-gateway-registry-close",
|
"agent-gateway-registry-close",
|
||||||
@@ -153,4 +243,55 @@ public final class Agent {
|
|||||||
private record LifecycleHook(String name, int order, Runnable action) {
|
private record LifecycleHook(String name, int order, Runnable action) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static abstract class AgentBootstrap {
|
||||||
|
|
||||||
|
private final AgentApp agentApp;
|
||||||
|
|
||||||
|
protected AgentBootstrap(AgentApp agentApp) {
|
||||||
|
this.agentApp = Objects.requireNonNull(agentApp, "agentApp");
|
||||||
|
}
|
||||||
|
|
||||||
|
public int order() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void bootstrap();
|
||||||
|
|
||||||
|
protected final void addScanPackage(String packageName) {
|
||||||
|
agentApp.addScanPackage(packageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void addScanDir(String scanDir) {
|
||||||
|
agentApp.addScanDir(scanDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void addPreShutdownHook(String name, Runnable action) {
|
||||||
|
agentApp.addPreShutdownHook(name, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void addPreShutdownHook(String name, int order, Runnable action) {
|
||||||
|
agentApp.addPreShutdownHook(name, order, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void addPostShutdownHook(String name, Runnable action) {
|
||||||
|
agentApp.addPostShutdownHook(name, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void addPostShutdownHook(String name, int order, Runnable action) {
|
||||||
|
agentApp.addPostShutdownHook(name, order, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void addExceptionReporter(ExceptionReporter... exceptionReporters) {
|
||||||
|
agentApp.addExceptionReporter(exceptionReporters);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void addConfigurable(Configurable configurable) {
|
||||||
|
agentApp.addConfigurable(configurable);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void addGatewayRegistration(AgentGatewayRegistration... gatewayRegistrations) {
|
||||||
|
agentApp.addGatewayRegistration(gatewayRegistrations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,11 @@ object AgentRegisterFactory {
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun launch(packageName: String) {
|
fun launch(packageName: String) {
|
||||||
urls.addAll(packageNameToURL(packageName))
|
urls.addAll(packageNameToURL(packageName))
|
||||||
val registerContext = AgentRegisterContext(urls)
|
launch(AgentRegisterContext(urls))
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun launch(registerContext: AgentRegisterContext) {
|
||||||
// 1. 校验 Component 级别注解是否合规,避免注入到异常位置
|
// 1. 校验 Component 级别注解是否合规,避免注入到异常位置
|
||||||
ComponentAnnotationValidatorFactory().execute(registerContext)
|
ComponentAnnotationValidatorFactory().execute(registerContext)
|
||||||
// 2. 收集所有的 AgentComponent 实例
|
// 2. 收集所有的 AgentComponent 实例
|
||||||
|
|||||||
Reference in New Issue
Block a user