mirror of
https://github.com/LamGC/Oracle-Sentry.git
synced 2025-04-29 14:17:34 +00:00
feat: 添加扩展接口, 通过 SPI 机制允许外部模块为脚本添加功能组件.
通过扩展接口, 可让第三方为脚本提供功能组件, 而无需局限于 Sentry 内置功能组件. Closes #6
This commit is contained in:
parent
7a008848db
commit
e6ff28e077
@ -4,11 +4,9 @@ import com.google.common.base.Throwables;
|
||||
import net.lamgc.oracle.sentry.oci.account.OracleAccount;
|
||||
import net.lamgc.oracle.sentry.oci.account.OracleAccountManager;
|
||||
import net.lamgc.oracle.sentry.oci.compute.ComputeInstanceManager;
|
||||
import net.lamgc.oracle.sentry.script.ScriptComponentExtension;
|
||||
import net.lamgc.oracle.sentry.script.ScriptComponents;
|
||||
import net.lamgc.oracle.sentry.script.ScriptLoggerFactory;
|
||||
import net.lamgc.oracle.sentry.script.ScriptManager;
|
||||
import net.lamgc.oracle.sentry.script.tools.http.ScriptHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -24,6 +22,7 @@ import javax.annotation.PostConstruct;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
* @author LamGC
|
||||
@ -96,18 +95,29 @@ class ApplicationInitiation {
|
||||
@Bean("sentry.script.manager")
|
||||
@Autowired
|
||||
public ScriptManager initialScriptManager(ComputeInstanceManager instanceManager, OracleAccountManager accountManager) {
|
||||
ScriptComponents context = new ScriptComponents(new ScriptHttpClient(HttpClientBuilder.create()
|
||||
.build()),
|
||||
instanceManager,
|
||||
new ScriptLoggerFactory(),
|
||||
accountManager
|
||||
);
|
||||
ScriptComponents components = new ScriptComponents();
|
||||
components.addComponentObject("InstanceManager", instanceManager);
|
||||
components.addComponentObject("AccountManager", accountManager);
|
||||
|
||||
ScriptManager manager = new ScriptManager(new File(scriptsLocation), context);
|
||||
configureScriptComponentsFromExtension(components);
|
||||
|
||||
ScriptManager manager = new ScriptManager(new File(scriptsLocation), components);
|
||||
manager.loadScripts();
|
||||
return manager;
|
||||
}
|
||||
|
||||
private void configureScriptComponentsFromExtension(ScriptComponents components) {
|
||||
ServiceLoader<ScriptComponentExtension> extensions = ServiceLoader.load(ScriptComponentExtension.class);
|
||||
for (ScriptComponentExtension extension : extensions) {
|
||||
try {
|
||||
extension.configureScriptComponents(components);
|
||||
} catch (Exception e) {
|
||||
log.error("脚本组件扩展配置组件时发生未捕获异常.(Extension: {})\n{}",
|
||||
extension.getClass().getName(), Throwables.getStackTraceAsString(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
@Order(1)
|
||||
@SuppressWarnings({"MismatchedReadAndWriteOfArray", "RedundantOperationOnEmptyContainer"})
|
||||
|
@ -0,0 +1,13 @@
|
||||
package net.lamgc.oracle.sentry.script;
|
||||
|
||||
import net.lamgc.oracle.sentry.script.tools.http.ScriptHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
|
||||
public class BuiltinComponentExtension implements ScriptComponentExtension{
|
||||
@Override
|
||||
public void configureScriptComponents(ScriptComponents components) {
|
||||
components.addComponentObject("HTTP", new ScriptHttpClient(HttpClientBuilder.create()
|
||||
.build()));
|
||||
components.addComponentFactory(new ScriptLoggerFactory());
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package net.lamgc.oracle.sentry.script;
|
||||
|
||||
/**
|
||||
* 脚本组件扩展.
|
||||
* <p> 通过实现该接口, 可在脚本加载前设置组件对象, 为脚本提供更多功能.
|
||||
* <p> 实现接口后, 需按 SPI 方式添加实现.
|
||||
* @author LamGC
|
||||
*/
|
||||
public interface ScriptComponentExtension {
|
||||
|
||||
/**
|
||||
* 配置脚本组件.
|
||||
* <p> 在方法中为组件集合添加组件.
|
||||
* @param components 脚本组件集合.
|
||||
*/
|
||||
void configureScriptComponents(final ScriptComponents components);
|
||||
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
package net.lamgc.oracle.sentry.script;
|
||||
|
||||
import net.lamgc.oracle.sentry.oci.compute.ComputeInstanceManager;
|
||||
import net.lamgc.oracle.sentry.oci.account.OracleAccountManager;
|
||||
import net.lamgc.oracle.sentry.script.tools.http.ScriptHttpClient;
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 脚本组件集合.
|
||||
@ -10,11 +10,49 @@ import net.lamgc.oracle.sentry.script.tools.http.ScriptHttpClient;
|
||||
* <p> 后续可能会改成用 {@link javax.script.Bindings} 之类的.
|
||||
* @author LamGC
|
||||
*/
|
||||
public final record ScriptComponents(
|
||||
ScriptHttpClient HTTP,
|
||||
ComputeInstanceManager InstanceManager,
|
||||
ScriptLoggerFactory loggerFactory,
|
||||
OracleAccountManager AccountManager
|
||||
) {
|
||||
public final class ScriptComponents {
|
||||
|
||||
private final ScriptComponents parent;
|
||||
private final Map<String, Object> componentObjects = new HashMap<>();
|
||||
private final Set<ScriptComponentFactory<?>> factories = new HashSet<>();
|
||||
|
||||
public ScriptComponents() {
|
||||
this.parent = null;
|
||||
}
|
||||
|
||||
public ScriptComponents(ScriptComponents parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public void addComponentObject(String componentName, Object componentObject) {
|
||||
if (Strings.isNullOrEmpty(componentName)) {
|
||||
throw new NullPointerException("The component name is null or empty.");
|
||||
} else if (componentObject == null) {
|
||||
throw new NullPointerException("ComponentObject is null");
|
||||
} else if (componentObjects.containsKey(componentName)) {
|
||||
throw new IllegalArgumentException("The corresponding object already exists for the component name.");
|
||||
}
|
||||
|
||||
componentObjects.put(componentName, componentObject);
|
||||
}
|
||||
|
||||
public void addComponentFactory(ScriptComponentFactory<?> factory) {
|
||||
Objects.requireNonNull(factory);
|
||||
factories.add(factory);
|
||||
}
|
||||
|
||||
public Map<String, Object> getComponentObjects() {
|
||||
Map<String, Object> componentObjects = this.parent == null ?
|
||||
new HashMap<>() : new HashMap<>(this.parent.getComponentObjects());
|
||||
componentObjects.putAll(this.componentObjects);
|
||||
return Collections.unmodifiableMap(componentObjects);
|
||||
}
|
||||
|
||||
public Set<ScriptComponentFactory<?>> getScriptComponentFactories() {
|
||||
Set<ScriptComponentFactory<?>> factories = this.parent == null ?
|
||||
new HashSet<>() : new HashSet<>(this.parent.factories);
|
||||
factories.addAll(this.factories);
|
||||
return Collections.unmodifiableSet(factories);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ import org.slf4j.LoggerFactory;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
@ -96,19 +95,15 @@ public class GroovyScriptLoader implements ScriptLoader {
|
||||
|
||||
private static Binding createBinding(ScriptComponents components, ScriptInfo info) {
|
||||
Binding binding = new Binding();
|
||||
for (Field field : components.getClass().getDeclaredFields()) {
|
||||
try {
|
||||
String name = field.getName();
|
||||
field.setAccessible(true);
|
||||
Object o = field.get(components);
|
||||
if (o instanceof ScriptComponentFactory factory) {
|
||||
binding.setProperty(factory.getPropertyName(), factory.getInstance(info));
|
||||
} else {
|
||||
binding.setProperty(name, o);
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
components.getComponentObjects().forEach(binding::setProperty);
|
||||
for (ScriptComponentFactory<?> factory : components.getScriptComponentFactories()) {
|
||||
String componentName = factory.getPropertyName();
|
||||
if (binding.hasVariable(componentName)) {
|
||||
log.warn("脚本组件名发生冲突: 工厂 {} 所给定的组件名已经存在静态组件, 将跳过创建.", factory.getClass());
|
||||
continue;
|
||||
}
|
||||
Object componentObject = factory.getInstance(info);
|
||||
binding.setProperty(componentName, componentObject);
|
||||
}
|
||||
return binding;
|
||||
}
|
||||
|
@ -13,8 +13,7 @@ class ScriptManagerTest {
|
||||
@Test
|
||||
public void loadScriptTest() {
|
||||
ScriptManager manager = new ScriptManager(new File("./run/scripts"),
|
||||
new ScriptComponents(new ScriptHttpClient(HttpClientBuilder.create().build()),
|
||||
new ComputeInstanceManager(), new ScriptLoggerFactory(), new OracleAccountManager()));
|
||||
new ScriptComponents());
|
||||
|
||||
manager.loadScripts();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user