From e6ff28e0771f0b65c308a9786f3cacf9521319b1 Mon Sep 17 00:00:00 2001 From: LamGC Date: Sun, 10 Oct 2021 11:12:49 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=89=A9=E5=B1=95?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3,=20=E9=80=9A=E8=BF=87=20SPI=20=E6=9C=BA?= =?UTF-8?q?=E5=88=B6=E5=85=81=E8=AE=B8=E5=A4=96=E9=83=A8=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E4=B8=BA=E8=84=9A=E6=9C=AC=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E7=BB=84=E4=BB=B6.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 通过扩展接口, 可让第三方为脚本提供功能组件, 而无需局限于 Sentry 内置功能组件. Closes #6 --- .../oracle/sentry/ApplicationInitiation.java | 30 ++++++---- .../script/BuiltinComponentExtension.java | 13 +++++ .../script/ScriptComponentExtension.java | 18 ++++++ .../sentry/script/ScriptComponents.java | 56 ++++++++++++++++--- .../script/groovy/GroovyScriptLoader.java | 21 +++---- .../sentry/script/ScriptManagerTest.java | 3 +- 6 files changed, 107 insertions(+), 34 deletions(-) create mode 100644 src/main/java/net/lamgc/oracle/sentry/script/BuiltinComponentExtension.java create mode 100644 src/main/java/net/lamgc/oracle/sentry/script/ScriptComponentExtension.java diff --git a/src/main/java/net/lamgc/oracle/sentry/ApplicationInitiation.java b/src/main/java/net/lamgc/oracle/sentry/ApplicationInitiation.java index ef55e93..6e37258 100644 --- a/src/main/java/net/lamgc/oracle/sentry/ApplicationInitiation.java +++ b/src/main/java/net/lamgc/oracle/sentry/ApplicationInitiation.java @@ -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 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"}) diff --git a/src/main/java/net/lamgc/oracle/sentry/script/BuiltinComponentExtension.java b/src/main/java/net/lamgc/oracle/sentry/script/BuiltinComponentExtension.java new file mode 100644 index 0000000..97a9bef --- /dev/null +++ b/src/main/java/net/lamgc/oracle/sentry/script/BuiltinComponentExtension.java @@ -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()); + } +} diff --git a/src/main/java/net/lamgc/oracle/sentry/script/ScriptComponentExtension.java b/src/main/java/net/lamgc/oracle/sentry/script/ScriptComponentExtension.java new file mode 100644 index 0000000..56a54ca --- /dev/null +++ b/src/main/java/net/lamgc/oracle/sentry/script/ScriptComponentExtension.java @@ -0,0 +1,18 @@ +package net.lamgc.oracle.sentry.script; + +/** + * 脚本组件扩展. + *

通过实现该接口, 可在脚本加载前设置组件对象, 为脚本提供更多功能. + *

实现接口后, 需按 SPI 方式添加实现. + * @author LamGC + */ +public interface ScriptComponentExtension { + + /** + * 配置脚本组件. + *

在方法中为组件集合添加组件. + * @param components 脚本组件集合. + */ + void configureScriptComponents(final ScriptComponents components); + +} diff --git a/src/main/java/net/lamgc/oracle/sentry/script/ScriptComponents.java b/src/main/java/net/lamgc/oracle/sentry/script/ScriptComponents.java index e313a2b..e2deff3 100644 --- a/src/main/java/net/lamgc/oracle/sentry/script/ScriptComponents.java +++ b/src/main/java/net/lamgc/oracle/sentry/script/ScriptComponents.java @@ -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; *

后续可能会改成用 {@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 componentObjects = new HashMap<>(); + private final Set> 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 getComponentObjects() { + Map componentObjects = this.parent == null ? + new HashMap<>() : new HashMap<>(this.parent.getComponentObjects()); + componentObjects.putAll(this.componentObjects); + return Collections.unmodifiableMap(componentObjects); + } + + public Set> getScriptComponentFactories() { + Set> factories = this.parent == null ? + new HashSet<>() : new HashSet<>(this.parent.factories); + factories.addAll(this.factories); + return Collections.unmodifiableSet(factories); + } } diff --git a/src/main/java/net/lamgc/oracle/sentry/script/groovy/GroovyScriptLoader.java b/src/main/java/net/lamgc/oracle/sentry/script/groovy/GroovyScriptLoader.java index 26ee52b..6852d1b 100644 --- a/src/main/java/net/lamgc/oracle/sentry/script/groovy/GroovyScriptLoader.java +++ b/src/main/java/net/lamgc/oracle/sentry/script/groovy/GroovyScriptLoader.java @@ -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; } diff --git a/src/test/java/net/lamgc/oracle/sentry/script/ScriptManagerTest.java b/src/test/java/net/lamgc/oracle/sentry/script/ScriptManagerTest.java index a419dad..38f35f7 100644 --- a/src/test/java/net/lamgc/oracle/sentry/script/ScriptManagerTest.java +++ b/src/test/java/net/lamgc/oracle/sentry/script/ScriptManagerTest.java @@ -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();