mirror of
https://github.com/LamGC/Oracle-Sentry.git
synced 2025-04-29 22:27: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.OracleAccount;
|
||||||
import net.lamgc.oracle.sentry.oci.account.OracleAccountManager;
|
import net.lamgc.oracle.sentry.oci.account.OracleAccountManager;
|
||||||
import net.lamgc.oracle.sentry.oci.compute.ComputeInstanceManager;
|
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.ScriptComponents;
|
||||||
import net.lamgc.oracle.sentry.script.ScriptLoggerFactory;
|
|
||||||
import net.lamgc.oracle.sentry.script.ScriptManager;
|
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.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -24,6 +22,7 @@ import javax.annotation.PostConstruct;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.util.ServiceLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author LamGC
|
* @author LamGC
|
||||||
@ -96,18 +95,29 @@ class ApplicationInitiation {
|
|||||||
@Bean("sentry.script.manager")
|
@Bean("sentry.script.manager")
|
||||||
@Autowired
|
@Autowired
|
||||||
public ScriptManager initialScriptManager(ComputeInstanceManager instanceManager, OracleAccountManager accountManager) {
|
public ScriptManager initialScriptManager(ComputeInstanceManager instanceManager, OracleAccountManager accountManager) {
|
||||||
ScriptComponents context = new ScriptComponents(new ScriptHttpClient(HttpClientBuilder.create()
|
ScriptComponents components = new ScriptComponents();
|
||||||
.build()),
|
components.addComponentObject("InstanceManager", instanceManager);
|
||||||
instanceManager,
|
components.addComponentObject("AccountManager", accountManager);
|
||||||
new ScriptLoggerFactory(),
|
|
||||||
accountManager
|
|
||||||
);
|
|
||||||
|
|
||||||
ScriptManager manager = new ScriptManager(new File(scriptsLocation), context);
|
configureScriptComponentsFromExtension(components);
|
||||||
|
|
||||||
|
ScriptManager manager = new ScriptManager(new File(scriptsLocation), components);
|
||||||
manager.loadScripts();
|
manager.loadScripts();
|
||||||
return manager;
|
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
|
@PostConstruct
|
||||||
@Order(1)
|
@Order(1)
|
||||||
@SuppressWarnings({"MismatchedReadAndWriteOfArray", "RedundantOperationOnEmptyContainer"})
|
@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;
|
package net.lamgc.oracle.sentry.script;
|
||||||
|
|
||||||
import net.lamgc.oracle.sentry.oci.compute.ComputeInstanceManager;
|
import com.google.common.base.Strings;
|
||||||
import net.lamgc.oracle.sentry.oci.account.OracleAccountManager;
|
|
||||||
import net.lamgc.oracle.sentry.script.tools.http.ScriptHttpClient;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 脚本组件集合.
|
* 脚本组件集合.
|
||||||
@ -10,11 +10,49 @@ import net.lamgc.oracle.sentry.script.tools.http.ScriptHttpClient;
|
|||||||
* <p> 后续可能会改成用 {@link javax.script.Bindings} 之类的.
|
* <p> 后续可能会改成用 {@link javax.script.Bindings} 之类的.
|
||||||
* @author LamGC
|
* @author LamGC
|
||||||
*/
|
*/
|
||||||
public final record ScriptComponents(
|
public final class ScriptComponents {
|
||||||
ScriptHttpClient HTTP,
|
|
||||||
ComputeInstanceManager InstanceManager,
|
private final ScriptComponents parent;
|
||||||
ScriptLoggerFactory loggerFactory,
|
private final Map<String, Object> componentObjects = new HashMap<>();
|
||||||
OracleAccountManager AccountManager
|
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.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -96,19 +95,15 @@ public class GroovyScriptLoader implements ScriptLoader {
|
|||||||
|
|
||||||
private static Binding createBinding(ScriptComponents components, ScriptInfo info) {
|
private static Binding createBinding(ScriptComponents components, ScriptInfo info) {
|
||||||
Binding binding = new Binding();
|
Binding binding = new Binding();
|
||||||
for (Field field : components.getClass().getDeclaredFields()) {
|
components.getComponentObjects().forEach(binding::setProperty);
|
||||||
try {
|
for (ScriptComponentFactory<?> factory : components.getScriptComponentFactories()) {
|
||||||
String name = field.getName();
|
String componentName = factory.getPropertyName();
|
||||||
field.setAccessible(true);
|
if (binding.hasVariable(componentName)) {
|
||||||
Object o = field.get(components);
|
log.warn("脚本组件名发生冲突: 工厂 {} 所给定的组件名已经存在静态组件, 将跳过创建.", factory.getClass());
|
||||||
if (o instanceof ScriptComponentFactory factory) {
|
continue;
|
||||||
binding.setProperty(factory.getPropertyName(), factory.getInstance(info));
|
|
||||||
} else {
|
|
||||||
binding.setProperty(name, o);
|
|
||||||
}
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
Object componentObject = factory.getInstance(info);
|
||||||
|
binding.setProperty(componentName, componentObject);
|
||||||
}
|
}
|
||||||
return binding;
|
return binding;
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,7 @@ class ScriptManagerTest {
|
|||||||
@Test
|
@Test
|
||||||
public void loadScriptTest() {
|
public void loadScriptTest() {
|
||||||
ScriptManager manager = new ScriptManager(new File("./run/scripts"),
|
ScriptManager manager = new ScriptManager(new File("./run/scripts"),
|
||||||
new ScriptComponents(new ScriptHttpClient(HttpClientBuilder.create().build()),
|
new ScriptComponents());
|
||||||
new ComputeInstanceManager(), new ScriptLoggerFactory(), new OracleAccountManager()));
|
|
||||||
|
|
||||||
manager.loadScripts();
|
manager.loadScripts();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user