diff --git a/.gitignore b/.gitignore
index 00e7e9cbcc16998f6c300dc49d7f01d739878be0..dae69c503907873b2d326f420c2fa747f038f219 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,3 +26,6 @@ dist
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
+
+# custom
+springboot-plugin-framework-parent.zip
diff --git a/README.md b/README.md
index 7c01cb34c67448aace978a6e6ea9d98655efa186..7a0ac8123277d98aaa5b7969ae57e9c8e0492fb0 100644
--- a/README.md
+++ b/README.md
@@ -29,840 +29,10 @@
[https://mvnrepository.com/artifact/com.gitee.starblues/springboot-plugin-framework](https://mvnrepository.com/artifact/com.gitee.starblues/springboot-plugin-framework)
-### 快速入门
+### 文档地址
-#### 新建项目。
-Maven目录结构下所示
-```
--example
- - example-runner
- - pom.xml
- - example-main
- - pom.xml
- - example-plugin-parent
- - pom.xml
- - plugins
- - example-plugin1
- - pom.xml
- - plugin.properties
- - example-plugin2
- - pom.xml
- - plugin.properties
- - pom.xml
- - pom.xml
-```
+[https://gitee.com/starblues/springboot-plugin-framework-parent/wikis/pages](https://gitee.com/starblues/springboot-plugin-framework-parent/wikis/pages)
-结构说明:
-
-1. pom.xml 代表maven的pom.xml
-2. plugin.properties 为开发环境下, 插件的元信息配置文件, 配置内容详见下文。
-3. example 为项目的总Maven目录。
-4. example-runner 在运行环境下启动的模块。主要依赖example-main模块和插件中使用到的依赖包, 并且解决开发环境下无法找到插件依赖包的问题。
-5. example-main 该模块为项目的主程序模块。
-6. example-plugin-parent 该模块为插件的父级maven pom 模块, 主要定义插件中公共用到的依赖, 以及插件的打包配置。
-7. plugins 该文件夹下主要存储插件模块。上述模块中主要包括example-plugin1、example-plugin2 两个插件。
-8. example-plugin1、example-plugin2 分别为两个插件Maven包。
-
-#### 主程序集成步骤
-
-主程序为上述目录结构中的 example-main 模块。
-
-1. 在主程序中新增maven依赖包
-
-```xml
-
- com.gitee.starblues
- springboot-plugin-framework
- ${springboot-plugin-framework.version}
-
-
-最新版本:
-
- com.gitee.starblues
- springboot-plugin-framework
- 2.1.3-RELEASE
-
-
-```
-
-2. 实现并定义配置
-
-实现 **com.plugin.development.integration.IntegrationConfiguration** 接口。
-
-```java
-import com.gitee.starblues.integration.DefaultIntegrationConfiguration;
-import org.pf4j.RuntimeMode;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.stereotype.Component;
-
-@Component
-@ConfigurationProperties(prefix = "plugin")
-public class PluginConfiguration extends DefaultIntegrationConfiguration {
-
- /**
- * 运行模式
- * 开发环境: development、dev
- * 生产/部署 环境: deployment、prod
- */
- @Value("${runMode:dev}")
- private String runMode;
-
- /**
- * 插件的路径
- */
- @Value("${pluginPath:plugins}")
- private String pluginPath;
-
- /**
- * 插件文件的路径
- */
- @Value("${pluginConfigFilePath:pluginConfigs}")
- private String pluginConfigFilePath;
-
-
- @Override
- public RuntimeMode environment() {
- return RuntimeMode.byName(runMode);
- }
-
- @Override
- public String pluginPath() {
- return pluginPath;
- }
-
- @Override
- public String pluginConfigFilePath() {
- return pluginConfigFilePath;
- }
-
- /**
- * 重写上传插件包的临时存储路径。只适用于生产环境
- * @return String
- */
- @Override
- public String uploadTempPath() {
- return "temp";
- }
-
- /**
- * 重写插件备份路径。只适用于生产环境
- * @return String
- */
- @Override
- public String backupPath() {
- return "backupPlugin";
- }
-
- /**
- * 重写插件RestController请求的路径前缀
- * @return String
- */
- @Override
- public String pluginRestControllerPathPrefix() {
- return "/api/plugins";
- }
-
- /**
- * 重写是否启用插件id作为RestController请求的路径前缀。
- * 启动则插件id会作为二级路径前缀。即: /api/plugins/pluginId/**
- * @return String
- */
- @Override
- public boolean enablePluginIdRestControllerPathPrefix() {
- return true;
- }
-
- public String getRunMode() {
- return runMode;
- }
-
- public void setRunMode(String runMode) {
- this.runMode = runMode;
- }
-
-
- public String getPluginPath() {
- return pluginPath;
- }
-
- public void setPluginPath(String pluginPath) {
- this.pluginPath = pluginPath;
- }
-
- public String getPluginConfigFilePath() {
- return pluginConfigFilePath;
- }
-
- public void setPluginConfigFilePath(String pluginConfigFilePath) {
- this.pluginConfigFilePath = pluginConfigFilePath;
- }
-
- @Override
- public String toString() {
- return "PluginArgConfiguration{" +
- "runMode='" + runMode + '\'' +
- ", pluginPath='" + pluginPath + '\'' +
- ", pluginConfigFilePath='" + pluginConfigFilePath + '\'' +
- '}';
- }
-}
-```
-
-配置说明:
-
-**runMode**:运行项目时的模式。分为开发环境(dev)、生产环境(prod)
-
-**pluginPath**: 插件的路径。开发环境建议直接配置为插件模块的父级目录。例如: plugins。如果启动主程序时, 插件为加载, 请检查该配置是否正确。
-
-**pluginConfigFilePath**: 在生产环境下, 插件的配置文件路径。在生产环境下, 请将所有插件使用到的配置文件统一放到该路径下管理。如果启动主程序时, 报插件的配置文件加载错误, 有可能是该该配置不合适导致的。
-
-**uploadTempPath**: 上传插件包时使用。上传插件包存储的临时路径。默认 temp(相对于主程序jar路径)
-
-**backupPath**: 备份插件包时使用。备份插件包的路径。默认: backupPlugin(相对于主程序jar路径)
-
-**pluginRestControllerPathPrefix**: 插件RestController请求的路径前缀
-
-**enablePluginIdRestControllerPathPrefix**: 是否启用插件id作为RestController请求的路径前缀。启动则插件id会作为二级路径前缀。即: /api/plugins/pluginId/**
-
-
-3. 配置bean
-
-```
-import com.gitee.starblues.integration.*;
-import com.gitee.starblues.integration.initialize.AutoPluginInitializer;
-import com.gitee.starblues.integration.initialize.PluginInitializer;
-import org.pf4j.PluginException;
-import org.pf4j.PluginManager;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-public class PluginBeanConfig {
-
- /**
- * 通过默认的集成工厂返回 PluginManager
- * @param integrationConfiguration 集成的配置文件
- * @return
- * @throws PluginException
- */
- @Bean
- public PluginManager pluginManager(IntegrationConfiguration integrationConfiguration) throws PluginException {
- IntegrationFactory integrationFactory = new DefaultIntegrationFactory();
- return integrationFactory.getPluginManager(integrationConfiguration);
- }
-
- /**
- * 定义默认的插件应用。使用可以注入它操作插件。
- * @return
- */
- @Bean
- public PluginApplication pluginApplication(){
- return new DefaultPluginApplication();
- }
-
- /**
- * 初始化插件。此处定义可以在系统启动时自动加载插件。
- * 如果想手动加载插件, 则可以使用 com.plugin.development.integration.initialize.ManualPluginInitializer 来初始化插件。
- * @param pluginApplication
- * @return
- */
- @Bean
- public PluginInitializer pluginInitializer(PluginApplication pluginApplication){
- AutoPluginInitializer autoPluginInitializer = new AutoPluginInitializer(pluginApplication);
- return autoPluginInitializer;
- }
-
-}
-
-```
-
-#### 插件包集成步骤
-
-1. 插件包pom.xml配置说明
-
-
-以 `provided` 方式引入springboot-plugin-framework包
-
-```xml
-
- com.gitee.starblues
- springboot-plugin-framework
- ${springboot-plugin-framework.version}
- provided
-
-```
-
-定义打包配置.主要用途是将 `Plugin-Id、Plugin-Version、Plugin-Provider、Plugin-Class、Plugin-Dependencies`的配置值定义到`META-INF\MANIFEST.MF`文件中
-```xml
-
- example-plugin1
- com.plugin.example.plugin1.DefinePlugin
- ${project.version}
- StarBlues
-
-
- 1.8
- UTF-8
- UTF-8
-
- 3.7.0
- 3.1.1
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- ${maven-compiler-plugin.version}
-
- ${java.version}
- ${java.version}
- ${project.build.sourceEncoding}
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
- ${maven-assembly-plugin.version}
-
-
- jar-with-dependencies
-
-
-
- true
- true
-
-
- ${plugin.id}
- ${plugin.version}
- ${plugin.provider}
- ${plugin.class}
-
-
-
-
-
- make-assembly
- package
-
- single
-
-
-
-
-
-
-```
-
-2. 在插件包的一级目录下新建plugin.properties文件(用于开发环境)
-新增如下内容(属性值同步骤1中pom.xml定义的`manifestEntries`属性一致):
-```
-plugin.id=example-plugin1
-plugin.class=com.plugin.example.plugin1.DefinePlugin
-plugin.version=2.0-SNAPSHOT
-plugin.provider=StarBlues
-```
-
-配置说明:
-```
-plugin.id: 插件id
-plugin.class: 插件实现类。见步骤3说明
-plugin.version: 插件版本
-plugin.provider: 插件作者
-```
-
-3. 继承 `com.gitee.starblues.realize.BasePlugin` 包
-``` java
-import com.gitee.starblues.realize.BasePlugin;
-import org.pf4j.PluginException;
-import org.pf4j.PluginWrapper;
-
-public class DefinePlugin extends BasePlugin {
- public DefinePlugin(PluginWrapper wrapper) {
- super(wrapper);
- }
-
- @Override
- protected void startEvent() throws PluginException {
-
- }
-
- @Override
- protected void deleteEvent() throws PluginException {
-
- }
-
- @Override
- protected void stopEvent() {
-
- }
-}
-```
-
-并且将该类的包路径(com.plugin.example.plugin1.DefinePlugin)配置在步骤1和2的plugin.class属性中。
-
-4. 新增HelloPlugin1 controller
-
-此步骤主要验证环境是否加载插件成功。
-
-```java
-
-@RestController
-@RequestMapping(path = "plugin1")
-public class HelloPlugin1 {
-
- @GetMapping()
- public String getConfig(){
- return "hello plugin1 example";
- }
-
-}
-
-```
-
-#### 运行配置
-1. 配置模块 example-runner 的pom.xml
-
-- 将主程序的依赖新增到pom.xml 下
-- 将插件中的依赖以 `provided` 方式引入到 pom.xml 下
-
-如下所示:
-
-``` xml
-
-
-
- 4.0.0
-
-
- org.springframework.boot
- spring-boot-starter-parent
- 2.0.3.RELEASE
-
-
-
- com.gitee.starblues
- plugin-example-runner
- 2.0-RELEASE
- pom
-
-
- 2.8.2
-
-
-
-
- com.gitee.starblues
- plugin-example-start
- ${project.version}
-
-
-
-
-
-
-
-
-```
-
-2. 设置idea的启动
-
-Working directory : D:\xx\xx\plugin-example
-
-Use classpath of module: plugin-exampe-runner
-
-勾选: Include dependencies with "Provided" scope
-
-3. 启动2步骤的配置。
-
-观察日志出现如下说明加载插件成功。
-
-``` java
- Plugin 'example-plugin1@2.0-RELEASE' resolved
- Start plugin 'example-plugin1@2.0-RELEASE'
- Init Plugins Success
-```
-
-4. 访问插件中的Controller 验证。
-
-浏览器输入:http://ip:port/api/plugins/example-plugin1/plugin1
-
-响应并显示: hello plugin1 example
-
-说明集成成功!
-
-### 使用说明
-
-#### 插件中定义配置文件
-
-1. 在插件包的 resources 目录下定义配置文件 plugin1.yml
-
-```yml
-name: plugin1
-plugin: examplePlugin1
-setString:
- - set1
- - set2
-listInteger:
- - 1
- - 2
- - 3
-subConfig:
- subName: subConfigName
-```
-
-2. 在代码中定义对应的bean
-
-```java
-import com.gitee.starblues.annotation.ConfigDefinition;
-import java.util.List;
-import java.util.Set;
-
-@ConfigDefinition("plugin1.yml")
-public class PluginConfig1 {
-
- private String name;
- private String plugin;
- private Set setString;
- private List listInteger;
- private String defaultValue = "defaultValue";
- private SubConfig subConfig;
-
- // 自行提供get set 方法
-
-}
-
-
-public class SubConfig {
-
- private String subName;
- public String getSubName() {
- return subName;
- }
-
- // 自行提供get set 方法
-}
-```
-
-该bean必须加上 @ConfigDefinition("plugin1.yml") 注解。其中值为插件文件的名称。
-
-3. 其他地方使用时, 可以通过注入方式使用。
-
-例如:
-``` java
-@Component("plugin2HelloService")
-public class HelloService {
-
- private final PluginConfig1 pluginConfig1;
- private final Service2 service2;
-
- @Autowired
- public HelloService(PluginConfig1 pluginConfig1, Service2 service2) {
- this.pluginConfig1 = pluginConfig1;
- this.service2 = service2;
- }
-
- public PluginConfig1 getPluginConfig1(){
- return pluginConfig1;
- }
-
-
- public String sayService2(){
- return service2.getName();
- }
-
-}
-```
-
-4. 注意事项
-
-*在开发环境:配置文件必须放在resources目录下。并且@ConfigDefinition("plugin1.yml")中定义的文件名和resources下配置的文件名一致。*
-
-*在生产环境: 该文件存放在`pluginConfigFilePath`配置的目录下。生产环境下插件的配置文件必须外置, 不能使用jar包里面的配置文件 *
-
-#### 插件之间数据交互功能
-
-插件之间的数据交互功能, 是在同一JVM运行环境下, 基于代理、反射机制完成方法调用。使用说明如下:
-
-1. 被调用类需要使用注解 @Supplier(""), 注解值为被调用者的唯一key, (全局key不能重复) 供调用者使用。例如:
-```java
-@Supplier("SupplierService")
-public class SupplierService {
-
- public Integer add(Integer a1, Integer a2){
- return a1 + a2;
- }
-
-}
-```
-2. 另一个插件中要调用1步骤中定义的调用类时, 需要定义一个接口,新增注解@Caller(""), 值为1步骤中被调用者定义的全局key。其中方法名、参数个数和类型、返回类型需要和被调用者中定义的方法名、参数个数和类型一致。例如:
-```java
-@Caller("SupplierService")
-public interface CallerService {
-
- Integer add(Integer a1, Integer a2);
-
-}
-```
-
-3.被调用者和调用者也可以使用注解定义被调用的方法。例如:
-
-被调用者:
-
-```java
-@Supplier("SupplierService")
-public class SupplierService {
-
- @Supplier.Method("call")
- public String call(CallerInfo callerInfo, String key){
- System.out.println(callerInfo);
- return key;
- }
-
-}
-```
-
-调用者:
-
-```java
-@Caller("SupplierService")
-public interface CallerService {
-
- @Caller.Method("call")
- String test(CallerInfo callerInfo, String key);
-
-}
-```
-
-该场景主要用于参数类型不在同一个地方定义时使用。比如 被调用者的参数类: CallerInfo 定义在被调用者的插件中, 调用者的参数类: CallerInfo 定义在调用者的插件中。就必须配合 @Supplier.Method("")、@Caller.Method("") 注解使用, 否则会导致NotFoundClass 异常。
-
-如果调用者没有使用注解 @Caller.Method("") 则默认使用方法和参数类型来调用。
-
-4.对于3步骤中问题的建议
-
-可以将被调用者和调用者的公用参数和返回值定义在主程序中、或者单独提出一个api maven包, 然后两者都依赖该包。
-
-
-5.案例位置
-
-basic-example:
-
-com.basic.example.plugin1.service.SupplierService
-com.basic.example.plugin2.service.CallerService
-com.basic.example.plugin2.rest.ProxyController
-
-
-
-### 集成扩展
-
-1. SpringBoot Mybatis 扩展
-
-文档见: [springboot-plugin-framework-extension-mybatis](https://gitee.com/starblues/springboot-plugin-framework-parent/tree/master/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis)
-
-
-### 案例说明
-
-basic-example:插件基础功能案例。
-
-integration-mybatis: 针对Mybatis集成的案例。
-
-integration-mybatisplus: 针对Mybatis-Plus集成的案例
-
-
-#### 基础功能案例演示
-
-- 普通例子运行见:basic-example
-
-- windows环境下运行: package.bat
-
-- linux、mac 环境下运行: package.sh
-
-- 接口地址查看: http://127.0.0.1:8080/doc.html
-
-#### mybatis 案例演示
-
-- 例子见:integration-mybatis
-
-- windows环境下运行: package.bat
-
-- linux、mac 环境下运行: package.sh
-
-- sql在 integration-mybatis/sql 文件夹下。
-
-- 接口地址查看: http://127.0.0.1:8081/doc.html
-
-#### mybatis-plus 案例演示
-
-- 例子见:integration-mybatisplus
-
-- windows环境下运行: package.bat
-
-- linux、mac 环境下运行: package.sh
-
-- sql在 integration-mybatisplus/sql 文件夹下。
-
-- 接口地址查看: http://127.0.0.1:8082/doc.html
-
-#### 案例常见报错
-
-1. 插件未编译
-java.lang.ClassNotFoundException: com.basic.example.plugin1.DefinePlugin
-java.lang.ClassNotFoundException: com.basic.example.plugin2.DefinePlugin
-该类型报错是由于插件源码没有编译成 class 文件; 需要手动编译, 保证在插件目录出现 target 文件
-
-### 生产环境目录
-
-```text
--main.jar
-
--main.yml
-
--plugins
- -plugin1.jar
- -plugin2.jar
-
--pluginFile
- -plugin1.yml
- -plugin2.yml
-
-```
-
-
-### 生产环境配置禁用启用功能
-
-#### 启用功能
-
-1.在插件目录下新建 `enabled.txt` 文件
-2.enabled.txt的内容为:
-
-```text
-########################################
-# - 启用的插件
-########################################
-example-plugin1
-```
-将需要启用的插件id配置到文件中。
-
-所有注释行(以#字符开头的行)都将被忽略。
-
-#### 启用、禁用功能
-
-1.在插件目录下新建 `disabled.txt` 文件
-2.disabled.txt的内容为:
-
-```text
-########################################
-# - 禁用的插件
-########################################
-example-plugin1
-```
-将需要启用的插件id配置到文件中。
-
-所有注释行(以#字符开头的行)都将被忽略。
-
-
-### 注意事项
-
-1. 插件中代码编写完后, 请保证在class文件下的类都是最新编译的, 再运行主程序, 否则会导致运行的插件代码不是最新的。
-2. 如果启动时插件没有加载。请检查配置文件中的 pluginPath
-
-```text
-如果 pluginPath 配置为相当路径,请检查是否是相对于当前工作环境的目录。
-
-如果 pluginPath 配置为绝对路径,请检查路径是否正确。
-```
-
-3. 如果出现Spring包冲突。可以排除Spring包。
-例如:
-```xml
-
-com.gitee.starblues
-springboot-plugin-framework
-${springboot-plugin-framework.version}
-
-
- org.springframework
- spring-context
-
-
- org.springframework
- spring-webmvc
-
-
-
-```
-4. 以下功能只适用于生产环境下。
-- 插件的上传。
-- 插件的动态更新(上传并安装插件)。
-- 插件的备份。
-- 插件的配置文件上传。
-- 删除插件
-
-
-### 小技巧
-1. idea 启动主程序时, 自动编译插件包的配置
-选择
-File->Project Structure->Project Settings->Artifacts->点击+号->JAR->From modules whith dependencies->选择对应的插件包->确认OK
-
-启动配置:
-在Before launch 下-> 点击小+号 -> Build ->Artifacts -> 选择上一步新增的>Artifacts
### QQ交流群
-859570617
-
-### 版本更新
-
-#### 2.1.3 版本
-在PluginUser接口新增getMainBeans方法, 用于获取Spring管理的主程序接口的实现类。
-
-#### 2.1.2 版本
-1. 修复使用多AOP情况, 无法加载插件类(被AOP代理的类)的bug。
-2. 新增可以通过插件id获取插件中的bean的实现。详见:PluginUser->getPluginBeans(String pluginId, Class aClass)
-3. 新增插件注册监听器可通过Class方式添加。案例详见: basic-eaxmple->com.basic.example.main.config.ExamplePluginListener
-
-#### 2.1.1 版本
-1. 插件中支持事务注解。
-2. 修复重复启动插件时报错的bug。
-
-#### 2.1.0 版本
-1. 修复mybatis案例无法加载mapper.xml的bug。
-2. 优化代码逻辑。
-3. 新增插件间的通信。详见文档-使用说明->插件之间数据交互功能
-
-#### 2.0.3 版本
-1. 修复插件动态重新安装后, 无法访问到插件中的接口的bug。
-
-#### 2.0.2 版本
-1. 新增 com.gitee.starblues.integration.user.PluginUser
-
-使用场景: 在主程序中定义了接口, 插件中存在实现了该接口的实现类, 通过PluginUser 的 getPluginBeans(接口Class) 可以获取所有插件中实现该接口的实现类。具体详见源码。
-
-2. 新增插件bean刷新抽象类。继承它可动态获取接口实现类集合。
-
-#### 2.0.1 版本
-1. 修复插件的Controller无法定义一级请求路径的bug。
-
-#### 2.0 版本(重大版本更新)
-1. 重构代码。
-2. 新增扩展机制。
-3. 简化依赖注入注解, 保持与SpringBoot依赖注入方式一致。
-4. 新增插件工厂监听器、新增插件初始化监听器(适用于第一次启动)。
-5. 新增插件包Mybatis的集成, 可在插件包中独立定义Mapper接口、Mapper xml、实体bean。
-
-#### 1.1 版本
-1. 新增插件注册、卸载监听器。
-2. 新增可通过 PluginUser 获取插件中实现主程序中定义的接口的实现类。
-3. 新增插件注册、卸载时监听器。
\ No newline at end of file
+859570617
\ No newline at end of file
diff --git a/example/basic-example/basic-example-main/src/main/java/com/basic/example/main/config/PluginConfiguration.java b/example/basic-example/basic-example-main/src/main/java/com/basic/example/main/config/PluginConfiguration.java
index 466c9b0422fa3393c93d8bbeee1e322eb7d77690..1052f283df2bc349d16bcff7bb1aae75eb5875fc 100644
--- a/example/basic-example/basic-example-main/src/main/java/com/basic/example/main/config/PluginConfiguration.java
+++ b/example/basic-example/basic-example-main/src/main/java/com/basic/example/main/config/PluginConfiguration.java
@@ -90,6 +90,10 @@ public class PluginConfiguration extends DefaultIntegrationConfiguration {
return true;
}
+
+
+
+
public String getRunMode() {
return runMode;
}
diff --git a/example/basic-example/basic-example-main/src/main/java/com/basic/example/main/rest/PluginResource.java b/example/basic-example/basic-example-main/src/main/java/com/basic/example/main/rest/PluginResource.java
index 5491bd0ceb3b6ddd28e34d3d336951a5e85bfe97..e26cf8714afac90743ce904c5d0b5378ca8f004b 100644
--- a/example/basic-example/basic-example-main/src/main/java/com/basic/example/main/rest/PluginResource.java
+++ b/example/basic-example/basic-example-main/src/main/java/com/basic/example/main/rest/PluginResource.java
@@ -1,6 +1,5 @@
package com.basic.example.main.rest;
-import com.gitee.starblues.exception.PluginPlugException;
import com.gitee.starblues.integration.PluginApplication;
import com.gitee.starblues.integration.operator.PluginOperator;
import com.gitee.starblues.integration.operator.module.PluginInfo;
@@ -46,7 +45,7 @@ public class PluginResource {
public Set getPluginFilePaths(){
try {
return pluginOperator.getPluginFilePaths();
- } catch (PluginPlugException e) {
+ } catch (Exception e) {
e.printStackTrace();
return null;
}
@@ -63,7 +62,7 @@ public class PluginResource {
try {
pluginOperator.stop(id);
return "plugin<" + id +"> stop success";
- } catch (PluginPlugException e) {
+ } catch (Exception e) {
e.printStackTrace();
return "plugin<" + id +"> stop failure : " + e.getMessage();
}
@@ -79,7 +78,7 @@ public class PluginResource {
try {
pluginOperator.start(id);
return "plugin<" + id +"> start success";
- } catch (PluginPlugException e) {
+ } catch (Exception e) {
e.printStackTrace();
return "plugin<" + id +"> start failure : " + e.getMessage();
}
@@ -96,7 +95,7 @@ public class PluginResource {
try {
pluginOperator.uninstall(id);
return "plugin<" + id +"> uninstall success";
- } catch (PluginPlugException e) {
+ } catch (Exception e) {
e.printStackTrace();
return "plugin<" + id +"> uninstall failure : " + e.getMessage();
}
@@ -113,7 +112,7 @@ public class PluginResource {
try {
pluginOperator.install(Paths.get(path));
return "installByPath success";
- } catch (PluginPlugException e) {
+ } catch (Exception e) {
e.printStackTrace();
return "installByPath failure : " + e.getMessage();
}
@@ -130,7 +129,7 @@ public class PluginResource {
try {
pluginOperator.uploadPluginAndStart(multipartFile);
return "install success";
- } catch (PluginPlugException e) {
+ } catch (Exception e) {
e.printStackTrace();
return "install failure : " + e.getMessage();
}
@@ -147,7 +146,7 @@ public class PluginResource {
try {
pluginOperator.uploadConfigFile(multipartFile);
return "uploadConfig success";
- } catch (PluginPlugException e) {
+ } catch (Exception e) {
e.printStackTrace();
return "uploadConfig failure : " + e.getMessage();
}
@@ -164,7 +163,7 @@ public class PluginResource {
try {
pluginOperator.delete(pluginId);
return "deleteById success";
- } catch (PluginPlugException e) {
+ } catch (Exception e) {
e.printStackTrace();
return "deleteById failure : " + e.getMessage();
}
@@ -180,7 +179,7 @@ public class PluginResource {
try {
pluginOperator.delete(Paths.get(pluginJarPath));
return "deleteByPath success";
- } catch (PluginPlugException e) {
+ } catch (Exception e) {
e.printStackTrace();
return "deleteByPath failure : " + e.getMessage();
}
@@ -196,7 +195,7 @@ public class PluginResource {
try {
pluginOperator.backupPlugin(pluginId, "testBack");
return "backupPlugin success";
- } catch (PluginPlugException e) {
+ } catch (Exception e) {
e.printStackTrace();
return "backupPlugin failure : " + e.getMessage();
}
diff --git a/example/integration-mybatis/integration-mybatis-main/src/main/java/com/mybatis/main/rest/PluginResource.java b/example/integration-mybatis/integration-mybatis-main/src/main/java/com/mybatis/main/rest/PluginResource.java
index 0a966c8777616446dcb8327d7a1813709a6aa16b..cdaa874431e7044f7513fe6c14f99bbdeb1b8cc3 100644
--- a/example/integration-mybatis/integration-mybatis-main/src/main/java/com/mybatis/main/rest/PluginResource.java
+++ b/example/integration-mybatis/integration-mybatis-main/src/main/java/com/mybatis/main/rest/PluginResource.java
@@ -1,10 +1,10 @@
package com.mybatis.main.rest;
-import com.gitee.starblues.exception.PluginPlugException;
import com.gitee.starblues.integration.PluginApplication;
import com.gitee.starblues.integration.operator.PluginOperator;
import com.gitee.starblues.integration.operator.module.PluginInfo;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@@ -46,7 +46,7 @@ public class PluginResource {
public Set getPluginFilePaths(){
try {
return pluginOperator.getPluginFilePaths();
- } catch (PluginPlugException e) {
+ } catch (Exception e) {
e.printStackTrace();
return null;
}
@@ -63,7 +63,7 @@ public class PluginResource {
try {
pluginOperator.stop(id);
return "plugin<" + id +"> stop success";
- } catch (PluginPlugException e) {
+ } catch (Exception e) {
e.printStackTrace();
return "plugin<" + id +"> stop failure : " + e.getMessage();
}
@@ -79,7 +79,7 @@ public class PluginResource {
try {
pluginOperator.start(id);
return "plugin<" + id +"> start success";
- } catch (PluginPlugException e) {
+ } catch (Exception e) {
e.printStackTrace();
return "plugin<" + id +"> start failure : " + e.getMessage();
}
@@ -96,7 +96,7 @@ public class PluginResource {
try {
pluginOperator.uninstall(id);
return "plugin<" + id +"> uninstall success";
- } catch (PluginPlugException e) {
+ } catch (Exception e) {
e.printStackTrace();
return "plugin<" + id +"> uninstall failure : " + e.getMessage();
}
@@ -113,7 +113,7 @@ public class PluginResource {
try {
pluginOperator.install(Paths.get(path));
return "installByPath success";
- } catch (PluginPlugException e) {
+ } catch (Exception e) {
e.printStackTrace();
return "installByPath failure : " + e.getMessage();
}
@@ -130,7 +130,7 @@ public class PluginResource {
try {
pluginOperator.uploadPluginAndStart(multipartFile);
return "install success";
- } catch (PluginPlugException e) {
+ } catch (Exception e) {
e.printStackTrace();
return "install failure : " + e.getMessage();
}
@@ -147,7 +147,7 @@ public class PluginResource {
try {
pluginOperator.uploadConfigFile(multipartFile);
return "uploadConfig success";
- } catch (PluginPlugException e) {
+ } catch (Exception e) {
e.printStackTrace();
return "uploadConfig failure : " + e.getMessage();
}
@@ -164,7 +164,7 @@ public class PluginResource {
try {
pluginOperator.delete(pluginId);
return "deleteById success";
- } catch (PluginPlugException e) {
+ } catch (Exception e) {
e.printStackTrace();
return "deleteById failure : " + e.getMessage();
}
@@ -180,7 +180,7 @@ public class PluginResource {
try {
pluginOperator.delete(Paths.get(pluginJarPath));
return "deleteByPath success";
- } catch (PluginPlugException e) {
+ } catch (Exception e) {
e.printStackTrace();
return "deleteByPath failure : " + e.getMessage();
}
@@ -196,7 +196,7 @@ public class PluginResource {
try {
pluginOperator.backupPlugin(pluginId, "testBack");
return "backupPlugin success";
- } catch (PluginPlugException e) {
+ } catch (Exception e) {
e.printStackTrace();
return "backupPlugin failure : " + e.getMessage();
}
diff --git a/example/integration-mybatis/plugins/integration-mybatis-plugin1/src/main/java/com/mybatis/plugin1/service/TestTransactional1.java b/example/integration-mybatis/plugins/integration-mybatis-plugin1/src/main/java/com/mybatis/plugin1/service/TestTransactional1.java
index f0ce55a19899a3f840d3c2ec291edaa9c46276d9..f3efe5919224d580e25d947d2cdb5f8e3f321517 100644
--- a/example/integration-mybatis/plugins/integration-mybatis-plugin1/src/main/java/com/mybatis/plugin1/service/TestTransactional1.java
+++ b/example/integration-mybatis/plugins/integration-mybatis-plugin1/src/main/java/com/mybatis/plugin1/service/TestTransactional1.java
@@ -2,6 +2,8 @@ package com.mybatis.plugin1.service;
import com.mybatis.plugin1.mapper.Plugin1Mapper;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Scope;
+import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
diff --git a/springboot-plugin-framework/pom.xml b/springboot-plugin-framework/pom.xml
index 55f83872dd50e13ee33decf2e9528de81c607cf4..1ed1d4d71185b060966b8a8c844930db83cdb922 100644
--- a/springboot-plugin-framework/pom.xml
+++ b/springboot-plugin-framework/pom.xml
@@ -105,6 +105,12 @@
${jackson.version}
+
+ commons-io
+ commons-io
+ 2.4
+
+
javax.servlet
javax.servlet-api
diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/exception/PluginPlugException.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/exception/PluginPlugException.java
deleted file mode 100644
index 6654af4341f73755c6934c9b960ae48cd77694df..0000000000000000000000000000000000000000
--- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/exception/PluginPlugException.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.gitee.starblues.exception;
-
-/**
- * 插件安装异常
- * @author zhangzhuo
- * @version 1.0
- */
-public class PluginPlugException extends Exception{
-
-
- public PluginPlugException() {
- super();
- }
-
- public PluginPlugException(String message) {
- super(message);
- }
-
- public PluginPlugException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public PluginPlugException(Throwable cause) {
- super(cause);
- }
-
- protected PluginPlugException(String message, Throwable cause, boolean enableSuppression,
- boolean writableStackTrace) {
- super(message, cause, enableSuppression, writableStackTrace);
- }
-}
diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/DefaultPluginFactory.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/DefaultPluginFactory.java
index 4e8c24c9a9b638b8f2a3c788c942dd392a130524..1aa92b295293a5f65cba72097fe23046733023a5 100644
--- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/DefaultPluginFactory.java
+++ b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/DefaultPluginFactory.java
@@ -84,7 +84,7 @@ public class DefaultPluginFactory implements PluginFactory {
return this;
} catch (Exception e) {
pluginListenerFactory.failure(pluginWrapper.getPluginId(), e);
- throw new Exception(e);
+ throw e;
} finally {
buildType = 1;
AopUtils.recoverAop();
@@ -107,7 +107,7 @@ public class DefaultPluginFactory implements PluginFactory {
return this;
} catch (Exception e) {
pluginListenerFactory.failure(pluginId, e);
- throw new Exception(e);
+ throw e;
} finally {
buildType = 2;
}
diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/PluginRegistryInfo.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/PluginRegistryInfo.java
index 120d0cec854ee2df7b65e99a1d06373484447708..9d74d0b6dcb03cbb11d27db2ed4b3bf0eef81d34 100644
--- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/PluginRegistryInfo.java
+++ b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/PluginRegistryInfo.java
@@ -4,6 +4,7 @@ import com.gitee.starblues.realize.BasePlugin;
import org.pf4j.PluginWrapper;
import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
/**
* 注册的插件信息
@@ -13,11 +14,15 @@ import java.util.*;
*/
public class PluginRegistryInfo {
+ /**
+ * 全局扩展信息
+ */
+ private static Map globalExtensionMap = new ConcurrentHashMap<>();
/**
* 扩展存储项
*/
- private Map extensionMap = new HashMap<>();
+ private Map extensionMap = new ConcurrentHashMap<>();
private PluginWrapper pluginWrapper;
private BasePlugin basePlugin;
@@ -104,8 +109,6 @@ public class PluginRegistryInfo {
-
-
/**
* 添加扩展数据
* @param key 扩展的key
@@ -130,4 +133,30 @@ public class PluginRegistryInfo {
}
}
+
+
+ /**
+ * 添加全局扩展数据
+ * @param key 扩展的key
+ * @param value 扩展值
+ */
+ public static synchronized void addGlobalExtension(String key, Object value){
+ globalExtensionMap.put(key, value);
+ }
+
+ /**
+ * 获取全局扩展值
+ * @param key 全局扩展的key
+ * @param 返回值泛型
+ * @return 扩展值
+ */
+ public static synchronized T getGlobalExtension(String key){
+ Object o = globalExtensionMap.get(key);
+ if(o == null){
+ return null;
+ } else {
+ return (T) o;
+ }
+ }
+
}
diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/SpringBeanRegister.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/SpringBeanRegister.java
index 82cced357a9cbc8a024bb52de54e459d0aecf593..e1307dc23066d5056f36343afbac493d5c17a154 100644
--- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/SpringBeanRegister.java
+++ b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/SpringBeanRegister.java
@@ -1,6 +1,8 @@
package com.gitee.starblues.factory;
import com.gitee.starblues.factory.process.pipe.bean.name.PluginAnnotationBeanNameGenerator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.ApplicationContext;
@@ -17,6 +19,8 @@ import java.util.function.Consumer;
*/
public class SpringBeanRegister {
+ private static final Logger logger = LoggerFactory.getLogger(SpringBeanRegister.class);
+
private final GenericApplicationContext applicationContext;
public SpringBeanRegister(ApplicationContext applicationContext){
@@ -36,12 +40,12 @@ public class SpringBeanRegister {
/**
* 默认注册
* @param pluginId 插件id
- * @param namePrefix bean名称前缀
+ * @param suffixName bean 后缀名称
* @param aClass 类名
* @return 注册的bean名称
*/
- public String register(String pluginId, String namePrefix, Class> aClass) {
- return register(pluginId, namePrefix, aClass, null);
+ public String register(String pluginId, String suffixName, Class> aClass) {
+ return register(pluginId, suffixName, aClass, null);
}
@@ -59,25 +63,24 @@ public class SpringBeanRegister {
/**
* 默认注册
* @param pluginId 插件id
- * @param namePrefix bean名称前缀
+ * @param suffixName bean 后缀名称
* @param aClass 注册的类
* @param consumer 自定义处理AnnotatedGenericBeanDefinition
* @return 注册的bean名称
*/
- public String register(String pluginId, String namePrefix, Class> aClass, Consumer consumer) {
+ public String register(String pluginId, String suffixName, Class> aClass,
+ Consumer consumer) {
AnnotatedGenericBeanDefinition beanDefinition = new
AnnotatedGenericBeanDefinition(aClass);
- if(namePrefix == null){
- namePrefix = "";
- }
BeanNameGenerator beanNameGenerator =
- new PluginAnnotationBeanNameGenerator(namePrefix);
+ new PluginAnnotationBeanNameGenerator(suffixName);
String beanName = beanNameGenerator.generateBeanName(beanDefinition, applicationContext);
if(PluginInfoContainer.existRegisterBeanName((beanName))){
String error = MessageFormat.format("Bean name {0} already exist of {1}",
beanName, aClass.getName());
- throw new RuntimeException(error);
+ logger.error(error);
+ return null;
}
if(consumer != null){
consumer.accept(beanDefinition);
diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/BasicBeanProcessor.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/BasicBeanProcessor.java
index 0726d989575e163ee5dfe6d33102f5f73048e170..1ed1e8d71b309d5e7a7b0ea6546f30c40393d269 100644
--- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/BasicBeanProcessor.java
+++ b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/BasicBeanProcessor.java
@@ -6,12 +6,18 @@ import com.gitee.starblues.factory.process.pipe.PluginPipeProcessor;
import com.gitee.starblues.factory.process.pipe.classs.group.ComponentGroup;
import com.gitee.starblues.factory.process.pipe.classs.group.ConfigurationGroup;
import com.gitee.starblues.factory.process.pipe.classs.group.RepositoryGroup;
+import org.springframework.aop.Advisor;
+import org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper;
+import org.springframework.aop.support.AopUtils;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
+import org.springframework.core.annotation.AnnotationUtils;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* 基础bean注册
@@ -21,13 +27,19 @@ import java.util.Set;
*/
public class BasicBeanProcessor implements PluginPipeProcessor {
+
private static final String KEY = "BasicBeanProcessor";
+ private final static String AOP_BEAN_NAME_INC_NUM = "AOP_BEAN_NAME_INC_NUM";
+
private final SpringBeanRegister springBeanRegister;
+ private final BeanFactoryAdvisorRetrievalHelper helper;
public BasicBeanProcessor(ApplicationContext applicationContext){
Objects.requireNonNull(applicationContext);
this.springBeanRegister = new SpringBeanRegister(applicationContext);
+ this.helper = new BeanFactoryAdvisorRetrievalHelper(
+ (ConfigurableListableBeanFactory)applicationContext.getAutowireCapableBeanFactory());
}
@Override
@@ -73,11 +85,43 @@ public class BasicBeanProcessor implements PluginPipeProcessor {
if(aClass == null){
continue;
}
- String beanName = springBeanRegister.register(pluginId, aClass);
+ String namePrefix = resolveAopClass(aClass);
+ String beanName = springBeanRegister.register(pluginId, namePrefix, aClass);
beanNames.add(beanName);
}
}
+ /**
+ * 该方法解决重复安装、卸载插件时, AOP 类无法注入的bug.
+ * 无法注入的原因如下:
+ *
+ * 第一次初始化插件时, 首先spring boot 会将要注入的类依次匹配,如果是代理类的话,则在
+ * AbstractAutoProxyCreator 该类的proxyTypes属性会将生成的代理类缓存下来,并返回,然后将该代理类注入到使用的类中。
+ * 第二次访问时, spring boot 会直接返回了缓存下来的代理类。导致注入类和代理类类型不匹配,无法注入。
+ * 解决办法: proxyTypes 缓存的key 是通过class、和beanName 生成的。所以每次注册插件时,将代理类的beanName 用AOP_BEAN_NAME_INC_NUM
+ * 的递增数字作为前缀,这样每次生成都得都是新代理类。
+ * @param aClass 当前要处理的类
+ * @return 返回代理类的bean名称前缀
+ */
+ private String resolveAopClass(Class> aClass){
+ List advisorBeans = helper.findAdvisorBeans();
+ List advisorsThatCanApply = AopUtils.findAdvisorsThatCanApply(advisorBeans, aClass);
+ if(advisorsThatCanApply.isEmpty()){
+ // 如果不是代理类, 则返回 null
+ return null;
+ } else {
+ Object o = PluginRegistryInfo.getGlobalExtension(AOP_BEAN_NAME_INC_NUM);
+ AtomicInteger atomicInteger = null;
+ if(o instanceof AtomicInteger){
+ atomicInteger = (AtomicInteger) o;
+ } else {
+ atomicInteger = new AtomicInteger(0);
+ PluginRegistryInfo.addGlobalExtension(AOP_BEAN_NAME_INC_NUM, atomicInteger);
+ }
+ // 是代理类
+ return String.valueOf(atomicInteger.getAndIncrement());
+ }
+ }
}
diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/name/PluginAnnotationBeanNameGenerator.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/name/PluginAnnotationBeanNameGenerator.java
index 9b19849178591fd842b72bf8c6054b451143a3ec..700b06b127ceafdfbb79ec780aa2bba17c02f08a 100644
--- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/name/PluginAnnotationBeanNameGenerator.java
+++ b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/name/PluginAnnotationBeanNameGenerator.java
@@ -15,15 +15,15 @@ import org.springframework.util.StringUtils;
public class PluginAnnotationBeanNameGenerator extends AnnotationBeanNameGenerator {
/**
- * 插件id
+ * 后缀名称
*/
- private final String pluginId;
+ private final String suffixName;
- public PluginAnnotationBeanNameGenerator(String pluginId) {
- if(pluginId == null){
- this.pluginId = "";
+ public PluginAnnotationBeanNameGenerator(String suffixName) {
+ if(StringUtils.isEmpty(suffixName)){
+ this.suffixName = "";
} else {
- this.pluginId = pluginId + "-";
+ this.suffixName = "@" + suffixName;
}
}
@@ -35,7 +35,7 @@ public class PluginAnnotationBeanNameGenerator extends AnnotationBeanNameGenerat
return beanName;
}
}
- return pluginId + buildDefaultBeanName(definition, registry);
+ return buildDefaultBeanName(definition, registry) + suffixName;
}
diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/PluginClassProcess.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/PluginClassProcess.java
index 33c1de6d548144fa04ae40ebf133bcd3785dcf58..45c2215346063423e6bab5b166af47378519f90a 100644
--- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/PluginClassProcess.java
+++ b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/PluginClassProcess.java
@@ -9,6 +9,7 @@ import com.gitee.starblues.loader.load.PluginClassLoader;
import com.gitee.starblues.realize.BasePlugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;
diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/bean/PluginControllerPostProcessor.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/bean/PluginControllerPostProcessor.java
index 503ed118fde1e6df2b3bd495d74505dda4c748cd..b5543f96010c22ee748f03bc43db7360e2e3a6be 100644
--- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/bean/PluginControllerPostProcessor.java
+++ b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/bean/PluginControllerPostProcessor.java
@@ -8,9 +8,9 @@ import com.gitee.starblues.factory.SpringBeanRegister;
import com.gitee.starblues.factory.process.pipe.classs.group.ControllerGroup;
import com.gitee.starblues.factory.process.post.PluginPostProcessor;
import com.gitee.starblues.utils.AopUtils;
-import org.pf4j.PluginWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
@@ -53,20 +53,25 @@ public class PluginControllerPostProcessor implements PluginPostProcessor {
public void registry(List pluginRegistryInfos) throws Exception {
for (PluginRegistryInfo pluginRegistryInfo : pluginRegistryInfos) {
AopUtils.resolveAop(pluginRegistryInfo.getPluginWrapper());
- List> groupClasses = pluginRegistryInfo.getGroupClasses(ControllerGroup.SPRING_CONTROLLER);
- if(groupClasses == null || groupClasses.isEmpty()){
- continue;
- }
- List controllerBeanWrappers = new ArrayList<>();
- for (Class> groupClass : groupClasses) {
- if(groupClass == null){
+ try {
+ List> groupClasses = pluginRegistryInfo.getGroupClasses(ControllerGroup.SPRING_CONTROLLER);
+ if(groupClasses == null || groupClasses.isEmpty()){
continue;
}
- ControllerBeanWrapper controllerBeanWrapper = registry(pluginRegistryInfo, groupClass);
- controllerBeanWrappers.add(controllerBeanWrapper);
- process(1, pluginRegistryInfo.getPluginWrapper().getPluginId(), groupClass);
+ List controllerBeanWrappers = new ArrayList<>();
+ for (Class> groupClass : groupClasses) {
+ if(groupClass == null){
+ continue;
+ }
+ ControllerBeanWrapper controllerBeanWrapper = registry(pluginRegistryInfo, groupClass);
+ controllerBeanWrappers.add(controllerBeanWrapper);
+ process(1, pluginRegistryInfo.getPluginWrapper().getPluginId(), groupClass);
+ }
+ pluginRegistryInfo.addProcessorInfo(getKey(pluginRegistryInfo), controllerBeanWrappers);
+ } finally {
+ AopUtils.recoverAop();
}
- pluginRegistryInfo.addProcessorInfo(getKey(pluginRegistryInfo), controllerBeanWrappers);
+
}
}
@@ -103,23 +108,23 @@ public class PluginControllerPostProcessor implements PluginPostProcessor {
*/
private ControllerBeanWrapper registry(PluginRegistryInfo pluginRegistryInfo, Class> aClass)
throws Exception {
- String pluginId= pluginRegistryInfo.getPluginWrapper().getPluginId();
+ String pluginId = pluginRegistryInfo.getPluginWrapper().getPluginId();
String beanName = springBeanRegister.register(pluginId, aClass);
if(beanName == null || "".equals(beanName)){
throw new PluginBeanFactoryException("registry "+ aClass.getName() + "failure!");
}
- Object object = applicationContext.getBean(beanName);
- if(object == null){
- throw new PluginBeanFactoryException("registry "+ aClass.getName() + "failure! " +
- "Not found The instance of" + aClass.getName());
- }
- ControllerBeanWrapper controllerBeanWrapper = new ControllerBeanWrapper();
- controllerBeanWrapper.setBeanName(beanName);
- setPathPrefix(pluginId, aClass);
- Method getMappingForMethod = ReflectionUtils.findMethod(RequestMappingHandlerMapping.class,
- "getMappingForMethod", Method.class, Class.class);
- getMappingForMethod.setAccessible(true);
try {
+ Object object = applicationContext.getBean(beanName);
+ if(object == null){
+ throw new PluginBeanFactoryException("registry "+ aClass.getName() + "failure! " +
+ "Not found The instance of" + aClass.getName());
+ }
+ ControllerBeanWrapper controllerBeanWrapper = new ControllerBeanWrapper();
+ controllerBeanWrapper.setBeanName(beanName);
+ setPathPrefix(pluginId, aClass);
+ Method getMappingForMethod = ReflectionUtils.findMethod(RequestMappingHandlerMapping.class,
+ "getMappingForMethod", Method.class, Class.class);
+ getMappingForMethod.setAccessible(true);
Method[] methods = aClass.getMethods();
Set requestMappingInfos = new HashSet<>();
for (Method method : methods) {
@@ -133,12 +138,10 @@ public class PluginControllerPostProcessor implements PluginPostProcessor {
controllerBeanWrapper.setRequestMappingInfos(requestMappingInfos);
controllerBeanWrapper.setBeanClass(aClass);
return controllerBeanWrapper;
- } catch (SecurityException e) {
- throw new Exception(e);
- } catch (InvocationTargetException e) {
- throw new Exception(e);
} catch (Exception e){
- throw new Exception(e);
+ // 出现异常, 卸载该 controller bean
+ springBeanRegister.unregister(pluginId, beanName);
+ throw e;
}
}
diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/bean/PluginInvokePostProcessor.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/bean/PluginInvokePostProcessor.java
index 3aa40d8fc624347efbf77d13f8fa31d3cb043bdb..481ec1348f3aea315dbc6c0012e042d448e77323 100644
--- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/bean/PluginInvokePostProcessor.java
+++ b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/bean/PluginInvokePostProcessor.java
@@ -53,19 +53,27 @@ public class PluginInvokePostProcessor implements PluginPostProcessor {
public void registry(List pluginRegistryInfos) throws Exception {
for (PluginRegistryInfo pluginRegistryInfo : pluginRegistryInfos) {
AopUtils.resolveAop(pluginRegistryInfo.getPluginWrapper());
- List> suppers = pluginRegistryInfo.getGroupClasses(SupplierGroup.SUPPLIER);
- if(suppers == null){
- continue;
+ try {
+ List> suppers = pluginRegistryInfo.getGroupClasses(SupplierGroup.SUPPLIER);
+ if(suppers == null){
+ continue;
+ }
+ processSupper(pluginRegistryInfo, suppers);
+ } finally {
+ AopUtils.recoverAop();
}
- processSupper(pluginRegistryInfo, suppers);
}
for (PluginRegistryInfo pluginRegistryInfo : pluginRegistryInfos) {
AopUtils.resolveAop(pluginRegistryInfo.getPluginWrapper());
- List> callers = pluginRegistryInfo.getGroupClasses(CallerGroup.CALLER);
- if(callers == null){
- continue;
+ try {
+ List> callers = pluginRegistryInfo.getGroupClasses(CallerGroup.CALLER);
+ if(callers == null){
+ continue;
+ }
+ processCaller(pluginRegistryInfo, callers);
+ } finally {
+ AopUtils.recoverAop();
}
- processCaller(pluginRegistryInfo, callers);
}
}
diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/IntegrationConfiguration.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/IntegrationConfiguration.java
index f10e9e7933339d93e7d0b62198169973495af155..76475ea8d609379145814049e412e26d9769e480 100644
--- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/IntegrationConfiguration.java
+++ b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/IntegrationConfiguration.java
@@ -11,37 +11,39 @@ import org.pf4j.RuntimeMode;
public interface IntegrationConfiguration {
/**
- * 运行环境
- * @return 运行环境
+ * 运行环境。运行项目时的模式。分为开发环境(DEVELOPMENT)、生产环境(DEPLOYMENT)
+ * @return RuntimeMode.DEVELOPMENT、RuntimeMode.DEPLOYMENT
*/
RuntimeMode environment();
/**
- * 插件的路径
+ * 插件的路径。开发环境建议直接配置为插件模块的父级目录。例如: plugins。如果启动主程序时, 插件为加载, 请检查该配置是否正确。
* @return 插件的路径
*/
String pluginPath();
/**
- * 插件文件的配置路径
+ * 插件文件的配置路径。在生产环境下, 插件的配置文件路径。
+ * 在生产环境下, 请将所有插件使用到的配置文件统一放到该路径下管理。
+ * 在开发环境下,配置为空串。程序会自动从 resources 获取配置文件, 所以请确保编译后的target 下存在该配置文件
* @return 插件文件的配置路径
*/
String pluginConfigFilePath();
/**
- * 上传插件的临时保存路径。
+ * 上传插件包存储的临时路径。默认 temp(相对于主程序jar路径)。
* @return 上传插件的临时保存路径。
*/
String uploadTempPath();
/**
- * 插件备份路径。
+ * 插件备份路径。默认 backupPlugin (相对于主程序jar路径)。
* @return 插件备份路径。
*/
String backupPath();
/**
- * 统一插件RestController的路径前缀
+ * 插件 RestController 统一请求的路径前缀。只有 pluginRestControllerPathPrefix
* @return path
*/
String pluginRestControllerPathPrefix();
diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/initialize/AbstractPluginInitializer.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/initialize/AbstractPluginInitializer.java
index c9db9db911c6694b4d9b701c3bc28fb0d5e7737d..a9e87260e65010170166363866c44776cd8080ee 100644
--- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/initialize/AbstractPluginInitializer.java
+++ b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/initialize/AbstractPluginInitializer.java
@@ -1,6 +1,5 @@
package com.gitee.starblues.integration.initialize;
-import com.gitee.starblues.exception.PluginPlugException;
import com.gitee.starblues.integration.listener.PluginInitializerListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -26,16 +25,16 @@ public abstract class AbstractPluginInitializer implements PluginInitializer{
}
@Override
- public void initialize() throws PluginPlugException {
+ public void initialize() throws Exception {
log.info("Start execute plugin initializer.");
this.executeInitialize();
}
/**
* 执行初始化
- * @throws PluginPlugException 插件插拔异常
+ * @throws Exception 插件执行初始化异常
*/
- public abstract void executeInitialize() throws PluginPlugException;
+ public abstract void executeInitialize() throws Exception;
/**
diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/initialize/AutoPluginInitializer.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/initialize/AutoPluginInitializer.java
index 8a2afc1c3bc858aef9347c21f4fbc20ae9d6a5a7..28ef0c8eef5e67ce4d780821d9817a6348d310a0 100644
--- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/initialize/AutoPluginInitializer.java
+++ b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/initialize/AutoPluginInitializer.java
@@ -1,6 +1,5 @@
package com.gitee.starblues.integration.initialize;
-import com.gitee.starblues.exception.PluginPlugException;
import com.gitee.starblues.integration.PluginApplication;
import com.gitee.starblues.integration.listener.PluginInitializerListener;
import com.gitee.starblues.integration.operator.PluginOperator;
@@ -32,12 +31,8 @@ public class AutoPluginInitializer extends AbstractPluginInitializer {
@PostConstruct
@Override
- public void executeInitialize() throws PluginPlugException {
- try {
- pluginOperator.initPlugins(pluginInitializerListener);
- } catch (Exception e) {
- e.printStackTrace();
- }
+ public void executeInitialize() throws Exception {
+ pluginOperator.initPlugins(pluginInitializerListener);
}
}
diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/initialize/ManualPluginInitializer.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/initialize/ManualPluginInitializer.java
index dc7f722bb1962ae258464a20c2a140cdb294b641..ae1d9dc95d87efcac9515606b376aa5a064b4045 100644
--- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/initialize/ManualPluginInitializer.java
+++ b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/initialize/ManualPluginInitializer.java
@@ -1,6 +1,5 @@
package com.gitee.starblues.integration.initialize;
-import com.gitee.starblues.exception.PluginPlugException;
import com.gitee.starblues.integration.PluginApplication;
import com.gitee.starblues.integration.listener.PluginInitializerListener;
import com.gitee.starblues.integration.operator.PluginOperator;
@@ -26,7 +25,7 @@ public class ManualPluginInitializer extends AbstractPluginInitializer {
@Override
- public void executeInitialize() throws PluginPlugException {
+ public void executeInitialize() throws Exception {
pluginOperator.initPlugins(pluginInitializerListener);
}
diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/initialize/PluginInitializer.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/initialize/PluginInitializer.java
index 6008f35eb88961cdcd669b6a4613bf83a47e47c6..4c4b09453a7d1719604b92c3993edd0bbcdb98ff 100644
--- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/initialize/PluginInitializer.java
+++ b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/initialize/PluginInitializer.java
@@ -1,6 +1,5 @@
package com.gitee.starblues.integration.initialize;
-import com.gitee.starblues.exception.PluginPlugException;
/**
* 插件初始化者
@@ -12,9 +11,9 @@ public interface PluginInitializer {
/**
* 初始化
- * @throws PluginPlugException 插件安装异常
+ * @throws Exception 插件安装异常
*/
- void initialize() throws PluginPlugException;
+ void initialize() throws Exception;
}
diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/DefaultPluginOperator.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/DefaultPluginOperator.java
index f442856e390c4d806e29c872ca60ecb4b5e7fd9b..abcee549e99a5f685c604cc85259f55aa6279d1d 100644
--- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/DefaultPluginOperator.java
+++ b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/DefaultPluginOperator.java
@@ -1,6 +1,5 @@
package com.gitee.starblues.integration.operator;
-import com.gitee.starblues.exception.PluginPlugException;
import com.gitee.starblues.integration.IntegrationConfiguration;
import com.gitee.starblues.integration.listener.PluginInitializerListener;
import com.gitee.starblues.integration.listener.PluginInitializerListenerFactory;
@@ -10,8 +9,9 @@ import com.gitee.starblues.integration.operator.verify.PluginLegalVerify;
import com.gitee.starblues.integration.operator.verify.PluginUploadVerify;
import com.gitee.starblues.factory.DefaultPluginFactory;
import com.gitee.starblues.factory.PluginFactory;
+import com.gitee.starblues.utils.PluginFileUtils;
+import org.apache.commons.io.FileUtils;
import org.pf4j.*;
-import org.pf4j.util.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
@@ -19,10 +19,12 @@ import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
@@ -67,14 +69,16 @@ public class DefaultPluginOperator implements PluginOperator {
@Override
- public synchronized boolean initPlugins(PluginInitializerListener pluginInitializerListener) throws PluginPlugException {
+ public synchronized boolean initPlugins(PluginInitializerListener pluginInitializerListener) throws Exception {
if(isInit){
- throw new PluginPlugException("Plugins Already initialized. Cannot be initialized again");
+ throw new RuntimeException("Plugins Already initialized. Cannot be initialized again");
}
try {
pluginInitializerListenerFactory.addPluginInitializerListeners(pluginInitializerListener);
log.info("Start initialize plugins");
pluginInitializerListenerFactory.before();
+ // 启动前, 清除空文件
+ PluginFileUtils.cleanEmptyFile(pluginManager.getPluginsRoot());
pluginManager.loadPlugins();
pluginManager.startPlugins();
List pluginWrappers = pluginManager.getStartedPlugins();
@@ -92,21 +96,17 @@ public class DefaultPluginOperator implements PluginOperator {
return true;
} catch (Exception e){
pluginInitializerListenerFactory.failure(e);
- throw new PluginPlugException(e);
+ throw e;
}
}
@Override
- public String loadPlugin(Path path) throws PluginPlugException {
- try {
- return pluginManager.loadPlugin(path);
- } catch (Exception e){
- throw new PluginPlugException(e);
- }
+ public String loadPlugin(Path path) throws Exception {
+ return pluginManager.loadPlugin(path);
}
@Override
- public boolean install(Path path) throws PluginPlugException {
+ public boolean install(Path path) throws Exception {
if(path == null){
throw new IllegalArgumentException("Method:install param [pluginId] can not be empty");
}
@@ -115,37 +115,40 @@ public class DefaultPluginOperator implements PluginOperator {
pluginId = pluginManager.loadPlugin(path);
return start(pluginId);
} catch (Exception e){
- if(pluginId != null){
- // 说明load成功, 但是没有启动成功, 则卸载该插件
- pluginManager.unloadPlugin(pluginId);
- try {
- backup(path, "installErrorPlugin", 2);
- } catch (Exception e1) {
- throw new PluginPlugException(e1);
- }
- }
- throw new PluginPlugException(e);
+ // 说明load成功, 但是没有启动成功, 则卸载该插件
+ uninstall(pluginId);
+ throw e;
}
}
@Override
- public boolean uninstall(String pluginId) throws PluginPlugException {
+ public boolean uninstall(String pluginId) throws Exception {
+ PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId);
+ if(pluginWrapper == null){
+ log.error("Uninstall Plugin failure, Not found plugin {}", pluginId);
+ return false;
+ }
try {
- stop(pluginId);
- if(pluginManager.unloadPlugin(pluginId)){
- log.info("Uninstall Plugin [{}] Success", pluginId);
- return true;
+ pluginFactory.unRegistry(pluginId);
+ pluginFactory.build();
+ return true;
+ } catch (Exception e){
+ throw new Exception("Stop plugin [" + pluginId + "] failure. " + e.getMessage() ,e);
+ } finally {
+ if (pluginManager.unloadPlugin(pluginId)) {
+ // 卸载完后,将插件文件移到备份文件中
+ backup(pluginWrapper.getPluginPath(), "uninstallPlugin", 1);
+ log.info("Unload Plugin [{}] success", pluginId);
+ log.info("Uninstall Plugin [{}] success", pluginId);
} else {
+ log.info("Unload Plugin [{}] failure", pluginId);
log.info("Uninstall Plugin [{}] failure", pluginId);
- return false;
}
- } catch (Exception e){
- throw new PluginPlugException(e);
}
}
@Override
- public boolean delete(String pluginId) throws PluginPlugException {
+ public boolean delete(String pluginId) throws Exception {
PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId);
if(pluginWrapper == null){
log.error("Delete -> Not Found plugin [{}]", pluginId);
@@ -154,16 +157,16 @@ public class DefaultPluginOperator implements PluginOperator {
if(pluginWrapper.getPluginState() == PluginState.STARTED){
uninstall(pluginId);
}
- backup(pluginWrapper.getPluginPath(), "deleteByPluginId", 2);
+ backup(pluginWrapper.getPluginPath(), "deleteByPluginId", 1);
log.info("Delete plugin [{}] Success", pluginId);
return true;
}
@Override
- public boolean delete(Path path) throws PluginPlugException {
+ public boolean delete(Path path) throws Exception {
try {
if(!Files.exists(path)){
- throw new PluginPlugException(path.toString() + " does not exist!");
+ throw new FileNotFoundException(path.toString() + " does not exist!");
}
PluginDescriptor pluginDescriptor = pluginDescriptorFinder.find(path);
PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginDescriptor.getPluginId());
@@ -173,8 +176,6 @@ public class DefaultPluginOperator implements PluginOperator {
log.error("Not found Plugin [{}] of path {}", pluginDescriptor.getPluginId(), path.toString());
return false;
}
- } catch (PluginException e) {
- throw new PluginPlugException(e);
} finally {
backup(path, "deleteByPath", 2);
}
@@ -182,44 +183,40 @@ public class DefaultPluginOperator implements PluginOperator {
@Override
- public boolean start(String pluginId) throws PluginPlugException {
+ public boolean start(String pluginId) throws Exception {
if(StringUtils.isEmpty(pluginId)){
throw new IllegalArgumentException("Method:start param [pluginId] can not be empty");
}
PluginWrapper pluginWrapper = getPluginWrapper(pluginId, "Start");
if(pluginWrapper.getPluginState() == PluginState.STARTED){
- throw new PluginPlugException("This plugin [" + pluginId + "] have already started");
+ throw new Exception("This plugin [" + pluginId + "] have already started");
}
- try {
- PluginState pluginState = pluginManager.startPlugin(pluginId);
- if(pluginState == PluginState.STARTED){
- pluginFactory.registry(pluginWrapper);
- pluginFactory.build();
- log.info("Start Plugin [{}] Success", pluginId);
- return true;
- }
- log.error("Start Plugin [{}] Failure, plugin state is not start. State[{}]", pluginId, pluginState.toString());
- return false;
- } catch (Exception e){
- throw new PluginPlugException("Start plugin " + pluginId + " failure. " + e.getMessage() ,e);
+ PluginState pluginState = pluginManager.startPlugin(pluginId);
+ if(pluginState == PluginState.STARTED){
+ pluginFactory.registry(pluginWrapper);
+ pluginFactory.build();
+ log.info("Start Plugin [{}] success", pluginId);
+ return true;
}
+ log.error("Start Plugin [{}] failure, plugin state is not start. State[{}]", pluginId, pluginState.toString());
+ return false;
}
@Override
- public boolean stop(String pluginId) throws PluginPlugException {
+ public boolean stop(String pluginId) throws Exception {
if(StringUtils.isEmpty(pluginId)){
throw new IllegalArgumentException("Method:stop param [pluginId] can not be empty");
}
PluginWrapper pluginWrapper = getPluginWrapper(pluginId, "Stop");
if(pluginWrapper.getPluginState() != PluginState.STARTED){
- throw new PluginPlugException("This plugin [" + pluginId + "] is not started");
+ throw new Exception("This plugin [" + pluginId + "] is not started");
}
try {
pluginFactory.unRegistry(pluginId);
pluginFactory.build();
return true;
} catch (Exception e){
- throw new PluginPlugException("Stop plugin [" + pluginId + "] failure. " + e.getMessage() ,e);
+ throw new Exception("Stop plugin [" + pluginId + "] failure. " + e.getMessage() ,e);
} finally {
pluginManager.stopPlugin(pluginId);
}
@@ -229,92 +226,84 @@ public class DefaultPluginOperator implements PluginOperator {
@Override
- public Path uploadPlugin(MultipartFile pluginFile) throws PluginPlugException {
+ public Path uploadPlugin(MultipartFile pluginFile) throws Exception {
if(pluginFile == null){
throw new IllegalArgumentException("Method:uploadPlugin param [pluginFile] can not be null");
}
+ // 获取文件的后缀名
+ String fileName = pluginFile.getOriginalFilename();
+ String suffixName = fileName.substring(fileName.lastIndexOf(".") + 1);
+ //检查文件格式是否合法
+ if(StringUtils.isEmpty(suffixName)){
+ throw new IllegalArgumentException("Invalid file type, please select .jar or .zip file");
+ }
+ if(!"jar".equalsIgnoreCase(suffixName) && !"zip".equalsIgnoreCase(suffixName)){
+ throw new IllegalArgumentException("Invalid file type, please select .jar or .zip file");
+ }
+ String tempPath = integrationConfiguration.uploadTempPath() + File.separator + fileName;
+ Path srcPath = PluginFileUtils.getExistPath(Paths.get(tempPath));
+ Path tempPluginFile = Files.write(srcPath, pluginFile.getBytes());
+
try {
- // 获取文件的后缀名
- String fileName = pluginFile.getOriginalFilename();
- String suffixName = fileName.substring(fileName.lastIndexOf(".") + 1);
- //检查文件格式是否合法
- if(StringUtils.isEmpty(suffixName)){
- throw new IllegalArgumentException("Invalid file type, please select .jar or .zip file");
- }
- if(!"jar".equalsIgnoreCase(suffixName) && !"zip".equalsIgnoreCase(suffixName)){
- throw new IllegalArgumentException("Invalid file type, please select .jar or .zip file");
- }
- String tempPath = integrationConfiguration.uploadTempPath() + File.separator + fileName;
- Path tempPluginFile = Files.write(getExistFile(Paths.get(tempPath)), pluginFile.getBytes());
- try {
- Path verifyPath = uploadPluginVerify.verify(tempPluginFile);
- if(verifyPath != null){
- String pluginFilePathString = pluginManager.getPluginsRoot().toString() +
- File.separator + fileName;
- Path pluginFilePath = Paths.get(pluginFilePathString);
- if(Files.exists(pluginFilePath)){
- // 如果存在同名插件的化, 先备份它
- backup(pluginFilePath, "uploadPluginFile", 1);
- }
- return Files.move(verifyPath, pluginFilePath);
- } else {
- PluginPlugException pluginPlugException =
- new PluginPlugException(fileName + " verify failure, verifyPath is null");
- verifyFailureDelete(tempPluginFile, pluginPlugException);
- throw pluginPlugException;
+ Path verifyPath = uploadPluginVerify.verify(tempPluginFile);
+ if(verifyPath != null){
+ String pluginFilePathString = pluginManager.getPluginsRoot().toString() +
+ File.separator + fileName;
+ Path pluginFilePath = Paths.get(pluginFilePathString);
+ File target = pluginFilePath.toFile();
+ if(target.exists()){
+ // 存在则拷贝一份
+ backup(pluginFilePath, "uploadPlugin", 2);
}
- } catch (Exception e){
- // 出现异常, 删除刚才上传的临时文件
- verifyFailureDelete(tempPluginFile, e);
- throw new PluginPlugException("Verify failure : " + e.getMessage(), e);
+ FileUtils.writeByteArrayToFile(target, FileUtils.readFileToByteArray(verifyPath.toFile()));
+ return pluginFilePath;
+ } else {
+ Exception exception =
+ new Exception(fileName + " verify failure, verifyPath is null");
+ verifyFailureDelete(tempPluginFile, exception);
+ throw exception;
}
} catch (Exception e){
- throw new PluginPlugException(e);
+ // 出现异常, 删除刚才上传的临时文件
+ verifyFailureDelete(tempPluginFile, e);
+ throw new Exception("Verify failure : " + e.getMessage(), e);
}
}
@Override
- public boolean uploadPluginAndStart(MultipartFile pluginFile) throws PluginPlugException {
+ public boolean uploadPluginAndStart(MultipartFile pluginFile) throws Exception {
if(pluginFile == null){
- throw new PluginPlugException("Method:uploadPluginAndStart param [pluginFile] can not be null");
- }
- try {
- Path path = uploadPlugin(pluginFile);
- this.install(path);
- log.info("Upload And Start Plugin [{}] Success. ", path.toString());
- return true;
- } catch (Exception e){
- throw new PluginPlugException(e);
+ throw new Exception("Method:uploadPluginAndStart param [pluginFile] can not be null");
}
+ Path path = uploadPlugin(pluginFile);
+ this.install(path);
+ log.info("Upload And Start Plugin Success. [{}]", path.toString());
+ return true;
}
@Override
- public boolean uploadConfigFile(MultipartFile configFile) throws PluginPlugException {
+ public boolean uploadConfigFile(MultipartFile configFile) throws Exception {
if(configFile == null){
throw new IllegalArgumentException("Method:uploadConfigFile param [configFile] can not be null");
}
- try {
- String fileName = configFile.getOriginalFilename();
- String configPath = integrationConfiguration.pluginConfigFilePath() +
- File.separator + fileName;
- Files.write(getExistFile(Paths.get(configPath)), configFile.getBytes());
- return true;
- } catch (Exception e){
- throw new PluginPlugException(e);
- }
+ String fileName = configFile.getOriginalFilename();
+ String configPath = integrationConfiguration.pluginConfigFilePath() +
+ File.separator + fileName;
+ Path srcPath = PluginFileUtils.getExistPath(Paths.get(configPath));
+ Files.write(srcPath, configFile.getBytes());
+ return true;
}
@Override
- public boolean backupPlugin(Path path, String appendName) throws PluginPlugException {
+ public boolean backupPlugin(Path path, String appendName) throws Exception {
Objects.requireNonNull(path);
return backup(path, appendName, 2);
}
-
@Override
- public boolean backupPlugin(String pluginId, String appendName) throws PluginPlugException {
+ public boolean backupPlugin(String pluginId, String appendName) throws Exception {
PluginWrapper pluginManager = getPluginWrapper(pluginId, "BackupPlugin by pluginId");
return backupPlugin(pluginManager.getPluginPath(), appendName);
}
@@ -337,7 +326,7 @@ public class DefaultPluginOperator implements PluginOperator {
@Override
- public Set getPluginFilePaths() throws PluginPlugException {
+ public Set getPluginFilePaths() throws Exception {
try {
RuntimeMode environment = integrationConfiguration.environment();
Set paths = new HashSet<>();
@@ -345,13 +334,13 @@ public class DefaultPluginOperator implements PluginOperator {
paths.add(integrationConfiguration.pluginPath());
return paths;
}
- List files = FileUtils.getJars(Paths.get(integrationConfiguration.pluginPath()));
+ List files = org.pf4j.util.FileUtils.getJars(Paths.get(integrationConfiguration.pluginPath()));
return files.stream()
.filter(file -> file != null)
.map(file -> file.getAbsolutePath())
.collect(Collectors.toSet());
} catch (Exception e){
- throw new PluginPlugException(e);
+ throw new Exception(e);
}
}
@@ -365,62 +354,46 @@ public class DefaultPluginOperator implements PluginOperator {
* 得到插件包装类
* @param pluginId 插件id
* @return PluginWrapper
- * @throws PluginPlugException 插件装配异常
+ * @throws Exception 插件装配异常
*/
- private PluginWrapper getPluginWrapper(String pluginId, String errorMsg) throws PluginPlugException {
+ private PluginWrapper getPluginWrapper(String pluginId, String errorMsg) throws Exception {
PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId);
if (pluginWrapper == null) {
- throw new PluginPlugException(errorMsg + " -> Not found plugin " + pluginId);
+ throw new Exception(errorMsg + " -> Not found plugin " + pluginId);
}
return pluginWrapper;
}
- /**
- * 得到存在的文件
- * @param path 插件路径
- * @return 插件路径
- * @throws IOException 没有发现文件异常
- */
- private Path getExistFile(Path path) throws IOException {
- Path parent = path.getParent();
- if(!Files.exists(parent)){
- Files.createDirectories(parent);
- }
- if(!Files.exists(path)){
- Files.createFile(path);
- }
- return path;
- }
/**
* 校验文件失败后, 删除临时文件
* @param tempPluginFile 临时文件路径
* @param e 异常信息
- * @throws PluginPlugException PluginPlugException
+ * @throws Exception Exception
*/
- private void verifyFailureDelete(Path tempPluginFile, Exception e) throws PluginPlugException {
+ private void verifyFailureDelete(Path tempPluginFile, Exception e) throws Exception {
try {
Files.deleteIfExists(tempPluginFile);
}catch (IOException e1){
- throw new PluginPlugException("Verify failure and delete temp file failure : " + e.getMessage(), e);
+ throw new Exception("Verify failure and delete temp file failure : " + e.getMessage(), e);
}
}
/**
* 备份
- * @param path 文件的路径
+ * @param sourcePath 源文件的路径
* @param appendName 追加的字符串
* @param type 类型 1移动 2拷贝
* @return 结果
- * @throws PluginPlugException PluginPlugException
+ * @throws Exception Exception
*/
- private boolean backup(Path path, String appendName, int type) throws PluginPlugException {
- if(!Files.exists(path)){
- throw new PluginPlugException(path.toString() + " does not exist!");
+ private boolean backup(Path sourcePath, String appendName, int type) throws Exception {
+ if(!Files.exists(sourcePath)){
+ throw new FileNotFoundException(sourcePath.toString() + " does not exist!");
}
try {
- String fileName = path.getFileName().toString();
+ String fileName = sourcePath.getFileName().toString();
String targetName = integrationConfiguration.backupPath() + File.separator + getNowTimeByFormat();
if(!StringUtils.isEmpty(appendName)){
targetName = targetName + "_" + appendName;
@@ -429,14 +402,15 @@ public class DefaultPluginOperator implements PluginOperator {
if(!Files.exists(target.getParent())){
Files.createDirectories(target.getParent());
}
+ File targetFile = target.toFile();
+ File sourceFile = sourcePath.toFile();
+ FileUtils.writeByteArrayToFile(targetFile, FileUtils.readFileToByteArray(sourceFile));
if(type == 1){
- Files.move(path, target);
- } else {
- Files.copy(path, target);
+ FileUtils.writeByteArrayToFile(sourceFile, "".getBytes());
}
return true;
} catch (IOException e) {
- throw new PluginPlugException("BackupPlugin " + path.toString() + " failure : " + e.getMessage(), e);
+ throw new Exception("BackupPlugin " + sourcePath.toString() + " failure : " + e.getMessage(), e);
}
}
diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/PluginOperator.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/PluginOperator.java
index 072ebb4d62ac44a5972c793b88f442ff3da73174..e4a80fadb0fdce029a481706427dc96fee4372fb 100644
--- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/PluginOperator.java
+++ b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/PluginOperator.java
@@ -1,6 +1,5 @@
package com.gitee.starblues.integration.operator;
-import com.gitee.starblues.exception.PluginPlugException;
import com.gitee.starblues.integration.listener.PluginInitializerListener;
import com.gitee.starblues.integration.operator.module.PluginInfo;
import org.pf4j.PluginWrapper;
@@ -19,115 +18,115 @@ import java.util.Set;
public interface PluginOperator {
/**
- * 初始化插件
+ * 初始化插件。该方法只能执行一次。
* @param pluginInitializerListener 插件初始化监听者
* @return 成功返回true.不成功抛出异常或者返回false
- * @throws PluginPlugException 插件插头异常
+ * @throws Exception 插件插头异常
*/
- boolean initPlugins(PluginInitializerListener pluginInitializerListener) throws PluginPlugException;
+ boolean initPlugins(PluginInitializerListener pluginInitializerListener) throws Exception;
/**
* 通过路径加载插件(不会启用)
* @param path 插件路径
* @return 成功返回插件id
- * @throws PluginPlugException 插件插头异常
+ * @throws Exception 插件插头异常
*/
- String loadPlugin(Path path) throws PluginPlugException;
+ String loadPlugin(Path path) throws Exception;
/**
* 通过路径安装插件(会启用)
* @param path 插件路径
* @return 成功返回true.不成功抛出异常或者返回false
- * @throws PluginPlugException 插件插头异常
+ * @throws Exception 插件插头异常
*/
- boolean install(Path path) throws PluginPlugException;
+ boolean install(Path path) throws Exception;
/**
* 卸载插件
* @param pluginId 插件id
* @return 成功返回true.不成功抛出异常或者返回false
- * @throws PluginPlugException 插件插头异常
+ * @throws Exception 插件插头异常
*/
- boolean uninstall(String pluginId) throws PluginPlugException;
+ boolean uninstall(String pluginId) throws Exception;
/**
* 通过插件id删除插件。只适用于生产环境
* @param pluginId 插件id
* @return 成功返回true.不成功抛出异常或者返回false
- * @throws PluginPlugException 插件插头异常
+ * @throws Exception 插件插头异常
*/
- boolean delete(String pluginId) throws PluginPlugException;
+ boolean delete(String pluginId) throws Exception;
/**
* 通过路径删除插件。只适用于生产环境
* @param path 插件路径
* @return 成功返回true.不成功抛出异常或者返回false
- * @throws PluginPlugException 插件插头异常
+ * @throws Exception 插件插头异常
*/
- boolean delete(Path path) throws PluginPlugException;
+ boolean delete(Path path) throws Exception;
/**
* 启用插件
* @param pluginId 插件id
* @return 成功返回true.不成功抛出异常或者返回false
- * @throws PluginPlugException 插件插头异常
+ * @throws Exception 插件插头异常
*/
- boolean start(String pluginId) throws PluginPlugException;
+ boolean start(String pluginId) throws Exception;
/**
* 停止插件
* @param pluginId 插件id
* @return 成功返回true.不成功抛出异常或者返回false
- * @throws PluginPlugException 插件插头异常
+ * @throws Exception 插件插头异常
*/
- boolean stop(String pluginId) throws PluginPlugException;
+ boolean stop(String pluginId) throws Exception;
/**
* 上传插件。只适用于生产环境
* @param pluginFile 插件文件
* @return 成功返回插件路径.不成功返回null, 或者抛出异常
- * @throws PluginPlugException 插件插头异常
+ * @throws Exception 插件插头异常
*/
- Path uploadPlugin(MultipartFile pluginFile) throws PluginPlugException;
+ Path uploadPlugin(MultipartFile pluginFile) throws Exception;
/**
* 上传插件并启用插件。只适用于生产环境
* @param pluginFile 配置文件
* @return 成功返回true.不成功返回false, 或者抛出异常
- * @throws PluginPlugException 插件插头异常
+ * @throws Exception 插件插头异常
*/
- boolean uploadPluginAndStart(MultipartFile pluginFile) throws PluginPlugException;
+ boolean uploadPluginAndStart(MultipartFile pluginFile) throws Exception;
/**
* 上传配置文件(如果存在, 则覆盖)。只适用于生产环境
* @param configFile 配置文件
* @return 成功返回true.不成功返回false, 或者抛出异常
- * @throws PluginPlugException 插件插头异常
+ * @throws Exception 插件插头异常
*/
- boolean uploadConfigFile(MultipartFile configFile) throws PluginPlugException;
+ boolean uploadConfigFile(MultipartFile configFile) throws Exception;
/**
* 通过路径备份插件文件。只适用于生产环境
* @param path 路径
* @param appendName 追加的名称
* @return 成功返回true.不成功返回false, 或者抛出异常
- * @throws PluginPlugException 插件插头异常
+ * @throws Exception 插件插头异常
*/
- boolean backupPlugin(Path path, String appendName) throws PluginPlugException;
+ boolean backupPlugin(Path path, String appendName) throws Exception;
/**
* 通过插件id备份插件。只适用于生产环境
* @param pluginId 插件id
* @param appendName 追加的名称
* @return 成功返回true.不成功返回false, 或者抛出异常
- * @throws PluginPlugException 插件插头异常
+ * @throws Exception 插件插头异常
*/
- boolean backupPlugin(String pluginId, String appendName) throws PluginPlugException;
+ boolean backupPlugin(String pluginId, String appendName) throws Exception;
/**
* 获取插件信息
@@ -139,9 +138,9 @@ public interface PluginOperator {
/**
* 得到插件文件的路径。只适用于生产环境
* @return 返回插件路径列表
- * @throws PluginPlugException 插件插头异常
+ * @throws Exception 插件插头异常
*/
- Set getPluginFilePaths() throws PluginPlugException;
+ Set getPluginFilePaths() throws Exception;
/**
* 得到插件的包装类
diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/AopUtils.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/AopUtils.java
index 127b55c4c45467e00f92829ccd28120d7565dcfe..def4cbfd5df7e5dbe4df81830e59d084db697b00 100644
--- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/AopUtils.java
+++ b/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/AopUtils.java
@@ -12,6 +12,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* AOP 无法找到插件类的解决工具类
@@ -23,6 +24,8 @@ public class AopUtils {
private static final Logger LOG = LoggerFactory.getLogger(AopUtils.class);
+ private static AtomicBoolean isRecover = new AtomicBoolean(true);
+
private static final List PROXY_WRAPPERS = new ArrayList<>();
private AopUtils(){}
@@ -57,6 +60,10 @@ public class AopUtils {
LOG.warn("ProxyProcessorSupports is empty, And Plugin AOP can't used");
return;
}
+ if(!isRecover.get()){
+ throw new RuntimeException("Not invoking resolveAop(). And can not AopUtils.resolveAop");
+ }
+ isRecover.set(false);
ClassLoader pluginClassLoader = pluginWrapper.getPluginClassLoader();
for (ProxyWrapper proxyWrapper : PROXY_WRAPPERS) {
ProxyProcessorSupport proxyProcessorSupport = proxyWrapper.getProxyProcessorSupport();
@@ -77,6 +84,7 @@ public class AopUtils {
ProxyProcessorSupport proxyProcessorSupport = proxyWrapper.getProxyProcessorSupport();
proxyProcessorSupport.setProxyClassLoader(proxyWrapper.getOriginalClassLoader());
}
+ isRecover.set(true);
}
/**
@@ -146,6 +154,14 @@ public class AopUtils {
void setOriginalClassLoader(ClassLoader originalClassLoader) {
this.originalClassLoader = originalClassLoader;
}
+
+ @Override
+ public String toString() {
+ return "ProxyWrapper{" +
+ "proxyProcessorSupport=" + proxyProcessorSupport +
+ ", originalClassLoader=" + originalClassLoader +
+ '}';
+ }
}
}
diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/PluginFileUtils.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/PluginFileUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..865a73540e578c9718e5bfb03a9123a6cff6b70a
--- /dev/null
+++ b/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/PluginFileUtils.java
@@ -0,0 +1,97 @@
+package com.gitee.starblues.utils;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.filefilter.IOFileFilter;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.MessageDigest;
+
+/**
+ * 插件文件工具类
+ *
+ * @author zhangzhuo
+ * @version 1.0
+ */
+public final class PluginFileUtils {
+
+ private PluginFileUtils(){}
+
+
+ public static String getMd5ByFile(File file) throws FileNotFoundException {
+ String value = null;
+ FileInputStream in = new FileInputStream(file);
+ try {
+ MappedByteBuffer byteBuffer = in.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length());
+ MessageDigest md5 = MessageDigest.getInstance("MD5");
+ md5.update(byteBuffer);
+ BigInteger bi = new BigInteger(1, md5.digest());
+ value = bi.toString(16);
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ if(null != in) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ return value;
+ }
+
+
+ public static void cleanEmptyFile(Path path){
+ if(path == null){
+ return;
+ }
+ if(!Files.exists(path)){
+ return;
+ }
+ try {
+ Files.list(path)
+ .forEach(subPath -> {
+ File file = subPath.toFile();
+ if(!file.isFile()){
+ return;
+ }
+ long length = file.length();
+ if(length == 0){
+ FileUtils.deleteQuietly(file);
+ }
+ });
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+
+
+ /**
+ * 得到存在的文件
+ * @param path 插件路径
+ * @return 插件路径
+ * @throws IOException 没有发现文件异常
+ */
+ public static Path getExistPath(Path path) throws IOException {
+ Path parent = path.getParent();
+ if(!Files.exists(parent)){
+ Files.createDirectories(parent);
+ }
+ if(!Files.exists(path)){
+ Files.createFile(path);
+ }
+ return path;
+ }
+
+
+}