diff --git a/LICENSE b/LICENSE index 261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64..7fd89d0cd9f6534504279ae8b6bace45eb517f10 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright [2019-2022] [starBlues] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index c9ca3a728a03680b12fc2827366c218ed3deceae..b7c06b894456f599f8090ec56b522844fa79a7a0 100644 --- a/README.md +++ b/README.md @@ -1,53 +1,29 @@ # SpringBoot Plugin Development Framework -### Introduce -This framework can develop plugins for extension projects on the `SpringBoot` project, -and can define extension functions such as interface, static file and `Mybatis-xml` separately in the plugin module. - -### Core -- Based on `Pf4j` development. -- Plugin configuration plug in SpringBoot project. -- Plugin development can be carried out on springboot with strong expansibility. -Different plugins can be developed for different projects and different plugin jar packages can be deployed. -- You can specify whether to enable or disable plugins through the configuration file, and you can specify the order in which plugins are started. -- It supports uploading plugins and plugin configuration files to the server, and dynamically deploying and updating plugins without restarting the main program. -- It supports viewing plugin operation status and plugin installation location. -- Without restarting the main program, dynamically install, uninstall, enable, stop, backup and delete plugins through the interface. -- On the plugin application module, `spring annotations` can be used to define components for dependency injection. -- Supports the development of `Http-Restful` interfaces in plugins -- Support to define persistence layer access and other requirements separately in the plugin. -- You can follow the plugin interface provided by the main program to develop any extended functions. -- It supports annotation for any business scenario extension, and uses defined coordinates for scenario hit. -- Plugin can customize independent configuration files according to the production and development environment. Currently only `YML files` are supported. -- It supports user-defined extension development interface, and users can extend additional functions on the reserved interface. -- Supports method calls between plugins. -- Supporting plugin http interface documentation: `Swagger`、`SpringDoc`。 -- The plugin supports custom development of the 'Spring-MVC interceptor`. -- The plugin supports 'Spring-Aop'. - -### Expansion pack -#### `SpringBoot-Mybatis` Expansion - -1. Supports customizing the `mapper` interface, `mapper XML` and the corresponding entity `Bean` in the plugin -2. Support integration `Mybatis`、`Mybatis-Plus`、`Tk-Mybatis` -3. Supports independent configuration of `DataSources` in plugin - -- See details: [SpringBoot Mybatis Expansion](http://www.starblues.cn/extension-doc/Mybatis%E6%89%A9%E5%B1%95.html) - -#### `Static resource access` Extension - -1. Supports accessing static resources in plugins through HTTP -2. Support `Thymeleaf` template engine for interface plugin development +- A new `3.0.0' version is available, a framework for dynamically extending the system. -- See details: [Static Resource Access Extension](http://www.starblues.cn/extension-doc/%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90%E8%AE%BF%E9%97%AE%E6%89%A9%E5%B1%95.html) - -#### `Plugin Log` Extension - -1. The plugin can independently configure the integrated `logback` log -2. The plugin can independently configure the integrated `log4j2` log - -- See details: [Plugin Log Extension](http://www.starblues.cn/extension-doc/Log%E6%89%A9%E5%B1%95.html) -- Thank for [sousouki](https://gitee.com/caoshx_sousouki) development +### Introduce +The framework can be developed in the `SpringBoot` project plugin, plugin can be used in exactly the same way as `SpringBoot`. Using this framework you can achieve the following requirements: + +- In the plugin, you can develop as a mini `spring-boot` project, which is easy to use. +- The extension of various function points in the system in plugin, for flexible system expansion, no longer need to use branches to deliver different requirements of the project. +- Various frameworks and their various `spring-boot-XXX-starter` can be integrated in plugin. +- Independent dependencies can now be defined in plugin, not in the main program. +- Can perfectly solve the plugin package and plugin package, plugin package and the main program because of the same framework of different versions of the conflict problem. Individual plugin can define different versions of the framework for the same dependency. +- Without the need to restart the main program, you can freely realize the dynamic installation and deployment of the plugin package, to dynamically expand the function of the system. +- Plugin can also integrate microservice modules independently of the main program. +- And so on, you can imagine what pressing needs and extensions this framework brings to you. +### Features +1. It simplifies the integration steps of the framework and makes it easier to get started. +2. Plugin development is closer to `spring-boot` native development. +3. Package plugin with `Maven` to support independent package compilation of plugins. Current support: +- Dev packaging: Package the plugin as a development environment plugin (packaged only once). +- Prod packaging: Package plug-ins into a `jar`, `zip`, `folder`, etc. +4. Plugin support two modes of operation: +- Plugin mode: As a plugin, it is bootloaded by the main program. +- Autonomous boot mode: Starts as a separate `SpringBoot` project。 +5. Self-developed class loader, support plugin definition of a variety of dependent 'jar' packages. +6. Various frameworks and their various `spring-boot-XXX-starter` can be integrated in plugins, such as integration: `mybatis`、`mybatis-plus`、`spring-jpa` etc. ### Environment 1. jdk1.8+d @@ -60,7 +36,7 @@ Different plugins can be developed for different projects and different plugin j ### Doc -- [http://www.starblues.cn/](http://www.starblues.cn/) +- [https://www.yuque.com/starblues/iuyk4y/ypurcw](https://www.yuque.com/starblues/iuyk4y/ypurcw) ### Derivative products #### Background management system with front and rear plugin function @@ -74,9 +50,4 @@ Different plugins can be developed for different projects and different plugin j ### Contact QQ: 859570617(**After you like the framework, you can enter the group. Please note the Gitee/GitHub nickname before entering the group**) -### Framework maintenance -**Welcome all development enthusiasts to participate in the maintenance and extension development of the framework** - -See for extended development documents: - [springboot-plugin-framework Extended](http://www.starblues.cn/extension-doc/%E8%87%AA%E5%AE%9A%E4%B9%89%E6%89%A9%E5%B1%95.html) - diff --git a/README_zh.md b/README_zh.md index b5bb0192a5f4dcea7739c8f5025fde87f3d41408..7b14600a2608a4fb2278163715df4ae49e67d42a 100644 --- a/README_zh.md +++ b/README_zh.md @@ -1,51 +1,43 @@ # springboot插件式开发框架 -### 介绍 -此框架可在`SpringBoot`项目上开发出用于扩展项目的插件,可在插件模块中单独定义接口、静态文件、`Mybatis-xml`等扩展功能。 - -### 核心功能 -- 基于`Pf4j`开发。 -- 插件配置式插拔于`SpringBoot`项目。 -- 在`SpringBoot`上可以进行插件式开发, 扩展性极强, 可以针对不同项目开发不同插件, 进行不同插件`jar`包的部署。 -- 可通过配置文件指定要启用或者禁用插件, 可以指定启动插件顺序。 -- 支持上传插件和插件配置文件到服务器, 并且无需重启主程序, 动态部署插件、更新插件。 -- 支持查看插件运行状态, 查看插件安装位置。 -- 无需重启主程序, 通过接口动态的安装插件、卸载插件、启用插件、停止插件、备份插件、删除插件。 -- 在插件应用模块上可以使用`Spring`注解定义组件, 进行依赖注入。 -- 支持在插件中开发`Http-Restful`接口。 -- 支持在插件中单独定义持久层访问等需求。 -- 可以遵循主程序提供的插件接口开发任意扩展功能。 -- 支持注解进行任意业务场景扩展, 并使用定义的坐标进行场景命中。 -- 插件可以根据生产和开发环境自定义独立的配置文件。目前只支持yml文件。 -- 支持自定义扩展开发接口, 使用者可以在预留接口上扩展额外功能。 -- 支持插件之间的方法调用。 -- 支持插件`Http`接口文档: `Swagger`、`SpringDoc`。 -- 插件支持`拦截器`的定制开发。 -- 插件对`Spring-Aop`进行支持。 - -### 扩展包功能 -#### `SpringBoot-Mybatis`扩展包 - -1. 支持在插件中自定义`Mapper`接口、`Mapper xml` 以及对应的实体`bean` -2. 支持集成`Mybatis`、`Mybatis-Plus`、`Tk-Mybatis` -3. 支持可在插件中独立配置数据源 - -- 详见 [插件SpringBoot Mybatis扩展](http://www.starblues.cn/extension-doc/Mybatis%E6%89%A9%E5%B1%95.html) - -#### 静态资源访问扩展包 - -1. 支持通过http访问插件中静态资源 -2. 支持`Thymeleaf`模板引擎进行界面插件式开发 +- 全新`3.0.0`版本上线啦,为动态扩展系统而生的框架。 -- 详见 [插件静态资源访问扩展](http://www.starblues.cn/extension-doc/%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90%E8%AE%BF%E9%97%AE%E6%89%A9%E5%B1%95.html) +### 背景 +在当下后端市场,还是以`SpringBoot`为核心框架进行系统开发,本框架可以在`SpringBoot`系统上进行插件式的开发,将插件当做一个`mini`版本的`SpringBoot`进行系统扩展开发,可以解决如下痛点: +1. 在`To-B`系统场景中,不同甲方会有不同的需求,在不打分支和改动系统核心代码的前提下,可以在插件中进行扩展开发特定功能,不同甲方使用不同插件,完美解决非核心系统的扩展功能开发。 +2. 在`To-C`系统场景中,可以在主程序通过定义`java-interface`,在插件中做不同实现,来达到动态扩展系统功能。 +3. 在开发中,由于引入了不同版本的依赖,导致系统无法运行,本框架可以完美解决在不同插件中定义不同版本的依赖,从底层进行隔离,以解决引入不同版本依赖冲突的问题。比如可以解决同一个程序同时连接`mysql-5`和`mysql-8`版本数据库。 +4. 在开发中,不同插件依赖不同框架的功能,可以按需引入。比如在插件A引入连接`mysql`、在插件B引入连接`elasticsearch`、在插件C引入连接`oracle`。 +5. 在插件中,可以任意集成不同的非`web`类型的`springboot-xx-starter`,然后将不同插件功能组装起来,以达到一个统一对外提供服务的完整系统,实现系统组装化、插拔化开发。 +6. 在不重启主程序的前提下,对插件进行动态的安装、卸载。 -#### 插件日志扩展包 - -1. 插件可自主配置集成`logback`日志 -2. 插件可自主配置集成`log4j2`日志 - -- 详见 [插件静态资源访问扩展](http://www.starblues.cn/extension-doc/Log%E6%89%A9%E5%B1%95.html) -- 感谢 [sousouki](https://gitee.com/caoshx_sousouki) 的开发提供 +### 介绍 +该框架可以在`SpringBoot`项目上开发出插件功能,在插件中可以和`SpringBoot`使用方式一模一样。使用了本框架您可以实现如下需求: + +- 在插件中,您可以当成一个微型的`Spring-Boot`项目来开发,简单易用。 +- 在插件中扩展出系统各种功能点,用于系统灵活扩展,再也不用使用分支来交付不同需求的项目了。 +- 在插件中可以集成各种框架及其各种`spring-boot-xxx-starter`。 +- 在插件中可以定义独立依赖包了,再也不用在主程序中定义依赖包了。 +- 可以完美解决插件包与插件包、插件包与主程序因为同一框架的不同版本冲突问题了。各个插件可以定义同一依赖的不同版本框架。 +- 无需重启主程序,可以自由实现插件包的动态安装部署,来动态扩展系统的功能。 +- 插件也可以不依赖主程序独立集成微服务模块。 +- 您可以丰富想象该框架给您带来哪些迫切的需求和扩展。 + +### 特性 +1. 简化了框架的集成步骤,更容易上手。 +2. 插件开发更加贴近`spring-boot`原生开发。 +3. 使用`Maven`打包插件,支持对插件的自主打包编译。目前支持: + + 开发打包:将插件打包成开发环境下的插件(仅需打包一次)。 + + 生产打包:将插件打包成一个`jar`、`zip`、`文件夹`等。 +4. 插件支持两种运行模式 + + 插件模式: 作为一个插件,由主程序引导加载。 + + 自主启动模式:单独作为一个`SpringBoot`项目来启动。 +5. 自主的开发的类加载器,支持插件定义各种的依赖`jar`包。 +6. 在插件中可以集成各种框架及其各种`spring-boot-xxx-starter`,比如集成`mybatis`、`mybatis-plus`、`spring-jpa`等。 ### 运行环境 1. jdk1.8+ @@ -58,7 +50,7 @@ ### 文档地址 -- [http://www.starblues.cn/](http://www.starblues.cn/) +- [https://www.yuque.com/starblues/springboot-plugin-framework-v3.0.0](https://www.yuque.com/starblues/springboot-plugin-framework-v3.0.0) ### 衍生产品 #### 携带前后端插件功能的后台管理系统 @@ -70,11 +62,4 @@ - [springboot-plugin-framework 功能测试+案例](https://gitee.com/starblues/springboot-plugin-framework-example) ### QQ交流群 -859570617(**点赞框架后可进群, 进群前请备注gitee昵称**) - -### 框架维护 -**欢迎各位开发爱好者参与到框架的维护和扩展开发当中** - -扩展开发文档见: - [springboot-plugin-framework 扩展功能](http://www.starblues.cn/extension-doc/%E8%87%AA%E5%AE%9A%E4%B9%89%E6%89%A9%E5%B1%95.html) - - +859570617(**点赞框架后可进群, 进群前请备注gitee昵称**) \ No newline at end of file diff --git a/pom.xml b/pom.xml index c4167139218a22121f9c3863683b944bf626531b..4847d111bc7296a71288fe4cc467b8cd5b75f303 100644 --- a/pom.xml +++ b/pom.xml @@ -4,17 +4,154 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 + + org.sonatype.oss + oss-parent + 7 + + com.gitee.starblues - springboot-plugin-framework-parent + spring-brick-parent pom - 2.4.6-RELEASE - - spring boot 插件开发集成包 + 3.0.0 - springboot-plugin-framework - springboot-plugin-framework-extension + spring-brick-common + spring-brick-loader + spring-brick + spring-brick-bootstrap + spring-brick-maven-packager - + + + 1.8 + UTF-8 + 3.8.1 + + 3.1.0 + 3.1.0 + 3.1.0 + 1.6 + + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + https://gitee.com/starblues/springboot-plugin-framework-parent + scm:https://gitee.com/starblues/springboot-plugin-framework-parent.git + scm:https://gitee.com/starblues/springboot-plugin-framework-parent.git + 1.0 + + + + + sonatype-nexus-snapshots + oss Snapshots Repository + https://oss.sonatype.org/content/repositories/snapshots + + + sonatype-nexus-staging + oss Staging Repository + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + StarBlues + starblues@foxmail.com + https://gitee.com/starblues/ + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + + + + + org.apache.maven.plugins + maven-source-plugin + ${maven-source-plugin.version} + + + package + + jar-no-fork + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven-javadoc-plugin.version} + + ${plugin.skip} + + + + package + + jar + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + ${maven-gpg-plugin.version} + + ${plugin.skip} + + + + sign-artifacts + verify + + sign + + + + + + + + + + + dev + + true + + + true + + + + + release + + false + + + + \ No newline at end of file diff --git a/spring-brick-bootstrap/pom.xml b/spring-brick-bootstrap/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..45d8b2eae1672249fe32db34623a6f63f4327cd0 --- /dev/null +++ b/spring-brick-bootstrap/pom.xml @@ -0,0 +1,88 @@ + + + 4.0.0 + + + spring-brick-parent + com.gitee.starblues + 3.0.0 + + + spring-brick-bootstrap + jar + + 插件引导模块 + + + 1.8 + UTF-8 + 3.8.1 + + 3.0.0 + + 1.9.6 + 1.7.7 + 2.11.3 + + 2.5.6 + 5.3.2 + 4.0.1 + 1.6.5 + + + + + org.aspectj + aspectjweaver + ${aspectj.version} + + + org.slf4j + slf4j-api + ${slf4j.version} + + + com.gitee.starblues + spring-brick + ${project.version} + provided + true + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + org.springframework.boot + spring-boot + ${spring-boot.version} + provided + true + + + org.springframework + spring-webmvc + ${spring.version} + provided + true + + + org.springdoc + springdoc-openapi-common + ${springdoc.version} + provided + true + + + javax.servlet + javax.servlet-api + ${javax.servlet-api.version} + provided + true + + + + \ No newline at end of file diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/ConfigureMainPluginEnvironment.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/ConfigureMainPluginEnvironment.java new file mode 100644 index 0000000000000000000000000000000000000000..c2e0080564535f9d08d943077d88ce6882844b22 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/ConfigureMainPluginEnvironment.java @@ -0,0 +1,107 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap; + +import com.gitee.starblues.bootstrap.annotation.OneselfConfig; +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.utils.AnnotationUtils; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.ResourceUtils; +import org.springframework.boot.env.PropertiesPropertySourceLoader; +import org.springframework.boot.env.PropertySourceLoader; +import org.springframework.boot.env.YamlPropertySourceLoader; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; +import org.springframework.util.ClassUtils; + +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; + +/** + * 插件环境配置 + * @author starBlues + * @version 3.0.0 + */ +class ConfigureMainPluginEnvironment { + + private final ProcessorContext processorContext; + private final List propertySourceLoaders; + + ConfigureMainPluginEnvironment(ProcessorContext processorContext) { + this.processorContext = processorContext; + + this.propertySourceLoaders = new ArrayList<>(2); + this.propertySourceLoaders.add(new YamlPropertySourceLoader()); + this.propertySourceLoaders.add(new PropertiesPropertySourceLoader()); + } + + void configureEnvironment(ConfigurableEnvironment environment, String[] args) { + SpringPluginBootstrap springPluginBootstrap = processorContext.getSpringPluginBootstrap(); + OneselfConfig oneselfConfig = AnnotationUtils.findAnnotation(springPluginBootstrap.getClass(), + OneselfConfig.class); + if(oneselfConfig == null){ + return; + } + String[] mainConfigFileName = oneselfConfig.mainConfigFileName(); + if(mainConfigFileName.length == 0){ + return; + } + for (String fileName : mainConfigFileName) { + load(environment, fileName); + } + } + + + private void load(ConfigurableEnvironment environment, String fileName){ + String fileSuffix = ResourceUtils.getFileSuffix(fileName); + if(ObjectUtils.isEmpty(fileSuffix)){ + return; + } + PropertySourceLoader sourceLoader = null; + for (PropertySourceLoader propertySourceLoader : propertySourceLoaders) { + String[] fileExtensions = propertySourceLoader.getFileExtensions(); + for (String fileExtension : fileExtensions) { + if(fileSuffix.equalsIgnoreCase(fileExtension)){ + sourceLoader = propertySourceLoader; + break; + } + } + } + if(sourceLoader == null){ + return; + } + URL url = Objects.requireNonNull(ClassUtils.getDefaultClassLoader()).getResource(fileName); + if(url == null){ + return; + } + try { + Path path = Paths.get(url.toURI()); + Resource resource = new FileSystemResource(path); + List> propertySources = sourceLoader.load(fileName, resource); + for (PropertySource propertySource : propertySources) { + environment.getPropertySources().addFirst(propertySource); + } + } catch (Exception e){ + throw new RuntimeException(e); + } + } + +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/ConfigurePluginEnvironment.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/ConfigurePluginEnvironment.java new file mode 100644 index 0000000000000000000000000000000000000000..c159c6e9b470648b34f8885e72c18d9f2c539328 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/ConfigurePluginEnvironment.java @@ -0,0 +1,95 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap; + +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.integration.AutoIntegrationConfiguration; +import com.gitee.starblues.utils.Assert; +import com.gitee.starblues.utils.FilesUtils; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.PluginFileUtils; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +/** + * 插件环境配置 + * @author starBlues + * @version 3.0.0 + */ +class ConfigurePluginEnvironment { + + private final static String PLUGIN_PROPERTY_NAME = "pluginPropertySources"; + + private final static String SPRING_CONFIG_NAME = "spring.config.name"; + private final static String SPRING_CONFIG_LOCATION = "spring.config.location"; + + private final static String SPRING_JMX_UNIQUE_NAMES = "spring.jmx.unique-names"; + private final static String SPRING_ADMIN_JMX_NAME = "spring.application.admin.jmx-name"; + private final static String SPRING_ADMIN_JMX_VALUE = "org.springframework.boot:type=Admin,name="; + + public static final String REGISTER_SHUTDOWN_HOOK_PROPERTY = "logging.register-shutdown-hook"; + public static final String MBEAN_DOMAIN_PROPERTY_NAME = "spring.liveBeansView.mbeanDomain"; + + private final ProcessorContext processorContext; + private final InsidePluginDescriptor pluginDescriptor; + + ConfigurePluginEnvironment(ProcessorContext processorContext) { + this.processorContext = Assert.isNotNull(processorContext, "processorContext 不能为空"); + this.pluginDescriptor = Assert.isNotNull(processorContext.getPluginDescriptor(), + "pluginDescriptor 不能为空"); + } + + void configureEnvironment(ConfigurableEnvironment environment, String[] args) { + Map env = new HashMap<>(); + String pluginId = pluginDescriptor.getPluginId(); + String configFileName = pluginDescriptor.getConfigFileName(); + if(!ObjectUtils.isEmpty(configFileName)){ + env.put(SPRING_CONFIG_NAME, PluginFileUtils.getFileName(configFileName)); + } + String configFileLocation = pluginDescriptor.getConfigFileLocation(); + if(!ObjectUtils.isEmpty(configFileLocation)){ + env.put(SPRING_CONFIG_LOCATION, getConfigFileLocation(configFileLocation)); + } + env.put(AutoIntegrationConfiguration.ENABLE_STARTER_KEY, false); + env.put(SPRING_JMX_UNIQUE_NAMES, true); + env.put(SPRING_ADMIN_JMX_NAME, SPRING_ADMIN_JMX_VALUE + pluginId); + env.put(REGISTER_SHUTDOWN_HOOK_PROPERTY, false); + env.put(MBEAN_DOMAIN_PROPERTY_NAME, pluginId); + environment.getPropertySources().addFirst(new MapPropertySource(PLUGIN_PROPERTY_NAME, env)); + + if(processorContext.runMode() == ProcessorContext.RunMode.ONESELF){ + ConfigureMainPluginEnvironment configureMainPluginEnvironment = + new ConfigureMainPluginEnvironment(processorContext); + configureMainPluginEnvironment.configureEnvironment(environment, args); + } + } + + private String getConfigFileLocation(String configFileLocation){ + String s = FilesUtils.resolveRelativePath(new File("").getAbsolutePath(), configFileLocation); + if(s.endsWith("/") || s.endsWith(File.separator)){ + return s; + } else { + return s + File.separator; + } + } + +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/DefaultSpringPluginHook.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/DefaultSpringPluginHook.java new file mode 100644 index 0000000000000000000000000000000000000000..7e47ff442de99e8e45e2ca9ae8d38b443e44cbf8 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/DefaultSpringPluginHook.java @@ -0,0 +1,124 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap; + +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; +import com.gitee.starblues.bootstrap.processor.web.thymeleaf.PluginThymeleafProcessor; +import com.gitee.starblues.bootstrap.realize.PluginCloseListener; +import com.gitee.starblues.bootstrap.realize.StopValidator; +import com.gitee.starblues.bootstrap.utils.DestroyUtils; +import com.gitee.starblues.bootstrap.utils.SpringBeanUtils; +import com.gitee.starblues.core.exception.PluginProhibitStopException; +import com.gitee.starblues.spring.ApplicationContext; +import com.gitee.starblues.spring.ApplicationContextProxy; +import com.gitee.starblues.spring.SpringPluginHook; +import com.gitee.starblues.spring.WebConfig; +import com.gitee.starblues.spring.web.thymeleaf.ThymeleafConfig; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.io.support.SpringFactoriesLoader; + +import java.util.List; +import java.util.Map; + +/** + * 默认的插件钩子器 + * @author starBlues + * @version 3.0.0 + */ +public class DefaultSpringPluginHook implements SpringPluginHook { + + private final SpringPluginProcessor pluginProcessor; + private final ProcessorContext processorContext; + private final StopValidator stopValidator; + + public DefaultSpringPluginHook(SpringPluginProcessor pluginProcessor, + ProcessorContext processorContext) { + this.pluginProcessor = pluginProcessor; + this.processorContext = processorContext; + this.stopValidator = SpringBeanUtils.getExistBean(processorContext.getApplicationContext(), + StopValidator.class); + } + + /** + * 先校验是否可卸载 + */ + @Override + public void stopVerify() { + if(stopValidator == null){ + return; + } + try { + StopValidator.Result result = stopValidator.verify(); + if(result != null && !result.isVerify()){ + throw new PluginProhibitStopException(processorContext.getPluginDescriptor(), + result.getMessage()); + } + } catch (Exception e){ + throw new PluginProhibitStopException(processorContext.getPluginDescriptor(), + e.getMessage()); + } + } + + + @Override + public void close() throws Exception{ + try { + GenericApplicationContext applicationContext = processorContext.getApplicationContext(); + callPluginCloseListener(applicationContext); + pluginProcessor.close(processorContext); + if(applicationContext != null){ + applicationContext.close(); + } + processorContext.clearRegistryInfo(); + DestroyUtils.destroyAll(null, SpringFactoriesLoader.class, "cache", Map.class); + } catch (Exception e){ + e.printStackTrace(); + } + } + + @Override + public ApplicationContext getApplicationContext() { + return new ApplicationContextProxy(processorContext.getApplicationContext().getBeanFactory()); + } + + @Override + public WebConfig getWebConfig() { + return processorContext.getWebConfig(); + } + + @Override + public ThymeleafConfig getThymeleafConfig() { + return processorContext.getRegistryInfo(PluginThymeleafProcessor.CONFIG_KEY); + } + + private void callPluginCloseListener(GenericApplicationContext applicationContext){ + List pluginCloseListeners = SpringBeanUtils.getBeans( + applicationContext, PluginCloseListener.class); + if(pluginCloseListeners.isEmpty()){ + return; + } + for (PluginCloseListener pluginCloseListener : pluginCloseListeners) { + try { + pluginCloseListener.close(processorContext.getPluginDescriptor()); + } catch (Exception e){ + e.printStackTrace(); + } + } + } + +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/EmptyMainApplicationContext.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/EmptyMainApplicationContext.java new file mode 100644 index 0000000000000000000000000000000000000000..53a55368e9971b990a1ed3c039d4ede5e69f721f --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/EmptyMainApplicationContext.java @@ -0,0 +1,54 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap; + +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.spring.SpringBeanFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.core.ResolvableType; + +import java.lang.annotation.Annotation; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +/** + * 空的MainApplicationContext实现 + * @author starBlues + * @version 3.0.0 + */ +public class EmptyMainApplicationContext implements MainApplicationContext { + + private final SpringBeanFactory springBeanFactory = new EmptySpringBeanFactory(); + + @Override + public SpringBeanFactory getSpringBeanFactory() { + return springBeanFactory; + } + + @Override + public void close() throws Exception { + + } + + @Override + public Map> getConfigurableEnvironment() { + return Collections.emptyMap(); + } +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/EmptySpringBeanFactory.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/EmptySpringBeanFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..b784bd5503633f70836893593d9e9ce8a399a855 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/EmptySpringBeanFactory.java @@ -0,0 +1,178 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap; + +import com.gitee.starblues.spring.SpringBeanFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.core.ResolvableType; + +import java.lang.annotation.Annotation; +import java.util.Map; + +/** + * 空的 SpringBeanFactory 实现 + * @author starBlues + * @version 3.0.0 + */ +public class EmptySpringBeanFactory implements SpringBeanFactory { + @Override + public boolean containsBeanDefinition(String beanName) { + return false; + } + + @Override + public int getBeanDefinitionCount() { + return 0; + } + + @Override + public String[] getBeanDefinitionNames() { + return new String[0]; + } + + @Override + public ObjectProvider getBeanProvider(Class requiredType, boolean allowEagerInit) { + return null; + } + + @Override + public ObjectProvider getBeanProvider(ResolvableType requiredType, boolean allowEagerInit) { + return null; + } + + @Override + public String[] getBeanNamesForType(ResolvableType type) { + return new String[0]; + } + + @Override + public String[] getBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) { + return new String[0]; + } + + @Override + public String[] getBeanNamesForType(Class type) { + return new String[0]; + } + + @Override + public String[] getBeanNamesForType(Class type, boolean includeNonSingletons, boolean allowEagerInit) { + return new String[0]; + } + + @Override + public Map getBeansOfType(Class type) throws BeansException { + return null; + } + + @Override + public Map getBeansOfType(Class type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException { + return null; + } + + @Override + public String[] getBeanNamesForAnnotation(Class annotationType) { + return new String[0]; + } + + @Override + public Map getBeansWithAnnotation(Class annotationType) throws BeansException { + return null; + } + + @Override + public A findAnnotationOnBean(String beanName, Class annotationType) throws NoSuchBeanDefinitionException { + return null; + } + + @Override + public Object getBean(String name) throws BeansException { + return null; + } + + @Override + public T getBean(String name, Class requiredType) throws BeansException { + return null; + } + + @Override + public Object getBean(String name, Object... args) throws BeansException { + return null; + } + + @Override + public T getBean(Class requiredType) throws BeansException { + return null; + } + + @Override + public T getBean(Class requiredType, Object... args) throws BeansException { + return null; + } + + @Override + public ObjectProvider getBeanProvider(Class requiredType) { + return null; + } + + @Override + public ObjectProvider getBeanProvider(ResolvableType requiredType) { + return null; + } + + @Override + public boolean containsBean(String name) { + return false; + } + + @Override + public boolean isSingleton(String name) throws NoSuchBeanDefinitionException { + return false; + } + + @Override + public boolean isPrototype(String name) throws NoSuchBeanDefinitionException { + return false; + } + + @Override + public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException { + return false; + } + + @Override + public boolean isTypeMatch(String name, Class typeToMatch) throws NoSuchBeanDefinitionException { + return false; + } + + @Override + public Class getType(String name) throws NoSuchBeanDefinitionException { + return null; + } + + @Override + public Class getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException { + return null; + } + + @Override + public String[] getAliases(String name) { + return new String[0]; + } +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginApplicationContext.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginApplicationContext.java new file mode 100644 index 0000000000000000000000000000000000000000..2ed37d010bff52beae6cdfe2b5745d590b5a3e35 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginApplicationContext.java @@ -0,0 +1,60 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap; + +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +/** + * 插件ApplicationContext实现 + * @author starBlues + * @version 3.0.0 + */ +public class PluginApplicationContext extends AnnotationConfigApplicationContext { + + private final PluginDescriptor pluginDescriptor; + + public PluginApplicationContext(DefaultListableBeanFactory beanFactory, + ProcessorContext processorContext) { + super(beanFactory); + setResourceLoader(processorContext.getResourceLoader()); + this.pluginDescriptor = processorContext.getPluginDescriptor(); + } + + @Override + public void registerShutdownHook() { + // 忽略 + } + + @Override + public String getApplicationName() { + return pluginDescriptor.getPluginId(); + } + + @Override + public void refresh() throws BeansException, IllegalStateException { + super.refresh(); + } + + @Override + public void close() { + super.close(); + } +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginListableBeanFactory.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginListableBeanFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..fda812953dfe96df79455ce6c412c62db5e3777f --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginListableBeanFactory.java @@ -0,0 +1,100 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap; + +import com.gitee.starblues.bootstrap.utils.DestroyUtils; +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.spring.SpringBeanFactory; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.ReflectionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.TypeConverter; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.DependencyDescriptor; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.lang.Nullable; + +import java.util.Set; + +/** + * 插件BeanFactory实现 + * @author starBlues + * @version 3.0.0 + */ +public class PluginListableBeanFactory extends DefaultListableBeanFactory { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private final MainApplicationContext applicationContext; + + public PluginListableBeanFactory(MainApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + @Override + public Object resolveDependency(DependencyDescriptor descriptor, + @Nullable String requestingBeanName, + @Nullable Set autowiredBeanNames, + @Nullable TypeConverter typeConverter) throws BeansException { + + try { + return super.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); + } catch (BeansException e){ + return resolveDependencyFromMain(descriptor); + } + } + + private Object resolveDependencyFromMain(DependencyDescriptor descriptor){ + String dependencyName = descriptor.getDependencyName(); + SpringBeanFactory springBeanFactory = applicationContext.getSpringBeanFactory(); + if(!ObjectUtils.isEmpty(dependencyName) && springBeanFactory.containsBean(dependencyName)){ + return springBeanFactory.getBean(dependencyName); + } else { + try { + return springBeanFactory.getBean(descriptor.getDependencyType()); + } catch (Exception e){ + throw new NoSuchBeanDefinitionException(descriptor.getDependencyType()); + } + } + } + + @Override + public void destroySingletons() { + String[] beanDefinitionNames = getBeanDefinitionNames(); + for (String beanDefinitionName : beanDefinitionNames) { + destroyBean(beanDefinitionName); + } + super.destroySingletons(); + destroyAll(); + } + + private void destroyAll(){ + ReflectionUtils.findField(this.getClass(), field -> { + field.setAccessible(true); + try { + Object o = field.get(this); + DestroyUtils.destroyAll(o); + } catch (IllegalAccessException e) { + // 忽略 + } + return false; + }); + } + +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginOneselfInteractive.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginOneselfInteractive.java new file mode 100644 index 0000000000000000000000000000000000000000..dfd07faeeb4f2456ea7d0db2b670b93d27474ced --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginOneselfInteractive.java @@ -0,0 +1,95 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap; + +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.core.descriptor.DevPluginDescriptorLoader; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.core.descriptor.PluginDescriptorLoader; +import com.gitee.starblues.core.launcher.plugin.PluginInteractive; +import com.gitee.starblues.integration.AutoIntegrationConfiguration; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.spring.extract.DefaultOpExtractFactory; +import com.gitee.starblues.spring.extract.OpExtractFactory; +import com.gitee.starblues.spring.invoke.DefaultInvokeSupperCache; +import com.gitee.starblues.spring.invoke.InvokeSupperCache; +import com.gitee.starblues.utils.FilesUtils; + +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * 插件自己的Interactive + * @author starBlues + * @version 3.0.0 + */ +public class PluginOneselfInteractive implements PluginInteractive { + + private final InsidePluginDescriptor pluginDescriptor; + private final MainApplicationContext mainApplicationContext; + private final IntegrationConfiguration configuration; + private final InvokeSupperCache invokeSupperCache; + private final OpExtractFactory opExtractFactory; + + public PluginOneselfInteractive(){ + this.pluginDescriptor = createPluginDescriptor(); + this.mainApplicationContext = new EmptyMainApplicationContext(); + this.configuration = new AutoIntegrationConfiguration(); + this.invokeSupperCache = new DefaultInvokeSupperCache(); + this.opExtractFactory = new DefaultOpExtractFactory(); + } + + @Override + public InsidePluginDescriptor getPluginDescriptor() { + return pluginDescriptor; + } + + @Override + public MainApplicationContext getMainApplicationContext() { + return mainApplicationContext; + } + + @Override + public IntegrationConfiguration getConfiguration() { + return configuration; + } + + @Override + public InvokeSupperCache getInvokeSupperCache() { + return invokeSupperCache; + } + + @Override + public OpExtractFactory getOpExtractFactory() { + return opExtractFactory; + } + + private InsidePluginDescriptor createPluginDescriptor(){ + try (PluginDescriptorLoader pluginDescriptorLoader = new DevPluginDescriptorLoader()){ + Path classesPath = Paths.get(this.getClass().getResource("/").toURI()).getParent(); + String metaInf = FilesUtils.joiningFilePath(classesPath.toString(), PackageStructure.META_INF_NAME); + InsidePluginDescriptor pluginDescriptor = pluginDescriptorLoader.load(Paths.get(metaInf)); + if(pluginDescriptor == null){ + throw new RuntimeException("没有发现插件信息, 请使用框架提供的Maven插件器对插件进行编译!"); + } + return pluginDescriptor; + } catch (Exception e){ + throw new RuntimeException(e); + } + } +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginSpringApplication.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginSpringApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..c46e5f620dc5cd40e42cca43844a988b117df61f --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginSpringApplication.java @@ -0,0 +1,120 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap; + +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; +import com.gitee.starblues.spring.ApplicationContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.boot.Banner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.WebApplicationType; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.StandardEnvironment; +import org.springframework.core.io.ResourceLoader; + +/** + * 插件SpringApplication实现 + * @author starBlues + * @version 3.0.0 + */ +public class PluginSpringApplication extends SpringApplication { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private final ProcessorContext.RunMode runMode; + + private final SpringPluginProcessor pluginProcessor; + private final ProcessorContext processorContext; + + private final GenericApplicationContext applicationContext; + + private final DefaultListableBeanFactory beanFactory; + private final ResourceLoader resourceLoader; + private final ConfigurePluginEnvironment configurePluginEnvironment; + + public PluginSpringApplication(SpringPluginProcessor pluginProcessor, + ProcessorContext processorContext, + Class... primarySources) { + super(primarySources); + this.runMode = processorContext.runMode(); + this.pluginProcessor = pluginProcessor; + this.processorContext = processorContext; + this.resourceLoader = processorContext.getResourceLoader(); + this.beanFactory = new PluginListableBeanFactory(processorContext.getMainApplicationContext()); + this.configurePluginEnvironment = new ConfigurePluginEnvironment(processorContext); + this.applicationContext = getApplicationContext(); + setDefaultPluginConfig(); + } + + protected GenericApplicationContext getApplicationContext(){ + if(runMode == ProcessorContext.RunMode.ONESELF){ + return (GenericApplicationContext) super.createApplicationContext(); + } else { + return new PluginApplicationContext(beanFactory, processorContext); + } + } + + public void setDefaultPluginConfig(){ + if(runMode == ProcessorContext.RunMode.PLUGIN){ + setResourceLoader(resourceLoader); + setBannerMode(Banner.Mode.OFF); + setEnvironment(new StandardEnvironment()); + setWebApplicationType(WebApplicationType.NONE); + setRegisterShutdownHook(false); + setLogStartupInfo(false); + } + } + + @Override + protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { + super.configureEnvironment(environment, args); + configurePluginEnvironment.configureEnvironment(environment, args); + } + + @Override + protected ConfigurableApplicationContext createApplicationContext() { + return this.applicationContext; + } + + @Override + public ConfigurableApplicationContext run(String... args) { + try { + processorContext.setApplicationContext(this.applicationContext); + pluginProcessor.initialize(processorContext); + return super.run(args); + } catch (Exception e) { + pluginProcessor.failure(processorContext); + logger.error("启动插件[{}]失败. {}", + processorContext.getPluginDescriptor().getPluginId(), + e.getMessage(), e); + throw new RuntimeException(e); + } + } + + @Override + protected void refresh(ConfigurableApplicationContext applicationContext) { + pluginProcessor.refreshBefore(processorContext); + super.refresh(applicationContext); + pluginProcessor.refreshAfter(processorContext); + } + +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/SpringPluginBootstrap.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/SpringPluginBootstrap.java new file mode 100644 index 0000000000000000000000000000000000000000..b54de8b32a9287fa8f8fda930b130ff5e066d585 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/SpringPluginBootstrap.java @@ -0,0 +1,99 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap; + +import com.gitee.starblues.bootstrap.processor.ComposeSpringPluginProcessor; +import com.gitee.starblues.bootstrap.processor.DefaultProcessorContext; +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; +import com.gitee.starblues.core.launcher.plugin.PluginInteractive; +import com.gitee.starblues.spring.SpringPluginHook; + +import java.util.ArrayList; +import java.util.List; + +/** + * 插件引导抽象类。插件入口需集成本抽象类 + * @author starBlues + * @version 3.0.0 + */ +public abstract class SpringPluginBootstrap { + + private ProcessorContext.RunMode runMode = ProcessorContext.RunMode.ONESELF; + + private volatile PluginInteractive pluginInteractive; + + private final List customPluginProcessors = new ArrayList<>(); + + public final SpringPluginHook run(String[] args){ + return run(this.getClass(), args); + } + + public final SpringPluginHook run(Class primarySources, String[] args){ + return run(new Class[]{ primarySources }, args); + } + + public final SpringPluginHook run(Class[] primarySources, String[] args){ + return start(primarySources, args); + } + + private SpringPluginHook start(Class[] primarySources, String[] args){ + createPluginInteractive(); + addCustomSpringPluginProcessor(); + SpringPluginProcessor pluginProcessor = new ComposeSpringPluginProcessor(runMode, customPluginProcessors); + ProcessorContext processorContext = new DefaultProcessorContext( + runMode, this, pluginInteractive, this.getClass() + ); + PluginSpringApplication springApplication = new PluginSpringApplication( + pluginProcessor, + processorContext, + primarySources); + springApplication.run(args); + return new DefaultSpringPluginHook(pluginProcessor, processorContext); + } + + public final SpringPluginBootstrap setPluginInteractive(PluginInteractive pluginInteractive) { + this.pluginInteractive = pluginInteractive; + this.runMode = ProcessorContext.RunMode.PLUGIN; + return this; + } + + public final SpringPluginBootstrap addSpringPluginProcessor(SpringPluginProcessor springPluginProcessor){ + if(springPluginProcessor != null){ + customPluginProcessors.add(springPluginProcessor); + } + return this; + } + + protected final void createPluginInteractive(){ + if(pluginInteractive != null){ + return; + } + createPluginInteractiveOfOneself(); + } + + protected final void createPluginInteractiveOfOneself(){ + this.pluginInteractive = new PluginOneselfInteractive(); + } + + + /** + * 子类自定义插件 SpringPluginProcessor + */ + protected void addCustomSpringPluginProcessor(){} + +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/annotation/DisablePluginWeb.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/annotation/DisablePluginWeb.java new file mode 100644 index 0000000000000000000000000000000000000000..9a4fba0dab474e6ae6ec8c4fd319ee53d5bb9de8 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/annotation/DisablePluginWeb.java @@ -0,0 +1,31 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.annotation; + +import java.lang.annotation.*; + +/** + * 禁用web环境. 如果该注解加入到入口类上, 表示当前插件禁用web的功能。 + * 包括:controller注册、拦截器注册、web静态资源访问、thymeleaf模板引擎 + * @author starBlues + * @version 3.0.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DisablePluginWeb { +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/annotation/OneselfConfig.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/annotation/OneselfConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..1af29cd5fb749b474a9191dea2c819a852bff514 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/annotation/OneselfConfig.java @@ -0,0 +1,38 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.annotation; + +import java.lang.annotation.*; + +/** + * 插件自主运行配置 + * @author starBlues + * @version 3.0.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface OneselfConfig { + + /** + * 主程序配置文件名称 + * @return String[] + */ + String[] mainConfigFileName() default {}; + + +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ComposeSpringPluginProcessor.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ComposeSpringPluginProcessor.java new file mode 100644 index 0000000000000000000000000000000000000000..05d48476a050b42a822b4235f3971118812427c9 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ComposeSpringPluginProcessor.java @@ -0,0 +1,186 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.processor; + +import com.gitee.starblues.bootstrap.SpringPluginBootstrap; +import com.gitee.starblues.bootstrap.annotation.DisablePluginWeb; +import com.gitee.starblues.bootstrap.processor.web.PluginControllerProcessor; +import com.gitee.starblues.bootstrap.processor.web.PluginInterceptorsProcessor; +import com.gitee.starblues.bootstrap.processor.web.PluginSpringDocControllerProcessor; +import com.gitee.starblues.bootstrap.processor.web.PluginStaticResourceProcessor; +import com.gitee.starblues.bootstrap.processor.web.thymeleaf.PluginThymeleafProcessor; +import com.gitee.starblues.bootstrap.utils.AnnotationUtils; +import com.gitee.starblues.bootstrap.utils.ProcessorUtils; +import com.gitee.starblues.utils.OrderUtils; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.OrderPriority; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 组合的处理器 + * @author starBlues + * @version 3.0.0 + */ +public class ComposeSpringPluginProcessor implements SpringPluginProcessor { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private final ProcessorContext.RunMode runMode; + + private List processors; + + public ComposeSpringPluginProcessor(ProcessorContext.RunMode runMode) { + this(runMode, null); + } + + public ComposeSpringPluginProcessor(ProcessorContext.RunMode runMode, List processors) { + this.runMode = runMode; + if(!ObjectUtils.isEmpty(processors)){ + this.processors = processors; + } else { + this.processors = new ArrayList<>(); + } + } + + public void addSpringPluginProcessor(SpringPluginProcessor springPluginProcessor){ + if(springPluginProcessor != null){ + this.processors.add(springPluginProcessor); + } + } + + @Override + public void initialize(ProcessorContext context) throws ProcessorException { + List processors = new ArrayList<>(); + addDefaultProcessors(context, processors); + addDefaultWebEnvProcessors(context, processors); + processors.addAll(this.processors); + this.processors = processors.stream() + .filter(p->{ + ProcessorContext.RunMode runMode = p.runMode(); + return runMode == ProcessorContext.RunMode.ALL || runMode == this.runMode; + }) + .sorted(OrderUtils.orderPriority(SpringPluginProcessor::order)) + .collect(Collectors.toList()); + for (SpringPluginProcessor processor : this.processors) { + try { + processor.initialize(context); + } catch (Throwable e){ + processException(processor, "initialize", e, true); + } + } + } + + @Override + public void refreshBefore(ProcessorContext context) throws ProcessorException { + for (SpringPluginProcessor processor : processors) { + try { + processor.refreshBefore(context); + } catch (Throwable e){ + processException(processor, "refreshBefore", e, true); + } + } + } + + @Override + public void refreshAfter(ProcessorContext context) throws ProcessorException { + for (SpringPluginProcessor processor : processors) { + try { + processor.refreshAfter(context); + } catch (Throwable e){ + processException(processor, "refreshAfter", e, true); + } + } + } + + @Override + public void failure(ProcessorContext context) throws ProcessorException { + for (SpringPluginProcessor processor : processors) { + try { + processor.failure(context); + } catch (Throwable e){ + processException(processor, "failure", e, false); + } + } + } + + @Override + public void close(ProcessorContext context) throws ProcessorException { + for (SpringPluginProcessor processor : processors) { + try { + processor.close(context); + } catch (Throwable e){ + processException(processor, "close", e, false); + } + } + } + + @Override + public OrderPriority order() { + return OrderPriority.getHighPriority(); + } + + @Override + public ProcessorContext.RunMode runMode() { + return ProcessorContext.RunMode.ALL; + } + + /** + * 获取默认的处理者 + * @param context ProcessorContext + * @param processors 处理者容器集合 + */ + protected void addDefaultProcessors(ProcessorContext context, List processors){ + processors.add(new FrameDefineBeanProcessor()); + processors.add(new ExtractBeanProcessor()); + processors.add(new InvokeOtherPluginProcessor()); + } + + /** + * 添加默认web环境处理者 + * @param context ProcessorContext + * @param processors 处理者容器集合 + */ + protected void addDefaultWebEnvProcessors(ProcessorContext context, List processors){ + SpringPluginBootstrap springPluginBootstrap = context.getSpringPluginBootstrap(); + DisablePluginWeb disablePluginWeb = AnnotationUtils.findAnnotation(springPluginBootstrap.getClass(), + DisablePluginWeb.class); + if(disablePluginWeb != null){ + return; + } + context.getWebConfig().setEnable(true); + processors.add(new PluginControllerProcessor()); + processors.add(new PluginInterceptorsProcessor()); + processors.add(new PluginStaticResourceProcessor()); + processors.add(new PluginThymeleafProcessor()); + ProcessorUtils.add(processors, PluginSpringDocControllerProcessor::new); + } + + private void processException(SpringPluginProcessor processor, String executeType, + Throwable e, boolean isThrow) throws ProcessorException{ + String error = "Processor[" + processor.getClass().getName() + "] execute[" + executeType + "] failure : " + + e.getMessage(); + logger.error(error, e); + if(isThrow){ + throw new ProcessorException(error, e); + } + } +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/DefaultProcessorContext.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/DefaultProcessorContext.java new file mode 100644 index 0000000000000000000000000000000000000000..d9d85e090eb25c0a030e640999f5ca7725d55996 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/DefaultProcessorContext.java @@ -0,0 +1,138 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.processor; + +import com.gitee.starblues.bootstrap.SpringPluginBootstrap; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.core.launcher.plugin.CacheRegistryInfo; +import com.gitee.starblues.core.launcher.plugin.PluginInteractive; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.spring.SpringBeanFactory; +import com.gitee.starblues.spring.WebConfig; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.ResourceLoader; +import org.springframework.util.ClassUtils; + +/** + * 默认的处理者上下文 + * @author starBlues + * @version 3.0.0 + */ +public class DefaultProcessorContext extends CacheRegistryInfo implements ProcessorContext{ + + private final RunMode runMode; + + private final SpringPluginBootstrap springPluginBootstrap; + private final PluginInteractive pluginInteractive; + private final Class runnerClass; + private final MainApplicationContext mainApplicationContext; + private final ClassLoader classLoader; + private final ResourceLoader resourceLoader; + + private final IntegrationConfiguration configuration; + private final WebConfig webConfig; + + private GenericApplicationContext applicationContext; + + public DefaultProcessorContext(RunMode runMode, SpringPluginBootstrap springPluginBootstrap, + PluginInteractive pluginInteractive, Class runnerClass) { + this.runMode = runMode; + this.springPluginBootstrap = springPluginBootstrap; + this.pluginInteractive = pluginInteractive; + this.runnerClass = runnerClass; + this.classLoader = getPluginClassLoader(); + this.resourceLoader = new DefaultResourceLoader(this.classLoader); + this.mainApplicationContext = pluginInteractive.getMainApplicationContext(); + this.configuration = pluginInteractive.getConfiguration(); + this.webConfig = new WebConfig(); + } + + @Override + public RunMode runMode() { + return runMode; + } + + @Override + public SpringPluginBootstrap getSpringPluginBootstrap() { + return springPluginBootstrap; + } + + @Override + public InsidePluginDescriptor getPluginDescriptor() { + return pluginInteractive.getPluginDescriptor(); + } + + @Override + public Class getRunnerClass() { + return runnerClass; + } + + @Override + public PluginInteractive getPluginInteractive() { + return pluginInteractive; + } + + @Override + public MainApplicationContext getMainApplicationContext() { + return mainApplicationContext; + } + + @Override + public SpringBeanFactory getMainBeanFactory() { + return mainApplicationContext.getSpringBeanFactory(); + } + + @Override + public IntegrationConfiguration getConfiguration() { + return configuration; + } + + @Override + public GenericApplicationContext getApplicationContext() { + if(applicationContext == null){ + return null; + } + return applicationContext; + } + + @Override + public ClassLoader getClassLoader() { + return classLoader; + } + + @Override + public ResourceLoader getResourceLoader() { + return resourceLoader; + } + + @Override + public WebConfig getWebConfig() { + return webConfig; + } + + @Override + public void setApplicationContext(GenericApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + + protected ClassLoader getPluginClassLoader(){ + return ClassUtils.getDefaultClassLoader(); + } +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ExtractBeanProcessor.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ExtractBeanProcessor.java new file mode 100644 index 0000000000000000000000000000000000000000..0ed867c6488db5c29e1d8e6e1494e01ff11f2695 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ExtractBeanProcessor.java @@ -0,0 +1,61 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.processor; + +import com.gitee.starblues.annotation.Extract; +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.processor.ProcessorException; +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; +import com.gitee.starblues.spring.extract.OpExtractFactory; +import com.gitee.starblues.utils.ObjectUtils; +import org.springframework.context.support.GenericApplicationContext; + +import java.util.Map; + +/** + * Extract 扩展Bean注册处理者 + * @author starBlues + * @version 3.0.0 + */ +public class ExtractBeanProcessor implements SpringPluginProcessor { + + @Override + public void refreshAfter(ProcessorContext context) throws ProcessorException { + GenericApplicationContext applicationContext = context.getApplicationContext(); + Map extractMap = applicationContext.getBeansWithAnnotation(Extract.class); + if(ObjectUtils.isEmpty(extractMap)){ + return; + } + String pluginId = context.getPluginDescriptor().getPluginId(); + OpExtractFactory opExtractFactory = context.getPluginInteractive().getOpExtractFactory(); + for (Object extract : extractMap.values()) { + opExtractFactory.add(pluginId, extract); + } + } + + @Override + public void close(ProcessorContext context) throws ProcessorException { + OpExtractFactory opExtractFactory = context.getPluginInteractive().getOpExtractFactory(); + String pluginId = context.getPluginDescriptor().getPluginId(); + opExtractFactory.remove(pluginId); + } + + @Override + public ProcessorContext.RunMode runMode() { + return ProcessorContext.RunMode.ALL; + } +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/FrameDefineBeanProcessor.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/FrameDefineBeanProcessor.java new file mode 100644 index 0000000000000000000000000000000000000000..56fcb6af01916a9de51eaf1c8c57d56230c3229a --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/FrameDefineBeanProcessor.java @@ -0,0 +1,60 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.processor; + + +import com.gitee.starblues.bootstrap.realize.DefaultMainEnvironmentProvider; +import com.gitee.starblues.bootstrap.realize.EmptyMainEnvironmentProvider; +import com.gitee.starblues.bootstrap.realize.MainEnvironmentProvider; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.integration.AutoIntegrationConfiguration; +import com.gitee.starblues.integration.ExtendPointConfiguration; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.support.GenericApplicationContext; + +/** + * 框架内置bean注册 + * @author starBlues + * @version 3.0.0 + */ +public class FrameDefineBeanProcessor implements SpringPluginProcessor { + + @Override + public void refreshBefore(ProcessorContext context) throws ProcessorException { + GenericApplicationContext applicationContext = context.getApplicationContext(); + InsidePluginDescriptor pluginDescriptor = context.getPluginDescriptor(); + ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory(); + beanFactory.registerSingleton("pluginDescriptor", pluginDescriptor.toPluginDescriptor()); + beanFactory.registerSingleton("mainApplicationContext", context.getMainApplicationContext()); + + MainEnvironmentProvider mainEnvironmentProvider = null; + if(context.runMode() == ProcessorContext.RunMode.ONESELF){ + beanFactory.registerSingleton("integrationConfiguration", new AutoIntegrationConfiguration()); + applicationContext.registerBean(ExtendPointConfiguration.class); + mainEnvironmentProvider = new EmptyMainEnvironmentProvider(); + } else { + mainEnvironmentProvider = new DefaultMainEnvironmentProvider(context.getMainApplicationContext()); + } + beanFactory.registerSingleton("mainEnvironmentProvider", mainEnvironmentProvider); + } + + @Override + public ProcessorContext.RunMode runMode() { + return ProcessorContext.RunMode.ALL; + } + +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/InvokeOtherPluginProcessor.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/InvokeOtherPluginProcessor.java new file mode 100644 index 0000000000000000000000000000000000000000..6c12254fb5e464637b8ae4b1cf1ffdfcfb3b3726 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/InvokeOtherPluginProcessor.java @@ -0,0 +1,126 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.processor; + +import com.gitee.starblues.annotation.Caller; +import com.gitee.starblues.annotation.Supplier; +import com.gitee.starblues.bootstrap.processor.invoke.InvokeBeanFactory; +import com.gitee.starblues.bootstrap.processor.scanner.PluginClassPathBeanDefinitionScanner; +import com.gitee.starblues.spring.ApplicationContext; +import com.gitee.starblues.spring.ApplicationContextProxy; +import com.gitee.starblues.spring.invoke.InvokeSupperCache; +import com.gitee.starblues.spring.invoke.SupperCache; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.ScanUtils; +import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.GenericBeanDefinition; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.type.filter.AnnotationTypeFilter; + +import java.util.Map; +import java.util.Set; + +/** + * 反射调用其他插件的处理者 + * @author starBlues + * @version 3.0.0 + */ +public class InvokeOtherPluginProcessor implements SpringPluginProcessor { + + @Override + public void refreshBefore(ProcessorContext context) throws ProcessorException { + InvokeCallerBeanDefinitionScanner scanner = new InvokeCallerBeanDefinitionScanner(context); + scanner.doScan(ScanUtils.getScanBasePackages(context.getRunnerClass())); + } + + @Override + public void refreshAfter(ProcessorContext context) throws ProcessorException { + GenericApplicationContext applicationContext = context.getApplicationContext(); + Map supplierBeans = applicationContext.getBeansWithAnnotation(Supplier.class); + String pluginId = context.getPluginDescriptor().getPluginId(); + ApplicationContext applicationContextReflection = new ApplicationContextProxy(applicationContext); + InvokeSupperCache invokeSupperCache = context.getPluginInteractive().getInvokeSupperCache(); + supplierBeans.forEach((k,v)->{ + Supplier supplier = AnnotationUtils.findAnnotation(v.getClass(), Supplier.class); + String supperKey = k; + if(supplier != null && !ObjectUtils.isEmpty(supplier.value())){ + supperKey = supplier.value(); + } + invokeSupperCache.add(pluginId, new SupperCache(supperKey, k, applicationContextReflection)); + }); + } + + @Override + public ProcessorContext.RunMode runMode() { + return ProcessorContext.RunMode.ALL; + } + + + private static class InvokeCallerBeanDefinitionScanner extends PluginClassPathBeanDefinitionScanner { + + private final ProcessorContext context; + + public InvokeCallerBeanDefinitionScanner(ProcessorContext context) { + super(context, false); + setResourceLoader(context.getResourceLoader()); + this.context = context; + addIncludeFilter(new AnnotationTypeFilter(Caller.class)); + addExcludeFilter((metadataReader, metadataReaderFactory) -> { + String className = metadataReader.getClassMetadata().getClassName(); + return className.endsWith("package-info"); + }); + } + + + @Override + protected Set doScan(String... basePackages) { + Set holders = super.doScan(basePackages); + ClassLoader pluginClassLoader = context.getClassLoader(); + InvokeSupperCache invokeSupperCache = context.getPluginInteractive().getInvokeSupperCache(); + for (BeanDefinitionHolder holder : holders) { + AbstractBeanDefinition definition = (AbstractBeanDefinition) holder.getBeanDefinition(); + try { + Class aClass = pluginClassLoader.loadClass(definition.getBeanClassName()); + Caller caller = AnnotationUtils.findAnnotation(aClass, Caller.class); + if(caller == null){ + continue; + } + // 是调用方 + definition.getPropertyValues().add("callerAnnotation", caller); + definition.getPropertyValues().add("callerInterface", aClass); + definition.getPropertyValues().add("invokeSupperCache", invokeSupperCache); + definition.setBeanClass(InvokeBeanFactory.class); + definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + return holders; + } + + @Override + protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { + return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent(); + } + + } + + +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ProcessorContext.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ProcessorContext.java new file mode 100644 index 0000000000000000000000000000000000000000..db4c125427aa79507918a11895fa75cf05606bd0 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ProcessorContext.java @@ -0,0 +1,138 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.processor; + + +import com.gitee.starblues.bootstrap.SpringPluginBootstrap; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.core.launcher.plugin.PluginInteractive; +import com.gitee.starblues.core.launcher.plugin.RegistryInfo; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.spring.SpringBeanFactory; +import com.gitee.starblues.spring.WebConfig; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.io.ResourceLoader; + +/** + * 处理者上下文 + * @author starBlues + * @version 3.0.0 + */ +public interface ProcessorContext extends RegistryInfo { + + /** + * 当前运行模式 + * @return RunMode + */ + RunMode runMode(); + + /** + * 得到入口类对象-SpringPluginBootstrap + * @return SpringPluginBootstrap + */ + SpringPluginBootstrap getSpringPluginBootstrap(); + + /** + * 得到插件信息 PluginDescriptor + * @return PluginDescriptor + */ + InsidePluginDescriptor getPluginDescriptor(); + + /** + * 得到启动的class类 + * @return Class + */ + Class getRunnerClass(); + + /** + * 得到 PluginInteractive + * @return PluginInteractive + */ + PluginInteractive getPluginInteractive(); + + /** + * 得到主程序的 ApplicationContext + * @return MainApplicationContext + */ + MainApplicationContext getMainApplicationContext(); + + /** + * 得到主程序的 SpringBeanFactory + * @return SpringBeanFactory + */ + SpringBeanFactory getMainBeanFactory(); + + /** + * 得到当前框架的集成配置 + * @return IntegrationConfiguration + */ + IntegrationConfiguration getConfiguration(); + + + /** + * 得到当前插件的 ApplicationContext + * @return GenericApplicationContext + */ + GenericApplicationContext getApplicationContext(); + + /** + * 得到当前插件的 ClassLoader + * @return ClassLoader + */ + ClassLoader getClassLoader(); + + /** + * 得到插件的资源loader + * @return ResourceLoader + */ + ResourceLoader getResourceLoader(); + + /** + * 获取 WebConfig + * @return WebConfig + */ + WebConfig getWebConfig(); + + /** + * set 当前插件的 ApplicationContext + * @param applicationContext GenericApplicationContext + */ + void setApplicationContext(GenericApplicationContext applicationContext); + + /** + * 运行模式 + */ + enum RunMode{ + /** + * 全部运行 + */ + ALL, + + /** + * 插件环境运行 + */ + PLUGIN, + + /** + * 插件独立运行 + */ + ONESELF + } + + +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ProcessorException.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ProcessorException.java new file mode 100644 index 0000000000000000000000000000000000000000..c0f6306af7593b8a8517b05dae30dafbe40fa49d --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ProcessorException.java @@ -0,0 +1,45 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.processor; + +/** + * 处理者异常 + * @author starBlues + * @version 3.0.0 + */ +public class ProcessorException extends RuntimeException{ + + public ProcessorException() { + super(); + } + + public ProcessorException(String message) { + super(message); + } + + public ProcessorException(String message, Throwable cause) { + super(message, cause); + } + + public ProcessorException(Throwable cause) { + super(cause); + } + + protected ProcessorException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/SpringPluginProcessor.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/SpringPluginProcessor.java new file mode 100644 index 0000000000000000000000000000000000000000..5b959466a634992bf46841e763802787994b284e --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/SpringPluginProcessor.java @@ -0,0 +1,90 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.processor; + +import com.gitee.starblues.utils.Order; +import com.gitee.starblues.utils.OrderPriority; + +/** + * spring plugin 处理器 + * @author starBlues + * @version 3.0.0 + */ +public interface SpringPluginProcessor extends Order { + + /** + * 初始化时 + * @param context ProcessorContext + * @throws ProcessorException 处理异常 + */ + default void initialize(ProcessorContext context) throws ProcessorException{ + + } + + /** + * 刷新上下文前 + * @param context ProcessorContext + * @throws ProcessorException 处理异常 + */ + default void refreshBefore(ProcessorContext context) throws ProcessorException{ + + } + + /** + * 刷新上下文后 + * @param context ProcessorContext + * @throws ProcessorException 处理异常 + */ + default void refreshAfter(ProcessorContext context) throws ProcessorException{ + + } + + /** + * 启动失败 + * @param context ProcessorContext + * @throws ProcessorException 处理异常 + */ + default void failure(ProcessorContext context) throws ProcessorException{ + + } + + /** + * 关闭容器时 + * @param context ProcessorContext + * @throws ProcessorException 处理异常 + */ + default void close(ProcessorContext context) throws ProcessorException{ + + } + + /** + * 执行顺序 + * @return OrderPriority + */ + @Override + default OrderPriority order(){ + return OrderPriority.getLowPriority(); + } + + /** + * 处理器运行模式 + * @return RunMode + */ + ProcessorContext.RunMode runMode(); + + +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/interceptor/PluginInterceptorRegister.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/interceptor/PluginInterceptorRegister.java new file mode 100644 index 0000000000000000000000000000000000000000..7d3454aede3b78cf7e0c9797a9ddb7b05f0e56ac --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/interceptor/PluginInterceptorRegister.java @@ -0,0 +1,33 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.processor.interceptor; + +/** + * 插件拦截器注册者 + * @author starBlues + * @version 3.0.0 + */ +public interface PluginInterceptorRegister { + + /** + * 拦截器注册者 + * @param registry 注册对象 + */ + void registry(PluginInterceptorRegistry registry); + + +} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/interceptor/PluginInterceptorRegistration.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/interceptor/PluginInterceptorRegistration.java similarity index 81% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/interceptor/PluginInterceptorRegistration.java rename to spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/interceptor/PluginInterceptorRegistration.java index ee8647e093c567c707082f2ca6341f6e3236eb09..668ab01a7a0409e7cc1d8f28e061329c52d34105 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/interceptor/PluginInterceptorRegistration.java +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/interceptor/PluginInterceptorRegistration.java @@ -1,7 +1,23 @@ -package com.gitee.starblues.factory.process.pipe.interceptor; +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.processor.interceptor; -import com.gitee.starblues.utils.CommonUtils; -import org.pf4j.util.StringUtils; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.UrlUtils; import org.springframework.lang.Nullable; import org.springframework.util.PathMatcher; import org.springframework.web.servlet.HandlerInterceptor; @@ -15,7 +31,7 @@ import java.util.List; /** * 插件拦截器注册的信息 * @author starBlues - * @version 2.4.1 + * @version 3.0.0 */ public class PluginInterceptorRegistration { @@ -68,10 +84,10 @@ public class PluginInterceptorRegistration { } // 局部的 for (String pattern : patterns) { - if(StringUtils.isNullOrEmpty(pattern)){ + if(ObjectUtils.isEmpty(pattern)){ continue; } - this.includePatterns.add(CommonUtils.joiningPath(pluginRestApiPrefix, pattern)); + this.includePatterns.add(UrlUtils.joiningUrlPath(pluginRestApiPrefix, pattern)); } return this; } @@ -86,10 +102,10 @@ public class PluginInterceptorRegistration { this.excludePatterns.addAll(Arrays.asList(patterns)); } for (String pattern : patterns) { - if(StringUtils.isNullOrEmpty(pattern)){ + if(ObjectUtils.isEmpty(pattern)){ continue; } - this.excludePatterns.add(CommonUtils.joiningPath(pluginRestApiPrefix, pattern)); + this.excludePatterns.add(UrlUtils.joiningUrlPath(pluginRestApiPrefix, pattern)); } return this; } @@ -137,7 +153,7 @@ public class PluginInterceptorRegistration { protected Object getInterceptor() { if(type == PluginInterceptorRegistry.Type.PLUGIN){ if(this.includePatterns.isEmpty()){ - this.includePatterns.add(CommonUtils.joiningPath(pluginRestApiPrefix, "/**")); + this.includePatterns.add(UrlUtils.joiningUrlPath(pluginRestApiPrefix, "/**")); } } if (this.includePatterns.isEmpty() && this.excludePatterns.isEmpty()) { diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/interceptor/PluginInterceptorRegistry.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/interceptor/PluginInterceptorRegistry.java similarity index 82% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/interceptor/PluginInterceptorRegistry.java rename to spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/interceptor/PluginInterceptorRegistry.java index f95ecc8476f834a1cb6623e1de465437c4fe3721..817bce85e2359741b59af3869eee4b5d91eba4b3 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/interceptor/PluginInterceptorRegistry.java +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/interceptor/PluginInterceptorRegistry.java @@ -1,4 +1,20 @@ -package com.gitee.starblues.factory.process.pipe.interceptor; +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.processor.interceptor; import org.springframework.core.OrderComparator; import org.springframework.core.Ordered; @@ -15,7 +31,7 @@ import java.util.stream.Collectors; /** * 插件拦截器添加者 * @author starBlues - * @version 2.4.1 + * @version 3.0.0 */ public class PluginInterceptorRegistry { @@ -68,6 +84,15 @@ public class PluginInterceptorRegistry { .collect(Collectors.toList()); } + private static final Comparator INTERCEPTOR_ORDER_COMPARATOR = + OrderComparator.INSTANCE.withSourceProvider(object -> { + if (object instanceof PluginInterceptorRegistration) { + return (Ordered) ((PluginInterceptorRegistration) object)::getOrder; + } + return null; + }); + + public enum Type{ /** @@ -82,14 +107,6 @@ public class PluginInterceptorRegistry { } - private static final Comparator INTERCEPTOR_ORDER_COMPARATOR = - OrderComparator.INSTANCE.withSourceProvider(object -> { - if (object instanceof PluginInterceptorRegistration) { - return (Ordered) ((PluginInterceptorRegistration) object)::getOrder; - } - return null; - }); - } diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/invoke/InvokeBeanFactory.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/invoke/InvokeBeanFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..de9f62d38eb82ba9b08ed1f68f1a989b408c0fb7 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/invoke/InvokeBeanFactory.java @@ -0,0 +1,66 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.processor.invoke; + +import com.gitee.starblues.annotation.Caller; +import com.gitee.starblues.spring.invoke.InvokeSupperCache; +import org.springframework.beans.factory.FactoryBean; + +import java.lang.reflect.Proxy; + +/** + * 反射调用其他插件定义的接口bean工厂 + * @author starBlues + * @version 3.0.0 + */ +public class InvokeBeanFactory implements FactoryBean { + + private Class callerInterface; + private Caller callerAnnotation; + private InvokeSupperCache invokeSupperCache; + + @Override + @SuppressWarnings("unchecked") + public T getObject() throws Exception { + ClassLoader classLoader = callerInterface.getClassLoader(); + Class[] interfaces = new Class[]{callerInterface}; + InvokeProxyHandler proxy = new InvokeProxyHandler(callerAnnotation, invokeSupperCache); + return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy); + } + + @Override + public Class getObjectType() { + return callerInterface; + } + + @Override + public boolean isSingleton() { + return true; + } + + public void setCallerInterface(Class callerInterface) { + this.callerInterface = callerInterface; + } + + public void setCallerAnnotation(Caller callerAnnotation) { + this.callerAnnotation = callerAnnotation; + } + + public void setInvokeSupperCache(InvokeSupperCache invokeSupperCache) { + this.invokeSupperCache = invokeSupperCache; + } +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/invoke/InvokeProxyHandler.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/invoke/InvokeProxyHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..9efd0e0d07f47ae362e50aa3c5a558e1fae2feb4 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/invoke/InvokeProxyHandler.java @@ -0,0 +1,210 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.processor.invoke; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.gitee.starblues.annotation.Caller; +import com.gitee.starblues.annotation.Supplier; +import com.gitee.starblues.spring.invoke.InvokeSupperCache; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.ReflectionUtils; + +import java.io.Serializable; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; +import java.util.Objects; + +/** + * 反射调用处理模块 + * @author starBlues + * @version 3.0.0 + */ +public class InvokeProxyHandler implements InvocationHandler { + + private final Caller callerAnnotation; + + private final static ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private final InvokeSupperCache invokeSupperCache; + + public InvokeProxyHandler(Caller callerAnnotation, InvokeSupperCache invokeSupperCache) { + this.callerAnnotation = callerAnnotation; + this.invokeSupperCache = invokeSupperCache; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] callerArgs) throws Throwable { + String pluginId = callerAnnotation.pluginId(); + Object supplierObject = invokeSupperCache.getSupperBean(pluginId, callerAnnotation.value()); + if (supplierObject == null) { + if (ObjectUtils.isEmpty(pluginId)) { + throw new Exception("Not found '" + callerAnnotation.value() + "' supplier object"); + } else { + throw new Exception("Not found '" + callerAnnotation.value() + "' supplier object in plugin '" + + pluginId + "'"); + } + } + Caller.Method callerMethod = method.getAnnotation(Caller.Method.class); + if (callerArgs == null) { + callerArgs = new Object[]{}; + } + if (callerMethod == null) { + return notAnnotationInvoke(method, supplierObject, callerArgs); + } else { + return annotationInvoke(method, callerMethod, supplierObject, callerArgs); + } + } + + + + /** + * 有注解的调用 + * @param method 调用接口的方法 + * @param callerMethod 调用者方法注解 + * @param supplierObject 调用者对象 + * @param callerArgs 调用者参数 + * @return 返回值 + * @throws Throwable 异常 + */ + private Object annotationInvoke(Method method, Caller.Method callerMethod, + Object supplierObject, Object[] callerArgs) throws Throwable{ + + String callerMethodName = callerMethod.value(); + Class supplierClass = supplierObject.getClass(); + Method[] methods = supplierClass.getMethods(); + Method supplierMethod = null; + for (Method m : methods) { + Supplier.Method supplierMethodAnnotation = m.getAnnotation(Supplier.Method.class); + if(supplierMethodAnnotation == null){ + continue; + } + if(Objects.equals(supplierMethodAnnotation.value(), callerMethodName)){ + supplierMethod = m; + break; + } + } + if(supplierMethod == null){ + // 如果为空, 说明没有找到被调用者的注解, 则走没有注解的代理调用。 + return notAnnotationInvoke(method, supplierObject, callerArgs); + } + Class[] parameterTypes = supplierMethod.getParameterTypes(); + if(parameterTypes.length != callerArgs.length){ + // 参数不匹配 + return notAnnotationInvoke(method, supplierObject, callerArgs); + } + Object[] supplierArgs = getSupplierArgs(callerArgs, supplierMethod); + Object invokeReturn = supplierMethod.invoke(supplierObject, supplierArgs); + return getReturnObject(invokeReturn, method); + } + + /** + * 没有注解调用 + * @param method 调用接口的方法 + * @param supplierObject 提供者对象 + * @param callerArgs 调用者参数 + * @return 返回值 + * @throws Throwable 异常 + */ + private Object notAnnotationInvoke(Method method, Object supplierObject, Object[] callerArgs) throws Throwable{ + String name = method.getName(); + Class[] supplierArgClasses = new Class[callerArgs.length]; + ClassLoader classLoader = supplierObject.getClass().getClassLoader(); + for (int i = 0; i < callerArgs.length; i++) { + Object callerArg = callerArgs[i]; + try { + supplierArgClasses[i] = classLoader.loadClass(callerArg.getClass().getName()); + } catch (Exception e){ + supplierArgClasses[i] = callerArg.getClass(); + } + } + Class supplierClass = supplierObject.getClass(); + Method supplierMethod = null; + try { + supplierMethod = supplierClass.getMethod(name, supplierArgClasses); + } catch (Exception e){ + supplierMethod = findSupplierMethod(supplierClass, name, supplierArgClasses); + } + if(supplierMethod == null){ + throw ReflectionUtils.getNoSuchMethodException(supplierClass, name, supplierArgClasses); + } + Object[] supplierArgs = getSupplierArgs(callerArgs, supplierMethod); + Object invokeReturn = supplierMethod.invoke(supplierObject, supplierArgs); + return getReturnObject(invokeReturn, method); + } + + private Object[] getSupplierArgs(Object[] callerArgs, Method supplierMethod) throws Exception{ + if(callerArgs == null || callerArgs.length == 0){ + return new Class[]{}; + } + Class[] supplierParameterTypes = supplierMethod.getParameterTypes(); + Object[] supplierArgs = new Object[callerArgs.length]; + for (int i = 0; i < supplierParameterTypes.length; i++) { + Class supplierParameterType = supplierParameterTypes[i]; + Object arg = callerArgs[i]; + if(supplierParameterType.isAssignableFrom(arg.getClass())){ + // 类型相同 + supplierArgs[i] = arg; + } else { + // 类型不匹配, 尝试使用json序列化. 当前序列化针对大数据量下性能比较低, 建议将大数据量传输的参数定义到主程序中 + String json = OBJECT_MAPPER.writeValueAsString(arg); + Object serializeObject = OBJECT_MAPPER.readValue(json, supplierParameterType); + supplierArgs[i] = serializeObject; + } + } + return supplierArgs; + } + + + /** + * 得到返回值对象 + * @param invokeReturn 反射调用后返回的对象 + * @param method 调用接口的方法 + * @return 返回值对象 + * @throws Throwable Throwable + */ + private Object getReturnObject(Object invokeReturn, Method method) throws Throwable{ + if(invokeReturn == null){ + return null; + } + Class returnType = method.getReturnType(); + if(invokeReturn.getClass().isAssignableFrom(returnType)){ + return invokeReturn; + } else { + String json = OBJECT_MAPPER.writeValueAsString(invokeReturn); + return OBJECT_MAPPER.readValue(json, OBJECT_MAPPER.getTypeFactory().constructType(method.getGenericReturnType()) ); + } + } + + private Method findSupplierMethod(Class supplierClass, String methodName, Class[] supplierArgClasses){ + while (supplierClass != null){ + Method[] methods = supplierClass.getMethods(); + for (Method method : methods) { + String name = method.getName(); + if(!Objects.equals(name, methodName)){ + continue; + } + if(method.getParameterTypes().length == supplierArgClasses.length){ + return method; + } + } + supplierClass = supplierClass.getSuperclass(); + } + return null; + } + +} \ No newline at end of file diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/scanner/PluginClassPathBeanDefinitionScanner.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/scanner/PluginClassPathBeanDefinitionScanner.java new file mode 100644 index 0000000000000000000000000000000000000000..d8641804bf1ad885e4a9017c07bfcd56d157538a --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/scanner/PluginClassPathBeanDefinitionScanner.java @@ -0,0 +1,40 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.processor.scanner; + +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; + + +/** + * 插件自定义 classpath bean 扫描 + * @author starBlues + * @version 3.0.0 + */ +public class PluginClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner { + + public PluginClassPathBeanDefinitionScanner(ProcessorContext processorContext) { + this(processorContext, true); + } + + public PluginClassPathBeanDefinitionScanner(ProcessorContext processorContext, boolean useDefaultFilters) { + super(processorContext.getApplicationContext(), useDefaultFilters, + processorContext.getApplicationContext().getEnvironment(), + processorContext.getResourceLoader()); + } + +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginControllerProcessor.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginControllerProcessor.java new file mode 100644 index 0000000000000000000000000000000000000000..a097e6464080e15ad68257d52722886cb44d5068 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginControllerProcessor.java @@ -0,0 +1,325 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.processor.web; + +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.processor.ProcessorException; +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; +import com.gitee.starblues.bootstrap.utils.AnnotationUtils; +import com.gitee.starblues.bootstrap.utils.DestroyUtils; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.spring.SpringBeanFactory; +import com.gitee.starblues.utils.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * 插件Controller处理者 + * @author starBlues + * @version 3.0.0 + */ +public class PluginControllerProcessor implements SpringPluginProcessor { + + private final static Logger LOG = LoggerFactory.getLogger(PluginControllerProcessor.class); + + static final String PROCESS_CONTROLLERS = "PROCESS_SUCCESS"; + + + private RequestMappingHandlerMapping requestMappingHandlerMapping; + private Method getMappingForMethod; + private RequestMappingHandlerAdapter handlerAdapter; + + private final AtomicBoolean canRegistered = new AtomicBoolean(false); + + + @Override + public void initialize(ProcessorContext processorContext) throws ProcessorException { + SpringBeanFactory mainBeanFactory = processorContext.getMainBeanFactory(); + this.requestMappingHandlerMapping = mainBeanFactory.getBean(RequestMappingHandlerMapping.class); + this.handlerAdapter = SpringBeanCustomUtils.getExistBean(processorContext.getMainApplicationContext(), + RequestMappingHandlerAdapter.class); + this.getMappingForMethod = ReflectionUtils.findMethod(RequestMappingHandlerMapping.class, + "getMappingForMethod", Method.class, Class.class); + if(getMappingForMethod == null){ + LOG.warn("RequestMappingHandlerMapping 类中没有发现 方法, 无法注册插件接口. " + + "请检查当前环境是否为 web 环境"); + } + this.getMappingForMethod.setAccessible(true); + canRegistered.set(true); + } + + + @Override + public void refreshBefore(ProcessorContext processorContext) throws ProcessorException { + if(!canRegistered.get()){ + return; + } + GenericApplicationContext applicationContext = processorContext.getApplicationContext(); + applicationContext.registerBean("changeRestPathPostProcessor", + ChangeRestPathPostProcessor.class, ()-> new ChangeRestPathPostProcessor(processorContext)); + } + + @Override + public void refreshAfter(ProcessorContext processorContext) throws ProcessorException { + if(!canRegistered.get()){ + return; + } + IntegrationConfiguration configuration = processorContext.getConfiguration(); + if(ObjectUtils.isEmpty(configuration.pluginRestPathPrefix()) + && !configuration.enablePluginIdRestPathPrefix()){ + // 如果 pluginRestPathPrefix 为空, 并且没有启用插件id作为插件前缀, 则不进行修改插件controller地址前缀 + return; + } + String pluginId = processorContext.getPluginDescriptor().getPluginId(); + List controllerWrappers = processorContext.getRegistryInfo(PROCESS_CONTROLLERS); + if(ObjectUtils.isEmpty(controllerWrappers)){ + return; + } + GenericApplicationContext applicationContext = processorContext.getApplicationContext(); + + Iterator iterator = controllerWrappers.iterator(); + while (iterator.hasNext()){ + ControllerWrapper controllerWrapper = iterator.next(); + if(!applicationContext.containsBean(controllerWrapper.getBeanName())){ + iterator.remove(); + } + Object controllerBean = applicationContext.getBean(controllerWrapper.getBeanName()); + Set requestMappingInfos = registry(applicationContext, controllerBean.getClass()); + if(requestMappingInfos.isEmpty()){ + iterator.remove(); + } else { + for (RequestMappingInfo requestMappingInfo : requestMappingInfos) { + LOG.info("插件[{}]注册接口: {}", pluginId, requestMappingInfo.toString()); + } + controllerWrapper.setRequestMappingInfos(requestMappingInfos); + } + } + } + + @Override + public void close(ProcessorContext context) throws ProcessorException { + List controllerWrappers = context.getRegistryInfo(PROCESS_CONTROLLERS); + if(ObjectUtils.isEmpty(controllerWrappers)){ + return; + } + for (ControllerWrapper controllerWrapper : controllerWrappers) { + unregister(controllerWrapper); + } + controllerWrappers.clear(); + } + + @Override + public ProcessorContext.RunMode runMode() { + return ProcessorContext.RunMode.PLUGIN; + } + + + private Set registry(GenericApplicationContext pluginApplicationContext, Class aClass) + throws ProcessorException { + Object object = pluginApplicationContext.getBean(aClass); + + Method[] methods = aClass.getDeclaredMethods(); + Set requestMappingInfos = new HashSet<>(); + for (Method method : methods) { + if (isHaveRequestMapping(method)) { + try { + RequestMappingInfo requestMappingInfo = (RequestMappingInfo) + getMappingForMethod.invoke(requestMappingHandlerMapping, method, aClass); + requestMappingHandlerMapping.registerMapping(requestMappingInfo, object, method); + requestMappingInfos.add(requestMappingInfo); + } catch (Exception e){ + throw new ProcessorException(e.getMessage()); + } + } + } + return requestMappingInfos; + } + + /** + * 卸载具体的Controller操作 + * @param controllerBeanWrapper controllerBean包装 + */ + private void unregister(ControllerWrapper controllerBeanWrapper) { + Set requestMappingInfos = controllerBeanWrapper.getRequestMappingInfos(); + if(requestMappingInfos != null && !requestMappingInfos.isEmpty()){ + for (RequestMappingInfo requestMappingInfo : requestMappingInfos) { + requestMappingHandlerMapping.unregisterMapping(requestMappingInfo); + } + } + if(handlerAdapter != null){ + Class beanClass = controllerBeanWrapper.getBeanClass(); + DestroyUtils.destroyValue(handlerAdapter, "sessionAttributesHandlerCache", beanClass); + DestroyUtils.destroyValue(handlerAdapter, "initBinderCache", beanClass); + DestroyUtils.destroyValue(handlerAdapter, "modelAttributeCache", beanClass); + } + } + + /** + * 方法上是否存在 @RequestMapping 注解 + * @param method method + * @return boolean + */ + private boolean isHaveRequestMapping(Method method){ + return AnnotationUtils.findAnnotation(method, RequestMapping.class) != null; + } + + private static class ChangeRestPathPostProcessor implements BeanPostProcessor { + + private final static Logger LOG = LoggerFactory.getLogger(ChangeRestPathPostProcessor.class); + private final static String COMMON_ERROR = "无法统一处理该Controller请求路径前缀"; + + private final ProcessorContext processorContext; + + private ChangeRestPathPostProcessor(ProcessorContext processorContext) { + this.processorContext = processorContext; + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + Class aClass = bean.getClass(); + RequestMapping requestMapping = AnnotationUtils.findAnnotation(aClass, RequestMapping.class); + boolean isController = AnnotationUtils.existOr(aClass, new Class[]{ + Controller.class, RestController.class + }); + if(requestMapping != null && isController){ + changePathForClass(beanName, aClass, requestMapping); + } + return bean; + } + + private void changePathForClass(String beanName, Class aClass, RequestMapping requestMapping){ + String pluginId = processorContext.getPluginDescriptor().getPluginId(); + IntegrationConfiguration configuration = processorContext.getConfiguration(); + String pathPrefix = PluginConfigUtils.getPluginRestPrefix(configuration, pluginId); + + if(ObjectUtils.isEmpty(pathPrefix)){ + LOG.error("插件 [{}] Controller类 [{}] 未发现 path 配置, {}", + pluginId, aClass.getSimpleName(), COMMON_ERROR); + return; + } + Set definePaths = new HashSet<>(); + definePaths.addAll(Arrays.asList(requestMapping.path())); + definePaths.addAll(Arrays.asList(requestMapping.value())); + try { + Map memberValues = ClassUtils.getAnnotationsUpdater(requestMapping); + if(memberValues == null){ + LOG.error("插件 [{}] Controller 类 [{}] 无法反射获取注解属性, {}", + pluginId, aClass.getSimpleName(), COMMON_ERROR); + return; + } + String[] newPath = new String[definePaths.size()]; + int i = 0; + for (String definePath : definePaths) { + // 解决插件启用、禁用后, 路径前缀重复的问题。 + if(definePath.contains(pathPrefix)){ + newPath[i++] = definePath; + } else { + newPath[i++] = UrlUtils.restJoiningPath(pathPrefix, definePath); + } + } + if(newPath.length == 0){ + newPath = new String[]{ pathPrefix }; + } + memberValues.put("path", newPath); + memberValues.put("value", newPath); + + List controllerWrappers = processorContext.getRegistryInfo(PROCESS_CONTROLLERS); + if(controllerWrappers == null){ + controllerWrappers = new ArrayList<>(); + processorContext.addRegistryInfo(PROCESS_CONTROLLERS, controllerWrappers); + } + ControllerWrapper controllerWrapper = new ControllerWrapper(); + controllerWrapper.setPathPrefix(newPath); + controllerWrapper.setBeanName(beanName); + controllerWrapper.setBeanClass(aClass); + controllerWrappers.add(controllerWrapper); + } catch (Exception e) { + LOG.error("插件 [{}] Controller 类[{}] 注册异常. {}", pluginId, aClass.getName(), e.getMessage(), e); + } + } + } + + + static class ControllerWrapper{ + + /** + * controller bean 名称 + */ + private String beanName; + + /** + * controller 路径前缀 + */ + private String[] pathPrefix; + + /** + * controller bean 类型 + */ + private Class beanClass; + + /** + * controller 的 RequestMappingInfo 集合 + */ + private Set requestMappingInfos; + + public Class getBeanClass() { + return beanClass; + } + + public void setBeanClass(Class beanClass) { + this.beanClass = beanClass; + } + + public String getBeanName() { + return beanName; + } + + public void setBeanName(String beanName) { + this.beanName = beanName; + } + + public String[] getPathPrefix() { + return pathPrefix; + } + + public void setPathPrefix(String[] pathPrefix) { + this.pathPrefix = pathPrefix; + } + + public Set getRequestMappingInfos() { + return requestMappingInfos; + } + + public void setRequestMappingInfos(Set requestMappingInfos) { + this.requestMappingInfos = requestMappingInfos; + } + } + +} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginInterceptorsPipeProcessor.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginInterceptorsProcessor.java similarity index 59% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginInterceptorsPipeProcessor.java rename to spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginInterceptorsProcessor.java index 9783987a38d3bd72ee819d26da4cbdc9eeb4ee82..48bb8fb2c11b3db0d71ef30f5f42a4540ec4c764 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginInterceptorsPipeProcessor.java +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginInterceptorsProcessor.java @@ -1,16 +1,35 @@ -package com.gitee.starblues.factory.process.pipe; +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.processor.web; -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.pipe.interceptor.PluginInterceptorRegister; -import com.gitee.starblues.factory.process.pipe.interceptor.PluginInterceptorRegistry; +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.processor.ProcessorException; +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; +import com.gitee.starblues.bootstrap.processor.interceptor.PluginInterceptorRegister; +import com.gitee.starblues.bootstrap.processor.interceptor.PluginInterceptorRegistry; +import com.gitee.starblues.bootstrap.utils.SpringBeanUtils; import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.spring.MainApplicationContext; import com.gitee.starblues.utils.ClassUtils; -import com.gitee.starblues.utils.CommonUtils; -import com.gitee.starblues.utils.SpringBeanUtils; +import com.gitee.starblues.utils.OrderUtils; +import com.gitee.starblues.utils.PluginConfigUtils; +import com.gitee.starblues.utils.SpringBeanCustomUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.GenericApplicationContext; import org.springframework.web.context.request.WebRequestInterceptor; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.handler.AbstractHandlerMapping; @@ -20,28 +39,22 @@ import java.util.ArrayList; import java.util.List; /** - * 插件 SpringMVC 拦截器的处理 + * 插件拦截器处理者 * @author starBlues - * @version 2.4.1 + * @version 3.0.0 */ -public class PluginInterceptorsPipeProcessor implements PluginPipeProcessor{ +public class PluginInterceptorsProcessor implements SpringPluginProcessor { - private final ApplicationContext mainApplicationContext; - private final IntegrationConfiguration configuration; private final Logger logger = LoggerFactory.getLogger(this.getClass()); - private final static String INTERCEPTORS = "interceptors"; + private final static String INTERCEPTORS = "pluginHandlerInterceptors"; private AbstractHandlerMapping handlerMapping; - public PluginInterceptorsPipeProcessor(ApplicationContext mainApplicationContext){ - this.mainApplicationContext = mainApplicationContext; - this.configuration = mainApplicationContext.getBean(IntegrationConfiguration.class); - } - @Override - public void initialize() throws Exception { - handlerMapping = SpringBeanUtils.getExistBean(mainApplicationContext, + public void initialize(ProcessorContext context) throws ProcessorException { + MainApplicationContext applicationContext = context.getMainApplicationContext(); + handlerMapping = SpringBeanCustomUtils.getExistBean(applicationContext, AbstractHandlerMapping.class); if(handlerMapping == null){ logger.warn("Not found AbstractHandlerMapping, Plugin interceptor can't use"); @@ -49,19 +62,21 @@ public class PluginInterceptorsPipeProcessor implements PluginPipeProcessor{ } @Override - public void registry(PluginRegistryInfo pluginRegistryInfo) throws Exception { + public void refreshAfter(ProcessorContext context) throws ProcessorException { if(handlerMapping == null){ return; } - GenericApplicationContext pluginApplicationContext = pluginRegistryInfo.getPluginApplicationContext(); - List interceptorRegisters = SpringBeanUtils.getBeans(pluginApplicationContext, + List interceptorRegisters = SpringBeanUtils.getBeans( + context.getApplicationContext(), PluginInterceptorRegister.class); List interceptorsObjects = new ArrayList<>(); List adaptedInterceptors = getAdaptedInterceptors(); if(adaptedInterceptors == null){ return; } - String pluginRestPrefix = CommonUtils.getPluginRestPrefix(configuration, pluginRegistryInfo.getPluginWrapper().getPluginId()); + IntegrationConfiguration configuration = context.getConfiguration(); + String pluginId = context.getPluginDescriptor().getPluginId(); + String pluginRestPrefix = PluginConfigUtils.getPluginRestPrefix(configuration, pluginId); for (PluginInterceptorRegister interceptorRegister : interceptorRegisters) { PluginInterceptorRegistry interceptorRegistry = new PluginInterceptorRegistry(pluginRestPrefix); @@ -76,15 +91,15 @@ public class PluginInterceptorsPipeProcessor implements PluginPipeProcessor{ interceptorsObjects.add(handlerInterceptor); } } - pluginRegistryInfo.addExtension(INTERCEPTORS, interceptorsObjects); + context.addRegistryInfo(INTERCEPTORS, interceptorsObjects); } @Override - public void unRegistry(PluginRegistryInfo pluginRegistryInfo) throws Exception { + public void close(ProcessorContext context) throws ProcessorException { if(handlerMapping == null){ return; } - List interceptorsObjects = pluginRegistryInfo.getExtension(INTERCEPTORS); + List interceptorsObjects = context.getRegistryInfo(INTERCEPTORS); if(interceptorsObjects == null || interceptorsObjects.isEmpty()){ return; } @@ -97,6 +112,11 @@ public class PluginInterceptorsPipeProcessor implements PluginPipeProcessor{ } } + @Override + public ProcessorContext.RunMode runMode() { + return ProcessorContext.RunMode.PLUGIN; + } + /** * 得到拦截器存储者 * @return List @@ -125,5 +145,4 @@ public class PluginInterceptorsPipeProcessor implements PluginPipeProcessor{ throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName()); } } - } diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginSpringDocControllerProcessor.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginSpringDocControllerProcessor.java new file mode 100644 index 0000000000000000000000000000000000000000..4b2ce0668264d6294a4ffa4886b42a8ef1b10cb3 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginSpringDocControllerProcessor.java @@ -0,0 +1,126 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.processor.web; + +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.processor.ProcessorException; +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.ReflectionUtils; +import com.gitee.starblues.utils.SpringBeanCustomUtils; +import io.swagger.v3.oas.models.OpenAPI; +import org.springdoc.api.AbstractOpenApiResource; +import org.springdoc.core.OpenAPIService; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +/** + * spring doc + * @author starBlues + * @version 3.0.0 + */ +public class PluginSpringDocControllerProcessor implements SpringPluginProcessor { + + static final String CONTROLLER_API_CLASS = "controller_api_class"; + + private OpenAPIService openApiService; + private List> restControllers; + + @SuppressWarnings("unchecked") + @Override + public void initialize(ProcessorContext context) throws ProcessorException { + + try { + MainApplicationContext mainApplicationContext = context.getMainApplicationContext(); + openApiService = SpringBeanCustomUtils.getExistBean(mainApplicationContext, OpenAPIService.class); + AbstractOpenApiResource openApiResource = SpringBeanCustomUtils.getExistBean(mainApplicationContext, + AbstractOpenApiResource.class); + if(openApiResource == null){ + return; + } + restControllers = (List>) ReflectionUtils.getField(null, openApiResource.getClass(), + "ADDITIONAL_REST_CONTROLLERS", List.class); + } catch (Throwable e) { + restControllers = null; + } + } + + @Override + public void refreshAfter(ProcessorContext context) throws ProcessorException { + if(restControllers == null){ + return; + } + List controllerWrappers = context.getRegistryInfo( + PluginControllerProcessor.PROCESS_CONTROLLERS); + if(ObjectUtils.isEmpty(controllerWrappers)){ + return; + } + List> apiClass = new ArrayList<>(); + for (PluginControllerProcessor.ControllerWrapper controllerWrapper : controllerWrappers) { + Class beanClass = controllerWrapper.getBeanClass(); + restControllers.add(beanClass); + apiClass.add(beanClass); + } + context.addRegistryInfo(CONTROLLER_API_CLASS, apiClass); + refresh(); + } + + @Override + public void close(ProcessorContext context) throws ProcessorException { + if(restControllers == null){ + return; + } + List> apiClass = context.getRegistryInfo(CONTROLLER_API_CLASS); + if(ObjectUtils.isEmpty(apiClass)){ + return; + } + try { + for (Class controllerClass : apiClass) { + restControllers.remove(controllerClass); + } + refresh(); + } finally { + apiClass.clear(); + } + } + + private void refresh(){ + if(openApiService != null){ + try { + // 兼容版本: 1.5.x + Method setCachedOpenApiMethod = + ReflectionUtils.findMethod(openApiService.getClass(), "setCachedOpenAPI", OpenAPI.class); + if(setCachedOpenApiMethod != null){ + setCachedOpenApiMethod.invoke(openApiService, null); + } + } catch (Exception e){ + // 忽略 + } + openApiService.resetCalculatedOpenAPI(); + } + } + + + @Override + public ProcessorContext.RunMode runMode() { + return ProcessorContext.RunMode.PLUGIN; + } +} + diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginStaticResourceProcessor.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginStaticResourceProcessor.java new file mode 100644 index 0000000000000000000000000000000000000000..1d2413bb4c39597febf9c83156bf03739503f220 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginStaticResourceProcessor.java @@ -0,0 +1,72 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.processor.web; + +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.processor.ProcessorException; +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; +import com.gitee.starblues.utils.ObjectUtils; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; + +import java.util.HashSet; +import java.util.Set; + +/** + * 插件web资源处理器. 获取资源配置 + * @author starBlues + * @version 3.0.0 + */ +public class PluginStaticResourceProcessor implements SpringPluginProcessor { + + + /** + * 静态文件配置前缀 + * 静态文件路径 + * classpath: static/ + * file: D://path/test + */ + private final static String STATIC_LOCATIONS = "spring.resources.static-locations"; + + + @Override + public void refreshBefore(ProcessorContext context) throws ProcessorException { + GenericApplicationContext applicationContext = context.getApplicationContext(); + ConfigurableEnvironment environment = applicationContext.getEnvironment(); + String property = environment.getProperty(STATIC_LOCATIONS); + if(ObjectUtils.isEmpty(property)){ + return; + } + String[] staticLocations = property.split(","); + if (ObjectUtils.isEmpty(staticLocations)) { + return; + } + Set staticLocationsSet = new HashSet<>(staticLocations.length); + for (String staticLocation : staticLocations) { + if(ObjectUtils.isEmpty(staticLocation)){ + continue; + } + staticLocationsSet.add(staticLocation); + } + context.getWebConfig().setResourceLocations(staticLocationsSet); + } + + @Override + public ProcessorContext.RunMode runMode() { + return ProcessorContext.RunMode.PLUGIN; + } +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/thymeleaf/PluginThymeleafProcessor.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/thymeleaf/PluginThymeleafProcessor.java new file mode 100644 index 0000000000000000000000000000000000000000..0cca5ecd3719198f7ddedbeda216fd02678f2a43 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/thymeleaf/PluginThymeleafProcessor.java @@ -0,0 +1,57 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.processor.web.thymeleaf; + +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.processor.ProcessorException; +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; +import com.gitee.starblues.spring.web.thymeleaf.ThymeleafConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.support.GenericApplicationContext; + +/** + * 插件 Thymeleaf 注册 + * @author starBlues + * @version 3.0.0 + */ +public class PluginThymeleafProcessor implements SpringPluginProcessor { + + private static final Logger logger = LoggerFactory.getLogger(PluginThymeleafProcessor.class); + + public static final String CONFIG_KEY = "ThymeleafConfig"; + + + @Override + public void refreshBefore(ProcessorContext context) throws ProcessorException { + GenericApplicationContext applicationContext = context.getApplicationContext(); + ThymeleafConfig thymeleafConfig = ThymeleafConfigParse.parse(applicationContext.getEnvironment()); + context.addRegistryInfo(CONFIG_KEY, thymeleafConfig); + } + + @Override + public void close(ProcessorContext context) throws ProcessorException { + context.removeRegistryInfo(CONFIG_KEY); + } + + @Override + public ProcessorContext.RunMode runMode() { + return ProcessorContext.RunMode.PLUGIN; + } + + +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/thymeleaf/ThymeleafConfigParse.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/thymeleaf/ThymeleafConfigParse.java new file mode 100644 index 0000000000000000000000000000000000000000..411818b5276a6e8d5f35f9483442a4349d195bcf --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/thymeleaf/ThymeleafConfigParse.java @@ -0,0 +1,73 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.processor.web.thymeleaf; + +import com.gitee.starblues.spring.web.thymeleaf.ThymeleafConfig; +import com.gitee.starblues.utils.ObjectUtils; +import org.springframework.core.env.Environment; + +/** + * 解析ThymeleafConfig配置 + * @author starBlues + * @version 3.0.0 + */ +public class ThymeleafConfigParse { + + private static final String KEY_PREFIX = "spring.thymeleaf."; + + + public static final String ENABLED = KEY_PREFIX + "enabled"; + private static final String PREFIX = KEY_PREFIX + "prefix"; + private static final String SUFFIX = KEY_PREFIX + "suffix"; + private static final String MODE = KEY_PREFIX + "mode"; + private static final String CACHE = KEY_PREFIX + "cache"; + private static final String TEMPLATE_RESOLVER_ORDER = KEY_PREFIX + "templateResolverOrder"; + + + public static ThymeleafConfig parse(Environment environment){ + ThymeleafConfig thymeleafConfig = new ThymeleafConfig(); + String enabled = environment.getProperty(ENABLED); + if(!ObjectUtils.isEmpty(enabled) && !Boolean.parseBoolean(enabled)) { + thymeleafConfig.setEnabled(false); + return thymeleafConfig; + } + String prefix = environment.getProperty(PREFIX); + if(!ObjectUtils.isEmpty(prefix)){ + thymeleafConfig.setPrefix(prefix); + } + String suffix = environment.getProperty(SUFFIX); + if(!ObjectUtils.isEmpty(suffix)){ + thymeleafConfig.setSuffix(suffix); + } + String mode = environment.getProperty(MODE); + if(!ObjectUtils.isEmpty(mode)){ + thymeleafConfig.setMode(mode); + } + String cache = environment.getProperty(CACHE); + if(!ObjectUtils.isEmpty(cache)){ + thymeleafConfig.setCache(Boolean.getBoolean(cache)); + } + String templateResolverOrder = environment.getProperty(TEMPLATE_RESOLVER_ORDER); + if(!ObjectUtils.isEmpty(templateResolverOrder)){ + thymeleafConfig.setTemplateResolverOrder(Integer.getInteger(templateResolverOrder)); + } + return thymeleafConfig; + } + + + +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/DefaultMainEnvironmentProvider.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/DefaultMainEnvironmentProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..8487d3664dd6c607272685cccc47dd56471c2aaf --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/DefaultMainEnvironmentProvider.java @@ -0,0 +1,123 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.realize; + +import com.gitee.starblues.loader.utils.ObjectUtils; +import com.gitee.starblues.spring.MainApplicationContext; + +import java.util.Map; +import java.util.function.Function; + +/** + * 主程序配置信息提供者默认实现 + * + * @author starBlues + * @version 3.0.0 + */ +public class DefaultMainEnvironmentProvider implements MainEnvironmentProvider{ + + private final MainApplicationContext mainApplicationContext; + + public DefaultMainEnvironmentProvider(MainApplicationContext mainApplicationContext) { + this.mainApplicationContext = mainApplicationContext; + } + + @Override + public Object getValue(String name) { + Map> configurableEnvironment = mainApplicationContext.getConfigurableEnvironment(); + if(ObjectUtils.isEmpty(configurableEnvironment)){ + return null; + } + for (Map.Entry> entry : configurableEnvironment.entrySet()) { + Map value = entry.getValue(); + Object o = value.get(name); + if(o != null){ + return o; + } + } + return null; + } + + @Override + public String getString(String name) { + return getValue(name, String::valueOf); + } + + @Override + public Integer getInteger(String name) { + return getValue(name, value -> { + if(value instanceof Integer){ + return (Integer) value; + } + return Integer.parseInt(String.valueOf(value)); + }); + } + + @Override + public Long getLong(String name) { + return getValue(name, value -> { + if(value instanceof Long){ + return (Long) value; + } + return Long.parseLong(String.valueOf(value)); + }); + } + + @Override + public Double getDouble(String name) { + return getValue(name, value -> { + if(value instanceof Double){ + return (Double) value; + } + return Double.parseDouble(String.valueOf(value)); + }); + } + + @Override + public Float getFloat(String name) { + return getValue(name, value -> { + if(value instanceof Float){ + return (Float) value; + } + return Float.parseFloat(String.valueOf(value)); + }); + } + + @Override + public Boolean getBoolean(String name) { + return getValue(name, value -> { + if(value instanceof Boolean){ + return (Boolean) value; + } + return Boolean.parseBoolean(String.valueOf(value)); + }); + } + + @Override + public Map> getAll() { + return mainApplicationContext.getConfigurableEnvironment(); + } + + private T getValue(String name, Function function){ + Object value = getValue(name); + if(value == null){ + return null; + } + return function.apply(value); + } + +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/EmptyMainEnvironmentProvider.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/EmptyMainEnvironmentProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..8180226fff2cf9e92bb7b7ba1e5fd56c0ca417c0 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/EmptyMainEnvironmentProvider.java @@ -0,0 +1,68 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.realize; + +import java.util.Collections; +import java.util.Map; + +/** + * 主程序配置信息提供者空值实现 + * + * @author starBlues + * @version 3.0.0 + */ +public class EmptyMainEnvironmentProvider implements MainEnvironmentProvider{ + @Override + public Object getValue(String name) { + return null; + } + + @Override + public String getString(String name) { + return null; + } + + @Override + public Integer getInteger(String name) { + return null; + } + + @Override + public Long getLong(String name) { + return null; + } + + @Override + public Double getDouble(String name) { + return null; + } + + @Override + public Float getFloat(String name) { + return null; + } + + @Override + public Boolean getBoolean(String name) { + return null; + } + + @Override + public Map> getAll() { + return Collections.emptyMap(); + } +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/MainEnvironmentProvider.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/MainEnvironmentProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..ea68a164bec6f04e3b4c5e32c73b51c1f778c8b6 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/MainEnvironmentProvider.java @@ -0,0 +1,84 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.realize; + +import java.util.Map; + +/** + * 主程序配置信息提供者 + * + * @author starBlues + * @version 3.0.0 + */ +public interface MainEnvironmentProvider { + + /** + * 根据名称获取配置值 + * @param name 配置名称 + * @return 配置值 + */ + Object getValue(String name); + + /** + * 根据名称获取 String 类型配置值 + * @param name 配置名称 + * @return 配置值 + */ + String getString(String name); + + /** + * 根据名称获取 Integer 类型配置值 + * @param name 配置名称 + * @return 配置值 + */ + Integer getInteger(String name); + + /** + * 根据名称获取 Long 类型配置值 + * @param name 配置名称 + * @return 配置值 + */ + Long getLong(String name); + + /** + * 根据名称获取 Double 类型配置值 + * @param name 配置名称 + * @return 配置值 + */ + Double getDouble(String name); + + /** + * 根据名称获取 Float 类型配置值 + * @param name 配置名称 + * @return 配置值 + */ + Float getFloat(String name); + + /** + * 根据名称获取 Boolean 类型配置值 + * @param name 配置名称 + * @return 配置值 + */ + Boolean getBoolean(String name); + + /** + * 获取所有配置集合 + * @return Map + */ + Map> getAll(); + +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/PluginCloseListener.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/PluginCloseListener.java new file mode 100644 index 0000000000000000000000000000000000000000..dfdfd66132e1526abcd3d4048c2db539bccb9b5f --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/PluginCloseListener.java @@ -0,0 +1,34 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.realize; + +import com.gitee.starblues.core.descriptor.PluginDescriptor; + +/** + * 插件被停止监听者。用于自定义关闭资源 + * @author starBlues + * @version 3.0.0 + */ +public interface PluginCloseListener { + + /** + * 关闭时调用 + * @param descriptor 当前插件描述者 + */ + void close(PluginDescriptor descriptor); + +} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/realize/UnRegistryValidator.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/StopValidator.java similarity index 40% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/realize/UnRegistryValidator.java rename to spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/StopValidator.java index af17b2c2bcd67c1c96cf58f16671016a0c503ac6..be1d74dbc0046d706f3630287e366d653c820366 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/realize/UnRegistryValidator.java +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/StopValidator.java @@ -1,26 +1,41 @@ -package com.gitee.starblues.realize; +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.realize; /** - * 卸载校验器 + * 插件停止校验器. 自主实现判断是否可卸载 * @author starBlues - * @version 2.4.0 + * @version 3.0.0 */ -public interface UnRegistryValidator { +public interface StopValidator { /** - * 校验是否可卸载 + * 校验是否可停止/卸载。如果校验器抛出异常. 默认插件不可停止/卸载 * @return 校验结果 - * @throws Exception 校验异常. 校验异常后, 该插件默认不可卸载 */ - Result verify() throws Exception; + Result verify(); class Result{ /** * 是否可卸载 */ - private boolean verify; + private final boolean verify; /** * 不可卸载时的提示内容 diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/AnnotationUtils.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/AnnotationUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..aa778aec555b1ae756e4b321e78799fe8388b888 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/AnnotationUtils.java @@ -0,0 +1,62 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.utils; + + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + +/** + * 注解工具类 + * @author starBlues + * @version 3.0.0 + */ +public class AnnotationUtils { + + + private AnnotationUtils(){} + + public static A findAnnotation(Method method, Class annotationType) { + return org.springframework.core.annotation.AnnotationUtils.findAnnotation(method, annotationType); + } + + + public static A findAnnotation(Class clazz, Class annotationType) { + return org.springframework.core.annotation.AnnotationUtils.findAnnotation(clazz, annotationType); + } + + public static boolean existOr(Class clazz, Class[] annotationTypes) { + for (Class annotationType : annotationTypes) { + A annotation = findAnnotation(clazz, annotationType); + if(annotation != null){ + return true; + } + } + return false; + } + + public static boolean existAnd(Class clazz, Class[] annotationTypes) { + for (Class annotationType : annotationTypes) { + A annotation = findAnnotation(clazz, annotationType); + if(annotation == null){ + return false; + } + } + return true; + } + +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/DestroyUtils.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/DestroyUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..eec6ef1b4187a93f0a98549717512835207fafce --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/DestroyUtils.java @@ -0,0 +1,101 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.utils; + +import com.gitee.starblues.utils.ReflectionUtils; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; + +/** + * 销毁工具类 + * @author starBlues + * @version 3.0.0 + */ +public class DestroyUtils { + + private DestroyUtils(){} + + public static void destroyAll(Object object, String fieldName, Class fieldType){ + destroyAll(object, object.getClass(), fieldName, fieldType); + } + + public static void destroyAll(Object object, Class objectClass, String fieldName, Class fieldType){ + try { + Object fieldObject = ReflectionUtils.getField(object, objectClass, fieldName, fieldType); + if(fieldObject == null){ + return; + } + destroyAll(fieldObject); + } catch (Exception e){ + // 忽略 + } + } + + public static void destroyValue(Object object, String fieldName, Object value){ + destroyValue(object, object.getClass(), fieldName, value); + } + + public static void destroyValue(Object object, Class objectClass, String fieldName, Object value){ + destroyValue(object, objectClass, fieldName, null, value); + } + + + public static void destroyValue(Object object, Class objectClass, String fieldName, Class fieldType, Object value){ + try { + Object fieldObject = ReflectionUtils.getField(object, objectClass, fieldName, fieldType); + if(fieldObject == null){ + return; + } + destroyValue(fieldObject, value); + } catch (Exception e){ + // 忽略 + } + } + + @SuppressWarnings("all") + public static void destroyAll(Object object){ + if(object == null){ + return; + } + if(object instanceof Map){ + ((Map)object).clear(); + } else if (object instanceof Collection){ + ((Collection)object).clear(); + } else if(object.getClass().isArray()){ + Object[] array = (Object[])object; + for (int i = 0; i < array.length; i++) { + array[i] = null; + } + } + } + + @SuppressWarnings("all") + public static void destroyValue(Object object, Object value){ + if(object == null){ + return; + } + if(object instanceof Map){ + ((Map)object).remove(value); + } else if (object instanceof Collection){ + ((Collection) object).removeIf(next -> Objects.equals(next, value)); + } + } + +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/ProcessorUtils.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/ProcessorUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..15a8e5a3b13e301807de69c048aad609819f35a2 --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/ProcessorUtils.java @@ -0,0 +1,41 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.utils; + +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; + +import java.util.List; +import java.util.function.Supplier; + +/** + * ProcessorUtils + * @author starBlues + * @version 3.0.0 + */ +public class ProcessorUtils { + + public static void add(List pluginProcessors, Supplier supplier){ + try { + SpringPluginProcessor pluginProcessor = supplier.get(); + pluginProcessors.add(pluginProcessor); + } catch (Throwable e){ + // 忽略 + } + } + + +} diff --git a/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/SpringBeanUtils.java b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/SpringBeanUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..db039fbfc9bd59a05b63f0da3cef5d0206114caa --- /dev/null +++ b/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/SpringBeanUtils.java @@ -0,0 +1,104 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.bootstrap.utils; + +import org.springframework.context.ApplicationContext; +import org.springframework.util.ClassUtils; + +import java.lang.annotation.Annotation; +import java.util.*; + +/** + * 插件bean工具类 + * @author starBlues + * @version 3.0.0 + */ +public class SpringBeanUtils { + /** + * 获取bean名称 + * @param applicationContext ApplicationContext + * @return bean名称集合 + */ + public static Set getBeanName(ApplicationContext applicationContext){ + String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); + Set set = new HashSet<>(beanDefinitionNames.length); + set.addAll(Arrays.asList(beanDefinitionNames)); + return set; + } + + /** + * 得到ApplicationContext中的bean的实现 + * @param applicationContext ApplicationContext + * @param aClass 接口或者抽象类型bean类型 + * @param 接口或者抽象类型bean类型 + * @return 所有的实现对象 + */ + public static List getBeans(ApplicationContext applicationContext, Class aClass) { + Map beansOfTypeMap = applicationContext.getBeansOfType(aClass); + if(beansOfTypeMap.isEmpty()){ + return new ArrayList<>(); + } + return new ArrayList<>(beansOfTypeMap.values()); + } + + /** + * 得到存在的bean, 不存在则返回null + * @param applicationContext ApplicationContext容器 + * @param aClass bean 类型 + * @param bean 类型 + * @return 存在bean对象, 不存在返回null + */ + public static T getExistBean(ApplicationContext applicationContext, Class aClass){ + String[] beanNamesForType = applicationContext.getBeanNamesForType(aClass, false, false); + if(beanNamesForType.length > 0){ + return applicationContext.getBean(aClass); + } else { + return null; + } + } + + /** + * 得到存在的bean, 不存在则返回null + * @param applicationContext ApplicationContext容器 + * @param beanName bean 名称 + * @param 返回的bean类型 + * @return 存在bean对象, 不存在返回null + */ + @SuppressWarnings("unchecked") + public static T getExistBean(ApplicationContext applicationContext, String beanName){ + if(applicationContext.containsBean(beanName)){ + Object bean = applicationContext.getBean(beanName); + return (T) bean; + } else { + return null; + } + } + + /** + * 通过注解获取bean + * @param applicationContext applicationContext + * @param annotationType 注解类型 + * @return List + */ + public static List getBeansWithAnnotation(ApplicationContext applicationContext, + Class annotationType){ + Map beanMap = applicationContext.getBeansWithAnnotation(annotationType); + return new ArrayList<>(beanMap.values()); + } + + +} diff --git a/springboot-plugin-framework-extension/pom.xml b/spring-brick-common/pom.xml similarity index 38% rename from springboot-plugin-framework-extension/pom.xml rename to spring-brick-common/pom.xml index c206ec138abd4496a06ff3daa3632fd5c636f2f7..90be99fd61909125b5a3fd5b325a14af3153afbb 100644 --- a/springboot-plugin-framework-extension/pom.xml +++ b/spring-brick-common/pom.xml @@ -2,20 +2,21 @@ - 4.0.0 + + spring-brick-parent + com.gitee.starblues + 3.0.0 + - com.gitee.starblues - springboot-plugin-framework-extension - pom - 2.4.6-RELEASE + spring-brick-common + jar - spring boot 插件式开发集成包--扩展模块 - - springboot-plugin-framework-extension-mybatis - springboot-plugin-framework-extension-resources - springboot-plugin-framework-extension-log - + + 1.8 + UTF-8 + 3.8.1 + \ No newline at end of file diff --git a/spring-brick-common/src/main/java/com/gitee/starblues/common/AbstractDependencyPlugin.java b/spring-brick-common/src/main/java/com/gitee/starblues/common/AbstractDependencyPlugin.java new file mode 100644 index 0000000000000000000000000000000000000000..4052ff79de05a768e8844afb0a30a4406974b408 --- /dev/null +++ b/spring-brick-common/src/main/java/com/gitee/starblues/common/AbstractDependencyPlugin.java @@ -0,0 +1,107 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.common; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Supplier; + +/** + * 依赖的插件 + * + * @author starBlues + * @version 3.0.0 + */ +public abstract class AbstractDependencyPlugin implements DependencyPlugin{ + + public static final String SPLIT_ALL = ","; + public static final String SPLIT_ONE = "@"; + + + /** + * set依赖插件id + * + * @param id 插件id + */ + public abstract void setId(String id); + + /** + * set依赖插件版本 + * + * @param version 插件版本 + */ + public abstract void setVersion(String version); + + /** + * set optional + * + * @param optional 是否可选 + */ + public abstract void setOptional(Boolean optional); + + + public static String toStr(List dependencyPlugins){ + if(dependencyPlugins == null || dependencyPlugins.isEmpty()){ + return null; + } + StringBuilder stringBuilder = new StringBuilder(); + final int size = dependencyPlugins.size(); + for (int i = 0; i < size; i++) { + AbstractDependencyPlugin dependencyPlugin = dependencyPlugins.get(i); + Boolean optional = dependencyPlugin.getOptional(); + if(optional == null){ + optional = false; + } + stringBuilder.append(dependencyPlugin.getId()) + .append(SPLIT_ONE).append(dependencyPlugin.getVersion()) + .append(SPLIT_ONE).append(optional); + if(i <= size - 2){ + stringBuilder.append(SPLIT_ALL); + } + } + return stringBuilder.toString(); + } + + + public static List toList(String str, Supplier supplier){ + if(str == null || "".equals(str)){ + return Collections.emptyList(); + } + String[] all = str.split(SPLIT_ALL); + if(all.length == 0){ + return Collections.emptyList(); + } + List list = new ArrayList<>(all.length); + for (String s : all) { + String[] one = s.split(SPLIT_ONE); + if(one.length == 0){ + continue; + } + if(one.length != 3){ + continue; + } + AbstractDependencyPlugin dependencyPlugin = supplier.get(); + dependencyPlugin.setId(one[0]); + dependencyPlugin.setVersion(one[1]); + dependencyPlugin.setOptional("true".equalsIgnoreCase(one[2])); + list.add(dependencyPlugin); + } + return list; + } + +} diff --git a/spring-brick-common/src/main/java/com/gitee/starblues/common/Constants.java b/spring-brick-common/src/main/java/com/gitee/starblues/common/Constants.java new file mode 100644 index 0000000000000000000000000000000000000000..2151f6cc2a0eb2bd4c0cbf6dec752b41500c1cb4 --- /dev/null +++ b/spring-brick-common/src/main/java/com/gitee/starblues/common/Constants.java @@ -0,0 +1,49 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.common; + +/** + * 静态常量 + * @author starBlues + * @version 3.0.0 + */ +public abstract class Constants { + + private Constants(){} + + + /** + * 禁用所有插件标志 + */ + public final static String DISABLED_ALL_PLUGIN = "*"; + + /** + * 允许所有版本的标志 + */ + public final static String ALLOW_VERSION = "0.0.0"; + + /** + * 加载到主程序依赖的标志 + */ + public final static String LOAD_TO_MAIN_SIGN = "@LOAD_TO_MAIN"; + + /** + * 相对路径符号标志 + */ + public final static String RELATIVE_SIGN = "~"; + +} diff --git a/spring-brick-common/src/main/java/com/gitee/starblues/common/DependencyPlugin.java b/spring-brick-common/src/main/java/com/gitee/starblues/common/DependencyPlugin.java new file mode 100644 index 0000000000000000000000000000000000000000..cb306a9108caff73b4a72570cfbade7a57eb7e57 --- /dev/null +++ b/spring-brick-common/src/main/java/com/gitee/starblues/common/DependencyPlugin.java @@ -0,0 +1,48 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.common; + +/** + * 依赖的插件 + * + * @author starBlues + * @version 3.0.0 + */ +public interface DependencyPlugin { + + /** + * 依赖插件id + * + * @return String + */ + String getId(); + + /** + * 依赖插件版本. 如果设置为: 0.0.0 表示支持任意版本依赖 + * + * @return String + */ + String getVersion(); + + /** + * 是否为必须依赖. 默认: false + * + * @return boolean + */ + Boolean getOptional(); + +} diff --git a/spring-brick-common/src/main/java/com/gitee/starblues/common/ManifestKey.java b/spring-brick-common/src/main/java/com/gitee/starblues/common/ManifestKey.java new file mode 100644 index 0000000000000000000000000000000000000000..ae91f444f45f4f452201841d1558cc024faffddc --- /dev/null +++ b/spring-brick-common/src/main/java/com/gitee/starblues/common/ManifestKey.java @@ -0,0 +1,89 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.common; + +import java.util.jar.Attributes; + +/** + * Manifest-Key + * + * @author starBlues + * @version 3.0.0 + */ +public class ManifestKey { + + /** + * Manifest version + */ + public static final String MANIFEST_VERSION = "Manifest-Version"; + + /** + * Manifest-version: 1.0 + */ + public static final String MANIFEST_VERSION_1_0 = "1.0"; + + /** + * plugin meta path + */ + public static final String PLUGIN_META_PATH = "Plugin-Meta-Path"; + + /** + * plugin package type + */ + public static final String PLUGIN_PACKAGE_TYPE = "Plugin-Package-Type"; + + + /** + * main class + */ + public static final String MAIN_CLASS = "Main-Class"; + + /** + * jar in jar: main class value + */ + public static final String MAIN_CLASS_VALUE = "com.gitee.starblues.loader.launcher.SpringMainProdBootstrap"; + + /** + * jar in jar: start class + */ + public static final String START_CLASS = "Start-Class"; + + /** + * jar class path + */ + public static final String CLASS_PATH = "Class-Path"; + + + + /** + * 获取值 + * + * @param attributes attributes + * @param key 获取的key + * @return 获取的值 + */ + public static String getValue(Attributes attributes, String key){ + try { + return attributes.getValue(key); + } catch (Throwable e){ + // 忽略 + return null; + } + } + + +} diff --git a/spring-brick-common/src/main/java/com/gitee/starblues/common/PackageStructure.java b/spring-brick-common/src/main/java/com/gitee/starblues/common/PackageStructure.java new file mode 100644 index 0000000000000000000000000000000000000000..95907fdc05e0962ae09150601213cd8839dce696 --- /dev/null +++ b/spring-brick-common/src/main/java/com/gitee/starblues/common/PackageStructure.java @@ -0,0 +1,89 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.common; + + +import java.io.File; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Matcher; + +/** + * 插件打包结构 + * + * @author starBlues + * @version 3.0.0 + */ +public abstract class PackageStructure { + + private PackageStructure(){} + + public static final String[] ILLEGAL_FORMAT = new String[]{ + AbstractDependencyPlugin.SPLIT_ALL, AbstractDependencyPlugin.SPLIT_ONE + }; + + + public static final String CHARSET_NAME = "utf-8"; + + public static final String SEPARATOR = "/"; + + public static final String CLASSES_NAME = "classes"; + + public static final String LIB_NAME = "lib"; + + public static final String META_INF_NAME = "META-INF"; + + public static final String MANIFEST = "MANIFEST.MF"; + + public static final String PLUGIN_META_NAME = "PLUGIN.META"; + + public static final String RESOURCES_DEFINE_NAME = "RESOURCES.CONF"; + + public static final String RESOURCES_DEFINE_DEPENDENCIES = "dependencies.index"; + public static final String RESOURCES_DEFINE_LOAD_MAIN_INCLUDES = "load.main.resources.includes"; + public static final String RESOURCES_DEFINE_LOAD_MAIN_EXCLUDES = "load.main.resources.excludes"; + + public static final String PROD_MANIFEST_PATH = META_INF_NAME + SEPARATOR + MANIFEST; + + public static final String PROD_RESOURCES_DEFINE_PATH = META_INF_NAME + SEPARATOR + RESOURCES_DEFINE_NAME; + + public static final String PROD_PLUGIN_META_PATH = META_INF_NAME + SEPARATOR + PLUGIN_META_NAME; + + public static final String PROD_CLASSES_PATH = CLASSES_NAME + SEPARATOR; + + public static final String PROD_LIB_PATH = LIB_NAME + SEPARATOR; + + public static String resolvePath(String path){ + if(path == null || "".equals(path)){ + return path; + } + if(path.contains(SEPARATOR)){ + return path.replaceAll(SEPARATOR, Matcher.quoteReplacement(File.separator)); + } + return path; + } + + public static String getIllegal(String str){ + for (String s : ILLEGAL_FORMAT) { + if(str.contains(s)){ + return s; + } + } + return null; + } + +} diff --git a/spring-brick-common/src/main/java/com/gitee/starblues/common/PackageType.java b/spring-brick-common/src/main/java/com/gitee/starblues/common/PackageType.java new file mode 100644 index 0000000000000000000000000000000000000000..ce11a7746bcfb00372135a00d5b1b51d8b02e324 --- /dev/null +++ b/spring-brick-common/src/main/java/com/gitee/starblues/common/PackageType.java @@ -0,0 +1,38 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.common; + +/** + * 打包类型 + * + * @author starBlues + * @version 3.0.0 + */ +public class PackageType { + + public static final String PLUGIN_PACKAGE_TYPE_DEV = "dev"; + + public static final String PLUGIN_PACKAGE_TYPE_JAR = "jar"; + public static final String PLUGIN_PACKAGE_TYPE_JAR_OUTER = "jar-outer"; + public static final String PLUGIN_PACKAGE_TYPE_ZIP = "zip"; + public static final String PLUGIN_PACKAGE_TYPE_ZIP_OUTER = "zip-outer"; + public static final String PLUGIN_PACKAGE_TYPE_DIR = "dir"; + + public static final String MAIN_PACKAGE_TYPE_JAR = "jar"; + public static final String MAIN_PACKAGE_TYPE_JAR_OUTER = "jar-outer"; + +} diff --git a/spring-brick-common/src/main/java/com/gitee/starblues/common/PluginDescriptorKey.java b/spring-brick-common/src/main/java/com/gitee/starblues/common/PluginDescriptorKey.java new file mode 100644 index 0000000000000000000000000000000000000000..11bd32e5c25128165fd9f592b8ec70c37385a4cb --- /dev/null +++ b/spring-brick-common/src/main/java/com/gitee/starblues/common/PluginDescriptorKey.java @@ -0,0 +1,48 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.common; + +/** + * 插件描述文件配置key + * + * @author starBlues + * @version 3.0.0 + */ +public class PluginDescriptorKey { + + + /** Must configure prop **/ + public static final String PLUGIN_ID = "plugin.id"; + public static final String PLUGIN_BOOTSTRAP_CLASS = "plugin.bootstrapClass"; + public static final String PLUGIN_VERSION = "plugin.version"; + + /** Optional configure prop **/ + public static final String PLUGIN_DESCRIPTION = "plugin.description"; + public static final String PLUGIN_PROVIDER = "plugin.provider"; + public static final String PLUGIN_DEPENDENCIES = "plugin.dependencies"; + public static final String PLUGIN_REQUIRES = "plugin.requires"; + public static final String PLUGIN_LICENSE = "plugin.license"; + public static final String PLUGIN_CONFIG_FILE_NAME = "plugin.configFileName"; + public static final String PLUGIN_CONFIG_FILE_LOCATION = "plugin.configFileLocation"; + public static final String PLUGIN_ARGS = "plugin.args"; + + /** System create prop **/ + public static final String PLUGIN_PATH = "plugin.system.path"; + public static final String PLUGIN_RESOURCES_CONFIG = "plugin.system.resourcesConfig"; + + +} diff --git a/spring-brick-common/src/main/java/com/gitee/starblues/utils/Assert.java b/spring-brick-common/src/main/java/com/gitee/starblues/utils/Assert.java new file mode 100644 index 0000000000000000000000000000000000000000..839a8ba19b28ff1c127745e0731aea15d6ef86b2 --- /dev/null +++ b/spring-brick-common/src/main/java/com/gitee/starblues/utils/Assert.java @@ -0,0 +1,88 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.utils; + + +import java.util.function.Supplier; + +/** + * 参数校验工具类 + * + * @author starBlues + * @version 3.0.0 + */ +public abstract class Assert { + + private Assert(){}; + + public static void isTrue(boolean expression, String message) { + if (!expression) { + throw new IllegalArgumentException(message); + } + } + + public static void isTrue(boolean expression, Supplier messageSupplier) { + if (!expression) { + throw new IllegalArgumentException(nullSafeGet(messageSupplier)); + } + } + + public static void state(boolean expression, String message) { + if (!expression) { + throw new IllegalStateException(message); + } + } + + public static void state(boolean expression, Supplier messageSupplier) { + if (!expression) { + throw new IllegalStateException(nullSafeGet(messageSupplier)); + } + } + + public static T isNotNull(T t, String message) { + if (t == null) { + throw new IllegalArgumentException(message); + } + return t; + } + + public static T isNotNull(T t, Supplier messageSupplier) { + if (t == null) { + throw new IllegalArgumentException(nullSafeGet(messageSupplier)); + } + return t; + } + + public static T isNotEmpty(T t, String message) { + if (ObjectUtils.isEmpty(t)) { + throw new IllegalArgumentException(message); + } + return t; + } + + public static T isNotEmpty(T t, Supplier messageSupplier) { + if (ObjectUtils.isEmpty(t)) { + throw new IllegalArgumentException(nullSafeGet(messageSupplier)); + } + return t; + } + + private static String nullSafeGet(Supplier messageSupplier) { + return (messageSupplier != null ? messageSupplier.get() : null); + } + +} diff --git a/spring-brick-common/src/main/java/com/gitee/starblues/utils/CompareClassTypeUtils.java b/spring-brick-common/src/main/java/com/gitee/starblues/utils/CompareClassTypeUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..1901f57edf7437b95fe290a1fe2c660b4a831f80 --- /dev/null +++ b/spring-brick-common/src/main/java/com/gitee/starblues/utils/CompareClassTypeUtils.java @@ -0,0 +1,100 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.utils; + +/** + * 比较两个类类型 + * + * @author starBlues + * @version 3.0.0 + */ +public abstract class CompareClassTypeUtils { + + private CompareClassTypeUtils(){} + + public static boolean compare(Class class1, Class class2){ + if(class1.isAssignableFrom(class2)){ + return true; + } + if(isBoolean(class1) && isBoolean(class2)){ + return true; + } + if(isChar(class1) && isChar(class2)){ + return true; + } + if(isByte(class1) && isByte(class2)){ + return true; + } + if(isShort(class1) && isShort(class2)){ + return true; + } + if(isInt(class1) && isInt(class2)){ + return true; + } + if(isLong(class1) && isLong(class2)){ + return true; + } + if(isFloat(class1) && isFloat(class2)){ + return true; + } + if(isDouble(class1) && isDouble(class2)){ + return true; + } + if(isVoid(class1) && isVoid(class2)){ + return true; + } + return false; + } + + + public static boolean isBoolean(Class class1){ + return class1.isAssignableFrom(Boolean.class) || class1.isAssignableFrom(boolean.class); + } + + public static boolean isChar(Class class1){ + return class1.isAssignableFrom(Character.class) || class1.isAssignableFrom(char.class); + } + + public static boolean isByte(Class class1){ + return class1.isAssignableFrom(Byte.class) || class1.isAssignableFrom(byte.class); + } + + public static boolean isShort(Class class1){ + return class1.isAssignableFrom(Short.class) || class1.isAssignableFrom(short.class); + } + + public static boolean isInt(Class class1){ + return class1.isAssignableFrom(Integer.class) || class1.isAssignableFrom(int.class); + } + + public static boolean isLong(Class class1){ + return class1.isAssignableFrom(Long.class) || class1.isAssignableFrom(long.class); + } + + public static boolean isFloat(Class class1){ + return class1.isAssignableFrom(Float.class) || class1.isAssignableFrom(float.class); + } + + public static boolean isDouble(Class class1){ + return class1.isAssignableFrom(Double.class) || class1.isAssignableFrom(double.class); + } + + public static boolean isVoid(Class class1){ + return class1.isAssignableFrom(Void.class) || class1.isAssignableFrom(void.class); + } + +} diff --git a/spring-brick-common/src/main/java/com/gitee/starblues/utils/FieldFilter.java b/spring-brick-common/src/main/java/com/gitee/starblues/utils/FieldFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..915882f8744061690ca37ca7d76f95e4fc0bc916 --- /dev/null +++ b/spring-brick-common/src/main/java/com/gitee/starblues/utils/FieldFilter.java @@ -0,0 +1,39 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.utils; + +import java.lang.reflect.Field; + +/** + * 文过滤接口 + * + * @author starBlues + * @version 3.0.0 + */ +@FunctionalInterface +public interface FieldFilter { + + /** + * 过滤 + * + * @param field 当前字段 + * @return true 允许, false 不允许 + */ + boolean filter(Field field); + + +} diff --git a/spring-brick-common/src/main/java/com/gitee/starblues/utils/FilesUtils.java b/spring-brick-common/src/main/java/com/gitee/starblues/utils/FilesUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..8f7bfa01c7f1c99a86c417f1024c53e25ffb22d4 --- /dev/null +++ b/spring-brick-common/src/main/java/com/gitee/starblues/utils/FilesUtils.java @@ -0,0 +1,116 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.utils; + +import com.gitee.starblues.common.Constants; + +import java.io.File; +import java.io.IOException; +import java.util.function.Supplier; + +/** + * 文件工具类 + * + * @author starBlues + * @version 3.0.0 + */ +public class FilesUtils { + + /** + * 获取存在的文件 + * + * @param pathStr 文件路径 + * @return File + */ + public static File getExistFile(String pathStr){ + File file = new File(pathStr); + if(file.exists()){ + return file; + } + return null; + } + + + /** + * 拼接file路径 + * + * @param paths 拼接的路径 + * @return 拼接的路径 + */ + public static String joiningFilePath(String ...paths){ + if(paths == null || paths.length == 0){ + return ""; + } + StringBuilder stringBuilder = new StringBuilder(); + int length = paths.length; + for (int i = 0; i < length; i++) { + String path = paths[i]; + if(ObjectUtils.isEmpty(path)) { + continue; + } + if(i > 0){ + if(path.startsWith(File.separator) || path.startsWith("/") || + path.startsWith("\\") || path.startsWith("//")){ + stringBuilder.append(path); + } else { + stringBuilder.append(File.separator).append(path); + } + } else { + stringBuilder.append(path); + } + } + + return stringBuilder.toString(); + } + + public static File createFile(String path) throws IOException { + try { + File file = new File(path); + File parentFile = file.getParentFile(); + if(!parentFile.exists()){ + if(!parentFile.mkdirs()){ + throw new IOException("Create " + parentFile + " dir error"); + } + } + if(file.createNewFile()){ + return file; + } + throw new IOException("Create " + path + " file error"); + } catch (Exception e){ + throw new IOException("Create " + path + " file error"); + } + } + + + /** + * 解决相对路径 + * @param rootPath 根路径 + * @param relativePath 以 ~ 开头的相对路径 + * @return 处理后的路径 + */ + public static String resolveRelativePath(String rootPath, String relativePath){ + if(ObjectUtils.isEmpty(relativePath)){ + return relativePath; + } + if(relativePath.startsWith(Constants.RELATIVE_SIGN)){ + return joiningFilePath(rootPath, relativePath.replaceFirst(Constants.RELATIVE_SIGN, "")); + } else { + return relativePath; + } + } + +} diff --git a/spring-brick-common/src/main/java/com/gitee/starblues/utils/ObjectUtils.java b/spring-brick-common/src/main/java/com/gitee/starblues/utils/ObjectUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..ba1baca9e73aeb386015fea1c5db717b40d0eb80 --- /dev/null +++ b/spring-brick-common/src/main/java/com/gitee/starblues/utils/ObjectUtils.java @@ -0,0 +1,472 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.utils; + + +import java.lang.reflect.Array; +import java.util.*; + +/** + * Object 工具类 + * + * @author starBlues + * @version 3.0.0 + */ +public class ObjectUtils { + + private static final String[] EMPTY_STRING_ARRAY = {}; + + private static final String FOLDER_SEPARATOR = "/"; + + private static final String WINDOWS_FOLDER_SEPARATOR = "\\"; + + private static final String TOP_PATH = ".."; + + private static final String CURRENT_PATH = "."; + + private static final char EXTENSION_SEPARATOR = '.'; + + private ObjectUtils(){} + + /** + * 字符串是否为空 + * + * @param charSequence 字符串 + * @return true 为空, false 不为空 + */ + public static boolean isEmpty(CharSequence charSequence) { + return charSequence == null || charSequence.length() == 0; + } + + /** + * 对象是否为空 + * + * @param obj 对象 + * @return true 为空, false 不为空 + */ + public static boolean isEmpty(Object obj) { + if (obj == null) { + return true; + } + + if (obj instanceof Optional) { + return !((Optional) obj).isPresent(); + } + if (obj instanceof CharSequence) { + return ((CharSequence) obj).length() == 0; + } + if (obj.getClass().isArray()) { + return Array.getLength(obj) == 0; + } + if (obj instanceof Collection) { + return ((Collection) obj).isEmpty(); + } + if (obj instanceof Map) { + return ((Map) obj).isEmpty(); + } + return false; + } + + /** + * 是否存在字符 + * + * @param str str + * @return true 存在, false 不存在 + */ + public static boolean hasText(CharSequence str) { + return (str != null && str.length() > 0 && containsText(str)); + } + + /** + * 设置数组字符的空格 + * + * @param array array + * @return result array + */ + public static String[] trimArrayElements(String[] array) { + if (isEmpty(array)) { + return array; + } + + String[] result = new String[array.length]; + for (int i = 0; i < array.length; i++) { + String element = array[i]; + result[i] = (element != null ? element.trim() : null); + } + return result; + } + + + /** + * 将逗号分割的字符串转换为字符转租 + * + * @param str 逗号分割的字符 + * @return 字符数组 + */ + public static String[] commaDelimitedListToStringArray(String str) { + return delimitedListToStringArray(str, ","); + } + + /** + * 自定义符号分割的字符串转换为字符转租 + * + * @param str 待处理字符串 + * @param delimiter 分割符号 + * @return 字符数组 + */ + public static String[] delimitedListToStringArray(String str, String delimiter) { + return delimitedListToStringArray(str, delimiter, null); + } + + /** + * 自定义符号分割的字符串转换为字符转租 + * + * @param str 待处理字符串 + * @param delimiter 分割符号 + * @param charsToDelete 要删除的字符 + * @return 字符数组 + */ + public static String[] delimitedListToStringArray( + String str, String delimiter, String charsToDelete) { + + if (str == null) { + return EMPTY_STRING_ARRAY; + } + if (delimiter == null) { + return new String[] {str}; + } + + List result = new ArrayList<>(); + if (delimiter.isEmpty()) { + for (int i = 0; i < str.length(); i++) { + result.add(deleteAny(str.substring(i, i + 1), charsToDelete)); + } + } + else { + int pos = 0; + int delPos; + while ((delPos = str.indexOf(delimiter, pos)) != -1) { + result.add(deleteAny(str.substring(pos, delPos), charsToDelete)); + pos = delPos + delimiter.length(); + } + if (str.length() > 0 && pos <= str.length()) { + // Add rest of String, but not in case of empty input. + result.add(deleteAny(str.substring(pos), charsToDelete)); + } + } + return toStringArray(result); + } + + /** + * 将集合字符串转换为数组字符串 + * + * @param collection 集合 + * @return 数字字符串 + */ + public static String[] toStringArray(Collection collection) { + return (!isEmpty(collection) ? collection.toArray(EMPTY_STRING_ARRAY) : EMPTY_STRING_ARRAY); + } + + /** + * 从字符串中删除某个字符字符 + * + * @param inString 待处理字符串 + * @param charsToDelete 要删除的字符 + * @return 处理后的字符串 + */ + public static String deleteAny(String inString, String charsToDelete) { + if (!hasLength(inString) || !hasLength(charsToDelete)) { + return inString; + } + + int lastCharIndex = 0; + char[] result = new char[inString.length()]; + for (int i = 0; i < inString.length(); i++) { + char c = inString.charAt(i); + if (charsToDelete.indexOf(c) == -1) { + result[lastCharIndex++] = c; + } + } + if (lastCharIndex == inString.length()) { + return inString; + } + return new String(result, 0, lastCharIndex); + } + + /** + * 字符串是否存在长度 + * + * @param str 字符串 + * @return true 存在长度, false 不存在长度 + */ + public static boolean hasLength(String str) { + return (str != null && !str.isEmpty()); + } + + /** + * 将集合字转换为逗号分割的字符串 + * + * @param coll 集合 + * @return 字符串 + */ + public static String collectionToCommaDelimitedString(Collection coll) { + return collectionToDelimitedString(coll, ","); + } + + /** + * 将集合字转换为逗号分割的字符串 + * + * @param coll 集合 + * @param delim 分割符号 + * @return 字符串 + */ + public static String collectionToDelimitedString(Collection coll, String delim) { + return collectionToDelimitedString(coll, delim, "", ""); + } + + public static String collectionToDelimitedString( + Collection coll, String delim, String prefix, String suffix) { + + if (isEmpty(coll)) { + return ""; + } + + StringBuilder sb = new StringBuilder(); + Iterator it = coll.iterator(); + while (it.hasNext()) { + sb.append(prefix).append(it.next()).append(suffix); + if (it.hasNext()) { + sb.append(delim); + } + } + return sb.toString(); + } + + + public static boolean containsElement(Object[] array, Object element) { + if (array == null) { + return false; + } + for (Object arrayEle : array) { + if (nullSafeEquals(arrayEle, element)) { + return true; + } + } + return false; + } + + public static boolean nullSafeEquals(Object o1, Object o2) { + if (o1 == o2) { + return true; + } + if (o1 == null || o2 == null) { + return false; + } + if (o1.equals(o2)) { + return true; + } + if (o1.getClass().isArray() && o2.getClass().isArray()) { + return arrayEquals(o1, o2); + } + return false; + } + + public static boolean endsWithIgnoreCase(String str, String suffix) { + return (str != null && suffix != null && str.length() >= suffix.length() && + str.regionMatches(true, str.length() - suffix.length(), suffix, 0, suffix.length())); + } + + public static String getFilenameExtension(String path) { + if (path == null) { + return null; + } + + int extIndex = path.lastIndexOf(EXTENSION_SEPARATOR); + if (extIndex == -1) { + return null; + } + + int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR); + if (folderIndex > extIndex) { + return null; + } + + return path.substring(extIndex + 1); + } + + public static String cleanPath(String path) { + if (!hasLength(path)) { + return path; + } + String pathToUse = replace(path, WINDOWS_FOLDER_SEPARATOR, FOLDER_SEPARATOR); + + // Shortcut if there is no work to do + if (pathToUse.indexOf('.') == -1) { + return pathToUse; + } + + int prefixIndex = pathToUse.indexOf(':'); + String prefix = ""; + if (prefixIndex != -1) { + prefix = pathToUse.substring(0, prefixIndex + 1); + if (prefix.contains(FOLDER_SEPARATOR)) { + prefix = ""; + } + else { + pathToUse = pathToUse.substring(prefixIndex + 1); + } + } + if (pathToUse.startsWith(FOLDER_SEPARATOR)) { + prefix = prefix + FOLDER_SEPARATOR; + pathToUse = pathToUse.substring(1); + } + + String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR); + Deque pathElements = new ArrayDeque<>(); + int tops = 0; + + for (int i = pathArray.length - 1; i >= 0; i--) { + String element = pathArray[i]; + if (CURRENT_PATH.equals(element)) { + // Points to current directory - drop it. + } + else if (TOP_PATH.equals(element)) { + // Registering top path found. + tops++; + } + else { + if (tops > 0) { + // Merging path element with element corresponding to top path. + tops--; + } + else { + // Normal path element found. + pathElements.addFirst(element); + } + } + } + + // All path elements stayed the same - shortcut + if (pathArray.length == pathElements.size()) { + return prefix + pathToUse; + } + // Remaining top paths need to be retained. + for (int i = 0; i < tops; i++) { + pathElements.addFirst(TOP_PATH); + } + // If nothing else left, at least explicitly point to current path. + if (pathElements.size() == 1 && pathElements.getLast().isEmpty() && !prefix.endsWith(FOLDER_SEPARATOR)) { + pathElements.addFirst(CURRENT_PATH); + } + + return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR); + } + + + public static String replace(String inString, String oldPattern, String newPattern) { + if (!hasLength(inString) || !hasLength(oldPattern) || newPattern == null) { + return inString; + } + int index = inString.indexOf(oldPattern); + if (index == -1) { + // no occurrence -> can return input as-is + return inString; + } + + int capacity = inString.length(); + if (newPattern.length() > oldPattern.length()) { + capacity += 16; + } + StringBuilder sb = new StringBuilder(capacity); + + int pos = 0; // our position in the old string + int patLen = oldPattern.length(); + while (index >= 0) { + sb.append(inString, pos, index); + sb.append(newPattern); + pos = index + patLen; + index = inString.indexOf(oldPattern, pos); + } + + // append any characters to the right of a match + sb.append(inString, pos, inString.length()); + return sb.toString(); + } + + + public static int countOccurrencesOf(String str, String sub) { + if (!hasLength(str) || !hasLength(sub)) { + return 0; + } + + int count = 0; + int pos = 0; + int idx; + while ((idx = str.indexOf(sub, pos)) != -1) { + ++count; + pos = idx + sub.length(); + } + return count; + } + + public static String changePackageToMatch(String packageName){ + Assert.isNotEmpty(packageName, "参数 packageName 不能为空"); + return packageName.replace(".", "/") + "/**"; + } + + private static boolean containsText(CharSequence str) { + int strLen = str.length(); + for (int i = 0; i < strLen; i++) { + if (!Character.isWhitespace(str.charAt(i))) { + return true; + } + } + return false; + } + + private static boolean arrayEquals(Object o1, Object o2) { + if (o1 instanceof Object[] && o2 instanceof Object[]) { + return Arrays.equals((Object[]) o1, (Object[]) o2); + } + if (o1 instanceof boolean[] && o2 instanceof boolean[]) { + return Arrays.equals((boolean[]) o1, (boolean[]) o2); + } + if (o1 instanceof byte[] && o2 instanceof byte[]) { + return Arrays.equals((byte[]) o1, (byte[]) o2); + } + if (o1 instanceof char[] && o2 instanceof char[]) { + return Arrays.equals((char[]) o1, (char[]) o2); + } + if (o1 instanceof double[] && o2 instanceof double[]) { + return Arrays.equals((double[]) o1, (double[]) o2); + } + if (o1 instanceof float[] && o2 instanceof float[]) { + return Arrays.equals((float[]) o1, (float[]) o2); + } + if (o1 instanceof int[] && o2 instanceof int[]) { + return Arrays.equals((int[]) o1, (int[]) o2); + } + if (o1 instanceof long[] && o2 instanceof long[]) { + return Arrays.equals((long[]) o1, (long[]) o2); + } + if (o1 instanceof short[] && o2 instanceof short[]) { + return Arrays.equals((short[]) o1, (short[]) o2); + } + return false; + } + +} diff --git a/spring-brick-common/src/main/java/com/gitee/starblues/utils/PropertiesUtils.java b/spring-brick-common/src/main/java/com/gitee/starblues/utils/PropertiesUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..22700c7aaf30d25fe0655fc3b455fc9c5abde682 --- /dev/null +++ b/spring-brick-common/src/main/java/com/gitee/starblues/utils/PropertiesUtils.java @@ -0,0 +1,68 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.utils; + +import java.util.Properties; + +/** + * 操作 Manifest 工具类 + * + * @author starBlues + * @version 3.0.0 + */ +public abstract class PropertiesUtils { + + private PropertiesUtils(){} + + /** + * 获取值 + * + * @param properties properties + * @param key 获取的key + * @return 获取的值或者null + */ + public static String getValue(Properties properties, String key){ + return getValue(properties, key, true); + } + + /** + * 获取值 + * + * @param properties properties + * @param key 获取的key + * @param notExitsThrowException 如果不存在是否抛出异常 + * @return 获取的值 + */ + public static String getValue(Properties properties, String key, boolean notExitsThrowException){ + boolean throwException = false; + String value = null; + try { + value = properties.getProperty(key); + if(value == null && notExitsThrowException){ + throwException = true; + } + } catch (Throwable e){ + // 忽略 + throwException = true; + } + if(throwException){ + throw new IllegalStateException("Not found '" + key + "' config!"); + } + return value; + } + +} diff --git a/spring-brick-common/src/main/java/com/gitee/starblues/utils/ReflectionUtils.java b/spring-brick-common/src/main/java/com/gitee/starblues/utils/ReflectionUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..a087e073721af99808894838eae32411dcbd4dad --- /dev/null +++ b/spring-brick-common/src/main/java/com/gitee/starblues/utils/ReflectionUtils.java @@ -0,0 +1,247 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.utils; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.*; + +/** + * 反射工具类 + * + * @author starBlues + * @version 3.0.0 + */ +public abstract class ReflectionUtils { + + private static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; + + private ReflectionUtils(){} + + public static Field findField(Class clazz, String name) { + return findField(clazz, name, null); + } + + public static Field findField(Class clazz, Class fieldType) { + return findField(clazz, null, fieldType); + } + + public static Field findField(Class clazz, String fieldName, Class fieldType) { + if(fieldName == null && fieldType == null){ + throw new IllegalArgumentException("fieldName or fieldType of the field must be specified"); + } + return findField(clazz, (field)->{ + if ((fieldName == null || fieldName.equals(field.getName())) && + (fieldType == null || fieldType.equals(field.getType()))) { + return true; + } + return false; + }); + } + + public static Field findField(Class clazz, FieldFilter filter) { + Objects.requireNonNull(clazz, "clazz must not be null"); + + + Field[] declaredFields = clazz.getDeclaredFields(); + while (true){ + for (Field field : declaredFields) { + if (filter.filter(field)) { + return field; + } + } + clazz = clazz.getSuperclass(); + if(clazz == null || clazz == Object.class){ + break; + } + declaredFields = clazz.getDeclaredFields(); + } + + return null; + } + + + public static Object getField(Object object, Class objectClass, String fieldName) { + return getField(object, objectClass, fieldName, null); + } + + public static Object getField(Object object, Class objectClass, String fieldName, Class fieldType) { + Field field = findField(objectClass, fieldName, fieldType); + if(field == null){ + return null; + } + field.setAccessible(true); + try { + return field.get(object); + } catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + + + public static void setField(Object o, String fieldName, Object value) { + setField(o, fieldName, null, value); + } + + public static void setField(Object o, String fieldName, Class fieldType, Object value) { + Field field = findField(o.getClass(), fieldName, fieldType); + if(field != null){ + field.setAccessible(true); + try { + field.set(o, value); + } catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + } + + public static Method findMethod(Class clazz, String name) { + return findMethod(clazz, name, EMPTY_CLASS_ARRAY); + } + + public static Method findMethod(Class clazz, String name, Class... paramTypes) { + List methods = findMethods(clazz, name, paramTypes); + if(ObjectUtils.isEmpty(methods)){ + return null; + } else { + return methods.get(0); + } + } + + public static List findMethods(Class clazz, String name, Class... paramTypes){ + Assert.isNotNull(clazz, "Class must not be null"); + Assert.isNotEmpty(name, "Method name must not be null"); + Class searchType = clazz; + List methodList = new ArrayList<>(); + while (searchType != null) { + Method[] methods = (searchType.isInterface() ? searchType.getMethods() : + getDeclaredMethods(searchType, false)); + for (Method method : methods) { + if (name.equals(method.getName()) && (paramTypes == null || hasSameParams(method, paramTypes))) { + methodList.add(method); + } + } + searchType = searchType.getSuperclass(); + } + return methodList; + } + + @SuppressWarnings("unchecked") + public static T invoke(Object object, String methodName, Object... params) throws RuntimeException { + Class[] paramTypes = new Class[params.length]; + for (int i = 0; i < params.length; i++) { + paramTypes[i] = params[i].getClass(); + } + Class aClass = object.getClass(); + Method method = ReflectionUtils.findMethod(aClass, methodName, paramTypes); + if(method == null){ + throw new RuntimeException("Not found method : " + methodToString(aClass, methodName, paramTypes)); + } + try { + Object invoke = method.invoke(object, params); + if(invoke != null){ + return (T) invoke; + } else { + return null; + } + } catch (Exception e) { + throw new RuntimeException("Cannot call method : " + methodToString(aClass, methodName, paramTypes), e); + } + } + + public static void setAttribute(Object bean, String setMethodName, Object setObject) throws Exception { + Class aClass = bean.getClass(); + Method setMethod = ReflectionUtils.findMethod(aClass, setMethodName, setObject.getClass()); + + if(setMethod == null){ + throw new Exception("Not found method[" + setMethodName + "] of :" + aClass.getName()); + } + setMethod.invoke(bean, setObject); + } + + private static Method[] getDeclaredMethods(Class clazz, boolean defensive) { + Assert.isNotNull(clazz, "Class must not be null"); + Method[] result = new Method[]{}; + try { + Method[] declaredMethods = clazz.getDeclaredMethods(); + List defaultMethods = findConcreteMethodsOnInterfaces(clazz); + + if (defaultMethods != null) { + result = new Method[declaredMethods.length + defaultMethods.size()]; + System.arraycopy(declaredMethods, 0, result, 0, declaredMethods.length); + int index = declaredMethods.length; + for (Method defaultMethod : defaultMethods) { + result[index] = defaultMethod; + index++; + } + } else { + result = declaredMethods; + } + } catch (Throwable ex) { + throw new IllegalStateException("Failed to introspect Class [" + clazz.getName() + + "] from ClassLoader [" + clazz.getClassLoader() + "]", ex); + } + return result; + } + + private static List findConcreteMethodsOnInterfaces(Class clazz) { + List result = null; + for (Class ifc : clazz.getInterfaces()) { + for (Method ifcMethod : ifc.getMethods()) { + if (!Modifier.isAbstract(ifcMethod.getModifiers())) { + if (result == null) { + result = new ArrayList<>(); + } + result.add(ifcMethod); + } + } + } + return result; + } + + private static boolean hasSameParams(Method method, Class[] paramTypes) { + if(paramTypes.length != method.getParameterCount()){ + return false; + } + Class[] parameterTypes = method.getParameterTypes(); + for (int i = 0; i < paramTypes.length; i++) { + Class paramType = paramTypes[i]; + Class methodParamType = parameterTypes[i]; + if(!CompareClassTypeUtils.compare(methodParamType, paramType)){ + return false; + } + } + return true; + } + + + public static NoSuchMethodException getNoSuchMethodException(Class aClass, String name, Class[] argTypes) { + return new NoSuchMethodException("Not found method:" + methodToString(aClass, name, argTypes)); + } + + public static String methodToString(Class aClass, String name, Class[] argTypes) { + StringJoiner sj = new StringJoiner(", ", aClass.getName() + "." + name + "(", ")"); + if (argTypes != null) { + for (Class c : argTypes) { + sj.add((c == null) ? "null" : c.getName()); + } + } + return sj.toString(); + } + +} diff --git a/spring-brick-common/src/main/java/com/gitee/starblues/utils/ResourceUtils.java b/spring-brick-common/src/main/java/com/gitee/starblues/utils/ResourceUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..a75d3ec17ef3a4b76577879d90882d0422dc6478 --- /dev/null +++ b/spring-brick-common/src/main/java/com/gitee/starblues/utils/ResourceUtils.java @@ -0,0 +1,253 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.utils; + + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; +import java.util.function.Consumer; + +/** + * 资源工具类 + * + * @author starBlues + * @version 3.0.0 + */ +public class ResourceUtils { + + public static final String URL_PROTOCOL_FILE = "file"; + + public final static String TYPE_FILE = "file"; + public final static String TYPE_CLASSPATH = "classpath"; + public final static String TYPE_PACKAGE = "package"; + + public static final String ROOT_PLUGIN_SIGN = "~"; + + public final static String TYPE_SPLIT = ":"; + + public static final String CLASSPATH_URL_PREFIX = "classpath:"; + public static final String FILE_URL_PREFIX = "file:"; + + public static final String PACKAGE_SPLIT = "/"; + public static final String PACKAGE_SPLIT_DOT = "."; + + public static final String JAR_FILE_EXTENSION = ".jar"; + public static final String ZIP_FILE_EXTENSION = ".zip"; + public static final String CLASS_FILE_EXTENSION = ".class"; + public static final String URL_PROTOCOL_VFSFILE = "vfsfile"; + public static final String URL_PROTOCOL_VFS = "vfs"; + + /** + * 获取匹配路绝 + * @param locationMatch 原始匹配路径。规则为: file:xxx, classpath:xxx , package:xxx + * @return 整合出完整的匹配路绝 + */ + public static String getMatchLocation(String locationMatch){ + if(ObjectUtils.isEmpty(locationMatch)){ + return null; + } + String classPathType = TYPE_CLASSPATH + TYPE_SPLIT; + if(isClasspath(locationMatch)){ + return locationMatch.replaceFirst(classPathType, ""); + } + String fileType = TYPE_FILE + TYPE_SPLIT; + if(isFile(locationMatch)){ + return locationMatch.replaceFirst(fileType, ""); + } + String packageType = TYPE_PACKAGE + TYPE_SPLIT; + if(isPackage(locationMatch)){ + String location = locationMatch.replaceFirst(packageType, ""); + return replacePackage(location); + } + return null; + } + + public static boolean isClasspath(String locationMatch){ + if(ObjectUtils.isEmpty(locationMatch)){ + return false; + } + return locationMatch.startsWith(TYPE_CLASSPATH + TYPE_SPLIT); + } + + public static boolean isFile(String locationMatch){ + if(ObjectUtils.isEmpty(locationMatch)){ + return false; + } + return locationMatch.startsWith(TYPE_FILE + TYPE_SPLIT); + } + + public static boolean isPackage(String locationMatch){ + if(ObjectUtils.isEmpty(locationMatch)){ + return false; + } + return locationMatch.startsWith(TYPE_PACKAGE + TYPE_SPLIT); + } + + public static boolean isClass(String path){ + if(ObjectUtils.isEmpty(path)){ + return false; + } + return path.toLowerCase().endsWith(CLASS_FILE_EXTENSION); + } + + /** + * 是否为 zip 文件 + * @param path 文件路径 + * @return boolean + */ + public static boolean isZipFile(Path path) { + return Files.isRegularFile(path) && isZip(path.toString()); + } + + /** + * 是否为 jar 文件 + * @param path 文件路径 + * @return boolean + */ + public static boolean isJarFile(Path path) { + return Files.isRegularFile(path) && isJar(path.toString()); + } + + /** + * 是否为 zip 文件 + * @param name 文件名称 + * @return boolean + */ + public static boolean isZip(String name) { + return name != null && name.toLowerCase().endsWith(ZIP_FILE_EXTENSION); + } + + /** + * 是否为 jar 文件 + * @param name 文件名称 + * @return boolean + */ + public static boolean isJar(String name) { + return name != null && name.toLowerCase().endsWith(JAR_FILE_EXTENSION); + } + + public static boolean isDirFile(Path path) { + return path.toFile().isDirectory(); + } + + public static boolean isJarFileURL(URL url) { + return (URL_PROTOCOL_FILE.equals(url.getProtocol()) || + url.getPath().toLowerCase().endsWith(JAR_FILE_EXTENSION)); + } + + public static boolean isFileURL(URL url) { + String protocol = url.getProtocol(); + return (URL_PROTOCOL_FILE.equals(protocol) || URL_PROTOCOL_VFSFILE.equals(protocol) || + URL_PROTOCOL_VFS.equals(protocol)); + } + + + public static boolean existFile(File rootFile, String checkFileName){ + if(rootFile == null || !rootFile.exists()){ + return false; + } + final File[] listFiles = rootFile.listFiles(); + if(listFiles == null || listFiles.length == 0){ + return false; + } + for (File listFile : listFiles) { + if(Objects.equals(listFile.getName(), checkFileName)){ + return true; + } + if(listFile.isDirectory()){ + return existFile(listFile, checkFileName); + } + } + return false; + } + + public static void listFile(File rootFile, Consumer consumerFile){ + if(rootFile == null || !rootFile.exists()){ + return; + } + final File[] listFiles = rootFile.listFiles(); + if(listFiles == null || listFiles.length == 0){ + return; + } + for (File listFile : listFiles) { + if(listFile.isDirectory()){ + listFile(listFile, consumerFile); + continue; + } + consumerFile.accept(listFile); + } + } + + public static String replacePackage(String packageName){ + if(packageName == null){ + return null; + } + return packageName.replace(PACKAGE_SPLIT_DOT, PACKAGE_SPLIT); + } + + + public static boolean isUrl(String resourceLocation) { + if (resourceLocation == null) { + return false; + } + if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) { + return true; + } + try { + new URL(resourceLocation); + return true; + } + catch (MalformedURLException ex) { + return false; + } + } + + public static void closeQuietly(Object closeable){ + closeQuietly(closeable, null); + } + + public static void closeQuietly(Object closeable, Consumer consumer){ + if (closeable != null) { + try { + if(closeable instanceof AutoCloseable){ + ((AutoCloseable) closeable).close(); + } + } catch (final Exception e) { + if (consumer != null) { + consumer.accept(e); + } + } + } + } + + public static String getFileSuffix(String fileName){ + if(fileName == null){ + return null; + } + int i = fileName.lastIndexOf("."); + if(i > 0 && fileName.length() >= (i + 1)){ + return fileName.substring(i + 1); + } else { + return ""; + } + } + +} diff --git a/spring-brick-common/src/main/java/com/gitee/starblues/utils/StringUtils.java b/spring-brick-common/src/main/java/com/gitee/starblues/utils/StringUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..fc2abbbaf72c1926f30e056bb69251046564deed --- /dev/null +++ b/spring-brick-common/src/main/java/com/gitee/starblues/utils/StringUtils.java @@ -0,0 +1,62 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.utils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.StringTokenizer; + +/** + * String 工具类 + * + * @author starBlues + * @version 3.0.0 + */ +public class StringUtils { + + private static final String[] EMPTY_STRING_ARRAY = {}; + + private static String[] tokenizeLocaleSource(String localeSource) { + return tokenizeToStringArray(localeSource, "_ ", false, false); + } + + public static String[] tokenizeToStringArray(String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) { + + if (str == null) { + return EMPTY_STRING_ARRAY; + } + + StringTokenizer st = new StringTokenizer(str, delimiters); + List tokens = new ArrayList<>(); + while (st.hasMoreTokens()) { + String token = st.nextToken(); + if (trimTokens) { + token = token.trim(); + } + if (!ignoreEmptyTokens || token.length() > 0) { + tokens.add(token); + } + } + return toStringArray(tokens); + } + + public static String[] toStringArray(Collection collection) { + return (!ObjectUtils.isEmpty(collection) ? collection.toArray(EMPTY_STRING_ARRAY) : EMPTY_STRING_ARRAY); + } + +} diff --git a/spring-brick-common/src/main/java/com/gitee/starblues/utils/UrlUtils.java b/spring-brick-common/src/main/java/com/gitee/starblues/utils/UrlUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..90b80dbc4dbd3a069e095ec4aca0477ee11a266a --- /dev/null +++ b/spring-brick-common/src/main/java/com/gitee/starblues/utils/UrlUtils.java @@ -0,0 +1,113 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.utils; + +/** + * http url util + * + * @author starBlues + * @version 3.0.0 + */ +public class UrlUtils { + + private UrlUtils(){} + + public static final String SPLIT = "/"; + + /** + * rest接口拼接路径 + * + * @param path1 路径1 + * @param path2 路径2 + * @return 拼接的路径 + */ + public static String restJoiningPath(String path1, String path2){ + if(path1 != null && path2 != null){ + if(path1.endsWith(SPLIT) && path2.startsWith(SPLIT)){ + return path1 + path2.substring(1); + } else if(!path1.endsWith(SPLIT) && !path2.startsWith(SPLIT)){ + return path1 + SPLIT + path2; + } else { + return path1 + path2; + } + } else if(path1 != null){ + return path1; + } else if(path2 != null){ + return path2; + } else { + return ""; + } + } + + + /** + * 拼接url路径 + * + * @param paths 拼接的路径 + * @return 拼接的路径 + */ + public static String joiningUrlPath(String ...paths){ + if(paths == null || paths.length == 0){ + return ""; + } + StringBuilder stringBuilder = new StringBuilder(); + int length = paths.length; + for (int i = 0; i < length; i++) { + String path = paths[i]; + if(ObjectUtils.isEmpty(path)) { + continue; + } + if((i < length - 1) && path.endsWith(SPLIT)){ + path = path.substring(path.lastIndexOf(SPLIT)); + } + if(path.startsWith(SPLIT)){ + stringBuilder.append(path); + } else { + stringBuilder.append(SPLIT).append(path); + } + } + + return stringBuilder.toString(); + } + + /** + * 格式化 url + * @param url 原始url + * @return 格式化后的url + */ + public static String format(String url){ + if(ObjectUtils.isEmpty(url)){ + return url; + } + String[] split = url.split(SPLIT); + StringBuilder stringBuilder = new StringBuilder(); + int length = split.length; + for (int i = 0; i < length; i++) { + String str = split[i]; + if(ObjectUtils.isEmpty(str)){ + continue; + } + if(i < length - 1){ + stringBuilder.append(str).append(SPLIT); + } else { + stringBuilder.append(str); + } + } + return stringBuilder.toString(); + } + +} diff --git a/spring-brick-loader/pom.xml b/spring-brick-loader/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..8159265ea799e8d748a4d58b16bfab6e58b57a71 --- /dev/null +++ b/spring-brick-loader/pom.xml @@ -0,0 +1,16 @@ + + + + spring-brick-parent + com.gitee.starblues + 3.0.0 + + 4.0.0 + + spring-brick-loader + + + + \ No newline at end of file diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/PluginResourceStorage.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/PluginResourceStorage.java new file mode 100644 index 0000000000000000000000000000000000000000..056596aa54aad24897b4535cbaedd80367e45f7f --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/PluginResourceStorage.java @@ -0,0 +1,152 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader; + +import com.gitee.starblues.loader.jar.AbstractJarFile; +import com.gitee.starblues.loader.jar.JarFile; +import com.gitee.starblues.loader.jar.JarFileWrapper; +import com.gitee.starblues.loader.utils.IOUtils; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 插件资源存储者 + * @author starBlues + * @version 3.0.0 + */ +public class PluginResourceStorage { + + public final static Map STORAGE_MAP = new ConcurrentHashMap<>(); + + /** + * 添加插件资源 + * @param pluginId 插件id + * @param pluginFileName 插件文件名称 + */ + public static void addPlugin(String pluginId, String pluginFileName){ + if(STORAGE_MAP.containsKey(pluginId)){ + return; + } + STORAGE_MAP.put(pluginId, new Storage(pluginFileName)); + } + + /** + * 移除插件 + * @param pluginId 插件 + */ + public static void removePlugin(String pluginId){ + Storage storage = STORAGE_MAP.get(pluginId); + if(storage == null){ + return; + } + IOUtils.closeQuietly(storage); + STORAGE_MAP.remove(pluginId); + } + + /** + * 添加插件jar文件 + * @param jarFile jar插件文件 + */ + public static void addJarFile(AbstractJarFile jarFile){ + STORAGE_MAP.forEach((k,v)->{ + v.addJarFile(jarFile.getName(), jarFile); + }); + } + + /** + * 添加插件根的jar文件 + * @param file 插件文件 + * @param jarFile 插件jar文件 + */ + public static void addRootJarFile(File file, JarFile jarFile){ + STORAGE_MAP.forEach((k,v)->{ + v.addRootJarFile(file, jarFile); + }); + } + + /** + * 通过插件文件获取插件jar文件 + * @param file 插件文件 + * @return 插件jar文件 + */ + public static JarFile getRootJarFile(File file){ + for (Storage value : STORAGE_MAP.values()) { + JarFile jarFile = value.getRootJarFile(file); + if(jarFile != null){ + return jarFile; + } + } + return null; + } + + + private static class Storage implements Closeable { + private final String pluginFileName; + private final Map rootJarFileMap = new ConcurrentHashMap<>(); + private final Map> jarFileMap = new ConcurrentHashMap<>(); + + public Storage(String pluginFileName) { + this.pluginFileName = pluginFileName; + } + + public void addJarFile(String name, AbstractJarFile jarFile){ + if(name == null || jarFile == null){ + return; + } + if(name.contains(pluginFileName)){ + List jarFiles = jarFileMap.computeIfAbsent(name, k -> new ArrayList<>()); + jarFiles.add(jarFile); + } + } + + public void addRootJarFile(File file, JarFile jarFile){ + String absolutePath = file.getAbsolutePath(); + if(absolutePath.contains(pluginFileName)){ + rootJarFileMap.put(file, jarFile); + } + } + + public JarFile getRootJarFile(File file){ + return rootJarFileMap.get(file); + } + + @Override + public void close() throws IOException { + for (List value : jarFileMap.values()) { + for (AbstractJarFile jarFile : value) { + if(jarFile == null){ + continue; + } + if(jarFile instanceof JarFileWrapper){ + ((JarFileWrapper)jarFile).canClosed(); + } + IOUtils.closeQuietly(jarFile); + } + } + jarFileMap.clear(); + rootJarFileMap.clear(); + } + } + + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/archive/Archive.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/archive/Archive.java new file mode 100644 index 0000000000000000000000000000000000000000..50bdc2c82e8090145c09c247b1e477242fa64bfc --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/archive/Archive.java @@ -0,0 +1,142 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.archive; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.*; +import java.util.function.Consumer; +import java.util.jar.Manifest; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public interface Archive extends Iterable, AutoCloseable { + + /** + * Returns a URL that can be used to load the archive. + * @return the archive URL + * @throws MalformedURLException if the URL is malformed + */ + URL getUrl() throws MalformedURLException; + + /** + * Returns the manifest of the archive. + * @return the manifest + * @throws IOException if the manifest cannot be read + */ + Manifest getManifest() throws IOException; + + /** + * Returns nested {@link Archive}s for entries that match the specified filters. + * @param searchFilter filter used to limit when additional sub-entry searching is + * required or {@code null} if all entries should be considered. + * @param includeFilter filter used to determine which entries should be included in + * the result or {@code null} if all entries should be included + * @return the nested archives + * @throws IOException io exception + * @since 2.3.0 + */ + Iterator getNestedArchives(EntryFilter searchFilter, EntryFilter includeFilter) throws IOException; + + /** + * Return a new iterator for the archive entries. + * @return {@link Iterable} + */ + @Override + Iterator iterator(); + + /** + * Performs the given action for each element of the {@code Iterable} until all + * elements have been processed or the action throws an exception. + * @param action consumer entry + */ + @Override + default void forEach(Consumer action) { + Objects.requireNonNull(action); + for (Entry entry : this) { + action.accept(entry); + } + } + + /** + * Creates a {@link Spliterator} over the elements described by this {@code Iterable}. + * @see Iterable#spliterator + * @return {@link Spliterator} + */ + @Override + default Spliterator spliterator() { + return Spliterators.spliteratorUnknownSize(iterator(), 0); + } + + /** + * Return if the archive is exploded (already unpacked). + * @return if the archive is exploded + * @since 2.3.0 + */ + default boolean isExploded() { + return false; + } + + /** + * Closes the {@code Archive}, releasing any open resources. + * @throws Exception if an error occurs during close processing + * @since 2.2.0 + */ + @Override + default void close() throws Exception { + + } + + /** + * Represents a single entry in the archive. + */ + interface Entry { + + /** + * Returns {@code true} if the entry represents a directory. + * @return if the entry is a directory + */ + boolean isDirectory(); + + /** + * Returns the name of the entry. + * @return the name of the entry + */ + String getName(); + + } + + /** + * Strategy interface to filter {@link Entry Entries}. + */ + @FunctionalInterface + interface EntryFilter { + + /** + * Apply the jar entry filter. + * @param entry the entry to filter + * @return {@code true} if the filter matches + */ + boolean matches(Entry entry); + + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/archive/ExplodedArchive.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/archive/ExplodedArchive.java new file mode 100644 index 0000000000000000000000000000000000000000..284a2700716deeda6ab2e2d44e9d677c943ac6d4 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/archive/ExplodedArchive.java @@ -0,0 +1,334 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.archive; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.util.*; +import java.util.jar.Manifest; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public class ExplodedArchive implements Archive { + + private static final Set SKIPPED_NAMES = new HashSet<>(Arrays.asList(".", "..")); + + private final File root; + + private final boolean recursive; + + private final File manifestFile; + + private Manifest manifest; + + /** + * Create a new {@link ExplodedArchive} instance. + * @param root the root directory + */ + public ExplodedArchive(File root) { + this(root, true); + } + + /** + * Create a new {@link ExplodedArchive} instance. + * @param root the root directory + * @param recursive if recursive searching should be used to locate the manifest. + * Defaults to {@code true}, directories with a large tree might want to set this to + * {@code false}. + */ + public ExplodedArchive(File root, boolean recursive) { + if (!root.exists() || !root.isDirectory()) { + throw new IllegalArgumentException("Invalid source directory " + root); + } + this.root = root; + this.recursive = recursive; + this.manifestFile = getManifestFile(root); + } + + private File getManifestFile(File root) { + File metaInf = new File(root, "META-INF"); + return new File(metaInf, "MANIFEST.MF"); + } + + @Override + public URL getUrl() throws MalformedURLException { + return this.root.toURI().toURL(); + } + + @Override + public Manifest getManifest() throws IOException { + if (this.manifest == null && this.manifestFile.exists()) { + try (FileInputStream inputStream = new FileInputStream(this.manifestFile)) { + this.manifest = new Manifest(inputStream); + } + } + return this.manifest; + } + + @Override + public Iterator getNestedArchives(EntryFilter searchFilter, EntryFilter includeFilter) throws IOException { + return new ArchiveIterator(this.root, this.recursive, searchFilter, includeFilter); + } + + @Override + public Iterator iterator() { + return new EntryIterator(this.root, this.recursive, null, null); + } + + protected Archive getNestedArchive(Entry entry) throws IOException { + File file = ((FileEntry) entry).getFile(); + return (file.isDirectory() ? new ExplodedArchive(file) : new SimpleJarFileArchive((FileEntry) entry)); + } + + @Override + public boolean isExploded() { + return true; + } + + @Override + public String toString() { + try { + return getUrl().toString(); + } + catch (Exception ex) { + return "exploded archive"; + } + } + + /** + * File based {@link Entry} {@link Iterator}. + */ + private abstract static class AbstractIterator implements Iterator { + + private static final Comparator ENTRY_COMPARATOR = Comparator.comparing(File::getAbsolutePath); + + private final File root; + + private final boolean recursive; + + private final EntryFilter searchFilter; + + private final EntryFilter includeFilter; + + private final Deque> stack = new LinkedList<>(); + + private FileEntry current; + + private final String rootUrl; + + AbstractIterator(File root, boolean recursive, EntryFilter searchFilter, EntryFilter includeFilter) { + this.root = root; + this.rootUrl = this.root.toURI().getPath(); + this.recursive = recursive; + this.searchFilter = searchFilter; + this.includeFilter = includeFilter; + this.stack.add(listFiles(root)); + this.current = poll(); + } + + @Override + public boolean hasNext() { + return this.current != null; + } + + @Override + public T next() { + FileEntry entry = this.current; + if (entry == null) { + throw new NoSuchElementException(); + } + this.current = poll(); + return adapt(entry); + } + + private FileEntry poll() { + while (!this.stack.isEmpty()) { + Iterator peek = this.stack.peek(); + if(peek == null){ + continue; + } + while (peek.hasNext()) { + File file = peek.next(); + if (SKIPPED_NAMES.contains(file.getName())) { + continue; + } + FileEntry entry = getFileEntry(file); + if (isListable(entry)) { + this.stack.addFirst(listFiles(file)); + } + if (this.includeFilter == null || this.includeFilter.matches(entry)) { + return entry; + } + } + this.stack.poll(); + } + return null; + } + + private FileEntry getFileEntry(File file) { + URI uri = file.toURI(); + String name = uri.getPath().substring(this.rootUrl.length()); + try { + return new FileEntry(name, file, uri.toURL()); + } + catch (MalformedURLException ex) { + throw new IllegalStateException(ex); + } + } + + private boolean isListable(FileEntry entry) { + return entry.isDirectory() && (this.recursive || entry.getFile().getParentFile().equals(this.root)) + && (this.searchFilter == null || this.searchFilter.matches(entry)) + && (this.includeFilter == null || !this.includeFilter.matches(entry)); + } + + private Iterator listFiles(File file) { + File[] files = file.listFiles(); + if (files == null) { + return Collections.emptyIterator(); + } + Arrays.sort(files, ENTRY_COMPARATOR); + return Arrays.asList(files).iterator(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("remove"); + } + + protected abstract T adapt(FileEntry entry); + + } + + private static class EntryIterator extends AbstractIterator { + + EntryIterator(File root, boolean recursive, EntryFilter searchFilter, EntryFilter includeFilter) { + super(root, recursive, searchFilter, includeFilter); + } + + @Override + protected Entry adapt(FileEntry entry) { + return entry; + } + + } + + private static class ArchiveIterator extends AbstractIterator { + + ArchiveIterator(File root, boolean recursive, EntryFilter searchFilter, EntryFilter includeFilter) { + super(root, recursive, searchFilter, includeFilter); + } + + @Override + protected Archive adapt(FileEntry entry) { + File file = entry.getFile(); + return (file.isDirectory() ? new ExplodedArchive(file) : new SimpleJarFileArchive(entry)); + } + + } + + /** + * {@link Entry} backed by a File. + */ + private static class FileEntry implements Entry { + + private final String name; + + private final File file; + + private final URL url; + + FileEntry(String name, File file, URL url) { + this.name = name; + this.file = file; + this.url = url; + } + + File getFile() { + return this.file; + } + + @Override + public boolean isDirectory() { + return this.file.isDirectory(); + } + + @Override + public String getName() { + return this.name; + } + + URL getUrl() { + return this.url; + } + + } + + /** + * {@link Archive} implementation backed by a simple JAR file that doesn't itself + * contain nested archives. + */ + private static class SimpleJarFileArchive implements Archive { + + private final URL url; + + SimpleJarFileArchive(FileEntry file) { + this.url = file.getUrl(); + } + + @Override + public URL getUrl() throws MalformedURLException { + return this.url; + } + + @Override + public Manifest getManifest() throws IOException { + return null; + } + + @Override + public Iterator getNestedArchives(EntryFilter searchFilter, EntryFilter includeFilter) + throws IOException { + return Collections.emptyIterator(); + } + + @Override + @Deprecated + public Iterator iterator() { + return Collections.emptyIterator(); + } + + @Override + public String toString() { + try { + return getUrl().toString(); + } + catch (Exception ex) { + return "jar archive"; + } + } + + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/archive/JarFileArchive.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/archive/JarFileArchive.java new file mode 100644 index 0000000000000000000000000000000000000000..8698a2ef9e78c4f467f34fd4d3bbd344b3d27fe8 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/archive/JarFileArchive.java @@ -0,0 +1,300 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.archive; + +import com.gitee.starblues.loader.jar.JarFile; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.*; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.UUID; +import java.util.jar.JarEntry; +import java.util.jar.Manifest; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public class JarFileArchive implements Archive { + + private static final String UNPACK_MARKER = "UNPACK:"; + + private static final int BUFFER_SIZE = 32 * 1024; + + private static final FileAttribute[] NO_FILE_ATTRIBUTES = {}; + + private static final EnumSet DIRECTORY_PERMISSIONS = EnumSet.of(PosixFilePermission.OWNER_READ, + PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE); + + private static final EnumSet FILE_PERMISSIONS = EnumSet.of(PosixFilePermission.OWNER_READ, + PosixFilePermission.OWNER_WRITE); + + private final JarFile jarFile; + + private URL url; + + private Path tempUnpackDirectory; + + public JarFileArchive(File file) throws IOException { + this(file, file.toURI().toURL()); + } + + public JarFileArchive(File file, URL url) throws IOException { + this(new JarFile(file)); + this.url = url; + } + + public JarFileArchive(JarFile jarFile) { + this.jarFile = jarFile; + } + + @Override + public URL getUrl() throws MalformedURLException { + if (this.url != null) { + return this.url; + } + return this.jarFile.getUrl(); + } + + @Override + public Manifest getManifest() throws IOException { + return this.jarFile.getManifest(); + } + + @Override + public Iterator getNestedArchives(EntryFilter searchFilter, EntryFilter includeFilter) throws IOException { + return new NestedArchiveIterator(this.jarFile.iterator(), searchFilter, includeFilter); + } + + @Override + public Iterator iterator() { + return new EntryIterator(this.jarFile.iterator(), null, null); + } + + @Override + public void close() throws IOException { + this.jarFile.close(); + } + + protected Archive getNestedArchive(Entry entry) throws IOException { + JarEntry jarEntry = ((JarFileEntry) entry).getJarEntry(); + if (jarEntry.getComment().startsWith(UNPACK_MARKER)) { + return getUnpackedNestedArchive(jarEntry); + } + try { + JarFile jarFile = this.jarFile.getNestedJarFile(jarEntry); + return new JarFileArchive(jarFile); + } catch (Exception ex) { + throw new IllegalStateException("Failed to get nested archive for entry " + entry.getName(), ex); + } + } + + private Archive getUnpackedNestedArchive(JarEntry jarEntry) throws IOException { + String name = jarEntry.getName(); + if (name.lastIndexOf('/') != -1) { + name = name.substring(name.lastIndexOf('/') + 1); + } + Path path = getTempUnpackDirectory().resolve(name); + if (!Files.exists(path) || Files.size(path) != jarEntry.getSize()) { + unpack(jarEntry, path); + } + return new JarFileArchive(path.toFile(), path.toUri().toURL()); + } + + private Path getTempUnpackDirectory() { + if (this.tempUnpackDirectory == null) { + Path tempDirectory = Paths.get(System.getProperty("java.io.tmpdir")); + this.tempUnpackDirectory = createUnpackDirectory(tempDirectory); + } + return this.tempUnpackDirectory; + } + + private Path createUnpackDirectory(Path parent) { + int attempts = 0; + while (attempts++ < 1000) { + String fileName = Paths.get(this.jarFile.getName()).getFileName().toString(); + Path unpackDirectory = parent.resolve(fileName + "-spring-boot-libs-" + UUID.randomUUID()); + try { + createDirectory(unpackDirectory); + return unpackDirectory; + } catch (IOException ex) { + // ignore + } + } + throw new IllegalStateException("Failed to create unpack directory in directory '" + parent + "'"); + } + + private void unpack(JarEntry entry, Path path) throws IOException { + createFile(path); + path.toFile().deleteOnExit(); + try (InputStream inputStream = this.jarFile.getInputStream(entry); + OutputStream outputStream = Files.newOutputStream(path, StandardOpenOption.WRITE, + StandardOpenOption.TRUNCATE_EXISTING)) { + byte[] buffer = new byte[BUFFER_SIZE]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + outputStream.flush(); + } + } + + private void createDirectory(Path path) throws IOException { + Files.createDirectory(path, getFileAttributes(path.getFileSystem(), DIRECTORY_PERMISSIONS)); + } + + private void createFile(Path path) throws IOException { + Files.createFile(path, getFileAttributes(path.getFileSystem(), FILE_PERMISSIONS)); + } + + private FileAttribute[] getFileAttributes(FileSystem fileSystem, EnumSet ownerReadWrite) { + if (!fileSystem.supportedFileAttributeViews().contains("posix")) { + return NO_FILE_ATTRIBUTES; + } + return new FileAttribute[] { PosixFilePermissions.asFileAttribute(ownerReadWrite) }; + } + + @Override + public String toString() { + try { + return getUrl().toString(); + } catch (Exception ex) { + return "jar archive"; + } + } + + /** + * Abstract base class for iterator implementations. + */ + private abstract static class AbstractIterator implements Iterator { + + private final Iterator iterator; + + private final EntryFilter searchFilter; + + private final EntryFilter includeFilter; + + private Entry current; + + AbstractIterator(Iterator iterator, EntryFilter searchFilter, EntryFilter includeFilter) { + this.iterator = iterator; + this.searchFilter = searchFilter; + this.includeFilter = includeFilter; + this.current = poll(); + } + + @Override + public boolean hasNext() { + return this.current != null; + } + + @Override + public T next() { + T result = adapt(this.current); + this.current = poll(); + return result; + } + + private Entry poll() { + while (this.iterator.hasNext()) { + JarFileEntry candidate = new JarFileEntry(this.iterator.next()); + if ((this.searchFilter == null || this.searchFilter.matches(candidate)) + && (this.includeFilter == null || this.includeFilter.matches(candidate))) { + return candidate; + } + } + return null; + } + + protected abstract T adapt(Entry entry); + + } + + /** + * {@link Archive.Entry} iterator implementation backed by {@link JarEntry}. + */ + private static class EntryIterator extends AbstractIterator { + + EntryIterator(Iterator iterator, EntryFilter searchFilter, EntryFilter includeFilter) { + super(iterator, searchFilter, includeFilter); + } + + @Override + protected Entry adapt(Entry entry) { + return entry; + } + + } + + /** + * Nested {@link Archive} iterator implementation backed by {@link JarEntry}. + */ + private class NestedArchiveIterator extends AbstractIterator { + + NestedArchiveIterator(Iterator iterator, EntryFilter searchFilter, EntryFilter includeFilter) { + super(iterator, searchFilter, includeFilter); + } + + @Override + protected Archive adapt(Entry entry) { + try { + return getNestedArchive(entry); + } catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + + } + + /** + * {@link Archive.Entry} implementation backed by a {@link JarEntry}. + */ + private static class JarFileEntry implements Entry { + + private final JarEntry jarEntry; + + JarFileEntry(JarEntry jarEntry) { + this.jarEntry = jarEntry; + } + + JarEntry getJarEntry() { + return this.jarEntry; + } + + @Override + public boolean isDirectory() { + return this.jarEntry.isDirectory(); + } + + @Override + public String getName() { + return this.jarEntry.getName(); + } + + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/GenericClassLoader.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/GenericClassLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..fe1a01fd44b9f04184c142138e930f800bd1fa58 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/GenericClassLoader.java @@ -0,0 +1,299 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.classloader; + +import com.gitee.starblues.loader.classloader.resource.Resource; +import com.gitee.starblues.loader.classloader.resource.loader.ResourceLoader; +import com.gitee.starblues.loader.classloader.resource.loader.ResourceLoaderFactory; +import com.gitee.starblues.loader.utils.Assert; +import com.gitee.starblues.loader.utils.IOUtils; +import com.gitee.starblues.loader.utils.ResourceUtils; + +import java.io.*; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 基本的 ClassLoader + * @author starBlues + * @version 3.0.0 + */ +public class GenericClassLoader extends URLClassLoader { + + private final String name; + private final ClassLoader parent; + + protected final ResourceLoaderFactory resourceLoaderFactory; + + private final Map> pluginClassCache = new ConcurrentHashMap<>(); + + public GenericClassLoader(String name, ResourceLoaderFactory resourceLoaderFactory) { + this(name, null, resourceLoaderFactory); + } + + public GenericClassLoader(String name, ClassLoader parent, ResourceLoaderFactory resourceLoaderFactory) { + super(new URL[]{}, null); + this.name = Assert.isNotEmpty(name, "name 不能为空"); + this.resourceLoaderFactory = Assert.isNotNull(resourceLoaderFactory, "resourceLoaderFactory 不能为空"); + this.parent = parent; + + } + + public String getName() { + return name; + } + + + public void addResource(String path) throws Exception { + resourceLoaderFactory.addResource(path); + } + + public void addResource(File file) throws Exception { + resourceLoaderFactory.addResource(file); + } + + public void addResource(Path path) throws Exception { + resourceLoaderFactory.addResource(path); + } + + public void addResource(URL url) throws Exception { + resourceLoaderFactory.addResource(url); + } + + public void addResource(ResourceLoader resourceLoader) throws Exception{ + resourceLoaderFactory.addResource(resourceLoader); + } + + public ClassLoader getParentClassLoader(){ + return parent; + } + + @Override + public Class loadClass(String className) throws ClassNotFoundException { + synchronized (getClassLoadingLock(className)) { + return findClass(className); + } + } + + @Override + protected Class findClass(String className) throws ClassNotFoundException { + Class loadedClass = findClassFromParent(className); + if (loadedClass != null) { + return loadedClass; + } + loadedClass = findLoadedClass(className); + if (loadedClass != null) { + return loadedClass; + } + loadedClass = findClassFromLocal(className); + if (loadedClass != null) { + return loadedClass; + } + throw new ClassNotFoundException("ClassLoader[" + name +"]:" + className); + } + + protected Class findClassFromParent(String className) throws ClassNotFoundException{ + try { + if(parent != null){ + return parent.loadClass(className); + } + return null; + } catch (Exception e){ + return null; + } + } + + protected Class findClassFromLocal(String name) { + Class aClass; + String formatClassName = formatClassName(name); + aClass = pluginClassCache.get(formatClassName); + if (aClass != null) { + return aClass; + } + Resource resource = resourceLoaderFactory.findResource(formatClassName); + byte[] bytes = null; + if(resource != null){ + bytes = resource.getBytes(); + } + if(bytes == null || bytes.length == 0){ + bytes = getClassByte(formatClassName); + } + if(bytes == null || bytes.length == 0){ + return null; + } + aClass = defineClass(name, bytes, 0, bytes.length ); + if(aClass == null) { + return null; + } + if (aClass.getPackage() == null) { + int lastDotIndex = name.lastIndexOf( '.' ); + String packageName = (lastDotIndex >= 0) ? name.substring( 0, lastDotIndex) : ""; + definePackage(packageName, null, null, null, + null, null, null, null ); + } + pluginClassCache.put(name, aClass); + return aClass; + } + + private byte[] getClassByte(String formatClassName){ + InputStream inputStream = resourceLoaderFactory.getInputStream(formatClassName); + if(inputStream == null){ + return null; + } + try { + return IOUtils.read(inputStream); + } catch (Exception e){ + e.printStackTrace(); + return null; + } finally { + IOUtils.closeQuietly(inputStream); + } + } + + @Override + public URL[] getURLs() { + List urlList = resourceLoaderFactory.getUrls(); + URL[] urls = new URL[urlList.size()]; + for (int i = 0; i < urlList.size(); i++) { + urls[i] = urlList.get(i); + } + return urls; + } + + @Override + public InputStream getResourceAsStream(String name) { + name = formatResourceName(name); + InputStream inputStream = findInputStreamFromParent(name); + if(inputStream != null){ + return inputStream; + } + return findInputStreamFromLocal(name); + } + + protected InputStream findInputStreamFromParent(String name){ + if(parent != null){ + return parent.getResourceAsStream(name); + } + return null; + } + + protected InputStream findInputStreamFromLocal(String name){ + return resourceLoaderFactory.getInputStream(name); + } + + @Override + public URL getResource(String name) { + name = formatResourceName(name); + URL url = findResourceFromParent(name); + if(url != null){ + return url; + } + return findResourceFromLocal(name); + } + + protected URL findResourceFromParent(String name){ + if(parent != null){ + return parent.getResource(name); + } + return null; + } + + protected URL findResourceFromLocal(String name) { + Resource resource = resourceLoaderFactory.findResource(name); + if (resource == null) { + return null; + } + return resource.getUrl(); + } + + @Override + public Enumeration getResources(String name) throws IOException { + name = formatResourceName(name); + Enumeration parentResources = findResourcesFromParent(name); + Enumeration localResources = findResourcesFromLocal(name); + return new Enumeration() { + + private int index = 0; + + @Override + public boolean hasMoreElements() { + if(parentResources != null && parentResources.hasMoreElements()){ + return true; + } + index = 1; + return localResources.hasMoreElements(); + } + + @Override + public URL nextElement() { + if(index == 0){ + return parentResources.nextElement(); + } else { + return localResources.nextElement(); + } + } + }; + } + + protected Enumeration findResourcesFromParent(String name) throws IOException{ + if(parent != null){ + return parent.getResources(name); + } + return null; + } + + protected Enumeration findResourcesFromLocal(String name) throws IOException{ + Enumeration enumeration = resourceLoaderFactory.findResources(name); + return new Enumeration() { + @Override + public boolean hasMoreElements() { + return enumeration.hasMoreElements(); + } + + @Override + public URL nextElement() { + return enumeration.nextElement().getUrl(); + } + }; + } + + @Override + public void close() throws IOException { + synchronized (pluginClassCache){ + pluginClassCache.clear(); + IOUtils.closeQuietly(resourceLoaderFactory); + } + } + + private String formatResourceName(String name) { + return ResourceUtils.formatStandardName(name); + } + + private String formatClassName(String className) { + className = className.replace( '/', '~' ); + className = className.replace( '.', '/' ) + ".class"; + className = className.replace( '~', '/' ); + return className; + } + + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/filter/ExcludeResource.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/filter/ExcludeResource.java new file mode 100644 index 0000000000000000000000000000000000000000..237c4de9497db5dd25d7e785ff6e466f9afbbd77 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/filter/ExcludeResource.java @@ -0,0 +1,36 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.classloader.filter; + +import java.util.jar.JarEntry; + +/** + * 排除资源 + * @author starBlues + * @version 3.0.0 + */ +@FunctionalInterface +public interface ExcludeResource { + + /** + * 过滤排除 + * @param jarEntry jarEntry + * @return boolean + */ + boolean exclude(JarEntry jarEntry); + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/filter/IncludeResource.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/filter/IncludeResource.java new file mode 100644 index 0000000000000000000000000000000000000000..e0091231553f84937e36ec58cd12b12ebd925104 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/filter/IncludeResource.java @@ -0,0 +1,36 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.classloader.filter; + +import java.util.jar.JarEntry; + +/** + * 包含资源 + * @author starBlues + * @version 3.0.0 + */ +@FunctionalInterface +public interface IncludeResource { + + /** + * 过滤排除 + * @param jarEntry jarEntry + * @return boolean + */ + boolean include(JarEntry jarEntry); + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/Resource.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/Resource.java new file mode 100644 index 0000000000000000000000000000000000000000..59ea855bebf24b38c1177af0c0545541a754ed2a --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/Resource.java @@ -0,0 +1,61 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.classloader.resource; + +import java.net.URL; + +/** + * 资源信息 + * @author starBlues + * @version 3.0.0 + */ +public interface Resource extends AutoCloseable{ + + String PACKAGE_SPLIT = "/"; + + /** + * 得到资源名称 + * @return 全局唯一的资源名称 + */ + String getName(); + + /** + * 得到根URL地址 + * @return URL + */ + URL getBaseUrl(); + + /** + * 得到完整URL地址 + * @return URL + */ + URL getUrl(); + + /** + * 得到资源字节数组 + * @return byte[] + */ + byte[] getBytes(); + + /** + * 设置字节数 + * @param byteGetter byteGetter + */ + void setBytes(ResourceByteGetter byteGetter) throws Exception; + + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/ResourceByteGetter.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/ResourceByteGetter.java new file mode 100644 index 0000000000000000000000000000000000000000..e8b78f806f12c94cb6a46a055b49062414f288af --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/ResourceByteGetter.java @@ -0,0 +1,34 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.classloader.resource; + +/** + * 资源 byte 得到者 + * + * @author starBlues + * @version 3.0.0 + */ +public interface ResourceByteGetter { + + /** + * 得到 byte + * @return byte[] + * @throws Exception Exception + */ + byte[] get() throws Exception; + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/AbstractResourceLoader.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/AbstractResourceLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..67d1a918acac4096644cee9aa5fc2d41dc47e538 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/AbstractResourceLoader.java @@ -0,0 +1,101 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.classloader.resource.loader; + + +import com.gitee.starblues.loader.classloader.resource.storage.ResourceStorage; +import com.gitee.starblues.loader.utils.IOUtils; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.net.URL; + +/** + * 抽象的资源加载者 + * @author starBlues + * @version 3.0.0 + */ +public abstract class AbstractResourceLoader implements ResourceLoader{ + + private static final String CLASS_FILE_EXTENSION = ".class"; + + private boolean loaded = false; + + protected final URL baseUrl; + + protected AbstractResourceLoader(URL baseUrl) { + this.baseUrl = baseUrl; + } + + @Override + public URL getBaseUrl() { + return baseUrl; + } + + /** + * 初始化 resource + * @throws Exception 初始异常 + */ + @Override + public final synchronized void load(ResourceStorage resourceStorage) throws Exception{ + if(loaded){ + throw new Exception(this.getClass().getName()+": 已经初始化了, 不能再初始化!"); + } + try { + // 添加root 路径 + resourceStorage.add("/", baseUrl); + loadOfChild(resourceStorage); + } finally { + loaded = true; + } + } + + /** + * 子类初始化实现 + * @throws Exception 初始异常 + */ + protected abstract void loadOfChild(ResourceStorage resourceStorage) throws Exception; + + protected byte[] getClassBytes(String path, InputStream inputStream, boolean isClose) throws Exception{ + if(!isClass(path)){ + return null; + } + final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + try { + IOUtils.copy(inputStream, byteArrayOutputStream); + return byteArrayOutputStream.toByteArray(); + } finally { + if(isClose){ + IOUtils.closeQuietly(inputStream); + } + IOUtils.closeQuietly(byteArrayOutputStream); + } + } + + @Override + public void close() throws Exception { + + } + + private static boolean isClass(String path){ + if(path == null || "".equals(path)){ + return false; + } + return path.toLowerCase().endsWith(CLASS_FILE_EXTENSION); + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/ClassPathLoader.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/ClassPathLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..24048c04b7dc7fa5836dae840fb26b889e5f341d --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/ClassPathLoader.java @@ -0,0 +1,99 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.classloader.resource.loader; +import com.gitee.starblues.loader.classloader.resource.Resource; +import com.gitee.starblues.loader.classloader.resource.storage.ResourceStorage; + +import java.io.File; +import java.io.FileInputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Path; +import java.util.Objects; + +/** + * classpath 资源加载者 + * @author starBlues + * @version 3.0.0 + */ +public class ClassPathLoader extends AbstractResourceLoader { + + private final URL url; + + public ClassPathLoader(URL url) { + super(url); + this.url = Objects.requireNonNull(url, "url 不能为空"); + } + + public ClassPathLoader(File file) throws MalformedURLException { + this(file.toPath()); + } + + public ClassPathLoader(Path path) throws MalformedURLException { + super(path.toUri().toURL()); + this.url = super.baseUrl; + } + + @Override + protected void loadOfChild(ResourceStorage resourceStorage) throws Exception { + File file = new File(url.toURI()); + load(resourceStorage, file, null); + } + + private void load(ResourceStorage resourceStorage, File file, String currentPackageName) throws Exception { + if(currentPackageName == null){ + // 根目录 + currentPackageName = ""; + } else { + if("".equals(currentPackageName)){ + currentPackageName = file.getName(); + } else { + currentPackageName = currentPackageName + Resource.PACKAGE_SPLIT + file.getName(); + } + loadResource(resourceStorage, file, currentPackageName); + } + if(file.isDirectory()){ + File[] listFiles = file.listFiles(); + if(listFiles == null || listFiles.length == 0){ + return; + } + for (File subFile : listFiles) { + load(resourceStorage, subFile, currentPackageName); + } + } + } + + private void loadResource(ResourceStorage resourceStorage, File file, String packageName) throws Exception{ + if(file.isDirectory()){ + addResource(resourceStorage, file, packageName + Resource.PACKAGE_SPLIT); + } else { + addResource(resourceStorage, file, packageName); + } + } + + private void addResource(ResourceStorage resourceStorage, File file, String packageName) throws Exception { + resourceStorage.add(packageName, new URL(url.toString() + packageName), ()->{ + if(file.exists() && file.isFile()){ + return getClassBytes(file.getPath(), new FileInputStream(file), true); + } else { + return null; + } + }); + } + + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/DefaultResource.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/DefaultResource.java new file mode 100644 index 0000000000000000000000000000000000000000..d8f53f9f714309ce6ce93dee5de7628a32a71742 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/DefaultResource.java @@ -0,0 +1,70 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.classloader.resource.loader; + +import com.gitee.starblues.loader.classloader.resource.Resource; +import com.gitee.starblues.loader.classloader.resource.ResourceByteGetter; + +import java.net.URL; + +/** + * 默认的资源信息 + * @author starBlues + * @version 3.0.0 + */ +public class DefaultResource implements Resource { + + private final String name; + private final URL baseUrl; + private final URL url; + + public DefaultResource(String name, URL baseUrl, URL url) { + this.name = name; + this.baseUrl = baseUrl; + this.url = url; + } + + @Override + public String getName() { + return name; + } + + @Override + public URL getBaseUrl() { + return baseUrl; + } + + @Override + public URL getUrl() { + return url; + } + + @Override + public byte[] getBytes() { + return null; + } + + @Override + public void setBytes(ResourceByteGetter byteGetter) throws Exception{ + // 忽略 + } + + @Override + public void close() throws Exception { + + } +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/DefaultResourceLoaderFactory.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/DefaultResourceLoaderFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..e853705698cef3b9ccbe7f63545153e8223c9e40 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/DefaultResourceLoaderFactory.java @@ -0,0 +1,206 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.classloader.resource.loader; + +import com.gitee.starblues.loader.classloader.resource.Resource; +import com.gitee.starblues.loader.classloader.resource.storage.ResourceStorage; +import com.gitee.starblues.loader.classloader.resource.storage.SameRootResourceStorage; +import com.gitee.starblues.loader.launcher.ResourceLoaderFactoryGetter; +import com.gitee.starblues.loader.utils.IOUtils; +import com.gitee.starblues.loader.utils.ResourceUtils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * 默认的资源加载工厂 + * + * @author starBlues + * @version 3.0.0 + */ +public class DefaultResourceLoaderFactory implements ResourceLoaderFactory{ + + private final Map resourceLoaderMap = new ConcurrentHashMap<>(); + + private final String classLoaderName; + + public DefaultResourceLoaderFactory(String classLoaderName) { + this.classLoaderName = classLoaderName; + } + + + @Override + public void addResource(String path) throws Exception{ + if(path == null || "".equals(path)){ + return; + } + addResource(Paths.get(path)); + } + + @Override + public void addResource(File file) throws Exception{ + if(file == null){ + return; + } + addResource(file.toPath()); + } + + @Override + public void addResource(Path path) throws Exception{ + if(path == null){ + return; + } + if(!Files.exists(path)){ + return; + } + addResource(path.toUri().toURL()); + } + + @Override + public void addResource(URL url) throws Exception{ + AbstractResourceLoader resourceLoader = null; + if(ResourceUtils.isJarFileUrl(url)) { + if(ResourceUtils.isJarProtocolUrl(url)){ + resourceLoader = new JarResourceLoader(url); + } else { + resourceLoader = new JarResourceLoader(Paths.get(url.toURI()).toFile()); + } + } else if(ResourceUtils.isZipFileUrl(url)){ + resourceLoader = new JarResourceLoader(Paths.get(url.toURI()).toFile()); + } else if(ResourceUtils.isFileUrl(url)){ + resourceLoader = new ClassPathLoader(url); + } + if(resourceLoader != null){ + addResource(resourceLoader); + } + } + + @Override + public void addResource(ResourceLoader resourceLoader) throws Exception { + if(resourceLoader == null){ + return; + } + if (resourceLoaderMap.containsKey(resourceLoader.getBaseUrl())) { + return; + } + SameRootResourceStorage resourceStorage = ResourceLoaderFactoryGetter.getResourceStorage( + classLoaderName, + resourceLoader.getBaseUrl()); + resourceLoader.load(resourceStorage); + if(!resourceStorage.isEmpty()){ + resourceLoaderMap.put(resourceLoader.getBaseUrl(), resourceStorage); + } + } + + @Override + public Resource findResource(String name) { + for (Map.Entry entry : resourceLoaderMap.entrySet()) { + ResourceStorage resourceStorage = entry.getValue(); + Resource resource = resourceStorage.get(name); + if(resource != null){ + return resource; + } + } + return null; + } + + @Override + public Enumeration findResources(String name) { + return new Enumeration() { + private final List list = new ArrayList<>(resourceLoaderMap.values()); + private int index = 0; + private Resource resource = null; + + @Override + public boolean hasMoreElements() { + return next(); + } + + @Override + public Resource nextElement() { + if (!next()) { + throw new NoSuchElementException(); + } + Resource r = resource; + resource = null; + return r; + } + + private boolean next() { + if (resource != null) { + return true; + } else { + SameRootResourceStorage resourceStorage; + while (index < list.size()){ + resourceStorage = list.get(index++); + resource = getResource(resourceStorage); + if(resource != null){ + return true; + } + } + return false; + } + } + + private Resource getResource(SameRootResourceStorage resourceStorage){ + resource = resourceStorage.get(name); + if(resource != null){ + return resource; + } + return null; + } + }; + } + + @Override + public InputStream getInputStream(String name) { + for (Map.Entry entry : resourceLoaderMap.entrySet()) { + ResourceStorage resourceStorage = entry.getValue(); + InputStream inputStream = resourceStorage.getInputStream(name); + if(inputStream != null){ + return inputStream; + } + } + return null; + } + + @Override + public List getUrls() { + return new ArrayList<>(resourceLoaderMap.keySet()); + } + + @Override + public void close() throws Exception { + for (ResourceStorage resourceStorage : resourceLoaderMap.values()) { + IOUtils.closeQuietly(resourceStorage); + } + resourceLoaderMap.clear(); + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/JarResourceLoader.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/JarResourceLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..5984b42d35357cb3cdc99a2d3773e5a0a3d64ce8 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/JarResourceLoader.java @@ -0,0 +1,97 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.classloader.resource.loader; + +import com.gitee.starblues.loader.classloader.filter.ExcludeResource; +import com.gitee.starblues.loader.classloader.filter.IncludeResource; +import com.gitee.starblues.loader.classloader.resource.storage.ResourceStorage; + +import java.io.File; +import java.net.URL; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; + +/** + * jar 资源加载者 + * @author starBlues + * @version 3.0.0 + */ +public class JarResourceLoader extends AbstractResourceLoader { + + private final JarInputStream jarInputStream; + + private ExcludeResource excludeResource = (jarEntry)->false; + private IncludeResource includeResource = (jarEntry)->true; + + public JarResourceLoader(File file) throws Exception{ + super(new URL("jar:" + file.toURI().toURL() + "!/")); + URL url = file.toURI().toURL(); + this.jarInputStream = new JarInputStream(url.openStream()); + } + + public JarResourceLoader(URL url) throws Exception{ + super(url); + this.jarInputStream = new JarInputStream(url.openStream()); + } + + public JarResourceLoader(URL url, JarInputStream jarInputStream) throws Exception{ + super(url); + this.jarInputStream = jarInputStream; + } + + public void setExcludeResource(ExcludeResource excludeResource) { + if(excludeResource == null){ + return; + } + this.excludeResource = excludeResource; + } + + public void setIncludeResource(IncludeResource includeResource) { + if(includeResource == null){ + return; + } + this.includeResource = includeResource; + } + + @Override + protected void loadOfChild(ResourceStorage resourceStorage) throws Exception { + // 解析 + try { + JarEntry jarEntry = null; + while ((jarEntry = jarInputStream.getNextJarEntry()) != null) { + if(excludeResource.exclude(jarEntry)){ + continue; + } + if(includeResource.include(jarEntry)){ + String name = resolveName(jarEntry.getName()); + URL url = new URL(baseUrl.toString() + name); + resourceStorage.add(name, url, ()->{ + return getClassBytes(name, jarInputStream, false); + }); + jarInputStream.closeEntry(); + } + } + } finally { + jarInputStream.close(); + } + } + + protected String resolveName(String name){ + return name; + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/ResourceLoader.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/ResourceLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..819aff85567f538ad4df6af0404876f8b078c78c --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/ResourceLoader.java @@ -0,0 +1,35 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.classloader.resource.loader; + +import com.gitee.starblues.loader.classloader.resource.storage.ResourceStorage; + +import java.net.URL; + +/** + * 资源加载者 + * + * @author starBlues + * @version 3.0.0 + */ +public interface ResourceLoader extends AutoCloseable{ + + URL getBaseUrl(); + + void load(ResourceStorage resourceStorage) throws Exception; + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/ResourceLoaderFactory.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/ResourceLoaderFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..e626f42803ab033892308def366dd0bee199dad9 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/ResourceLoaderFactory.java @@ -0,0 +1,100 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.classloader.resource.loader; + +import com.gitee.starblues.loader.classloader.resource.Resource; +import com.gitee.starblues.loader.classloader.resource.storage.ResourceStorage; + +import java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Path; +import java.util.Enumeration; +import java.util.List; + +/** + * 资源加载工厂 + * + * @author starBlues + * @version 3.0.0 + */ +public interface ResourceLoaderFactory extends AutoCloseable{ + + /** + * 根据路径字符串添加资源 + * @param path 路径字符串 + * @throws Exception 添加资源异常 + */ + void addResource(String path) throws Exception; + + /** + * 根据文件添加资源 + * @param file 文件 + * @throws Exception 添加资源异常 + */ + void addResource(File file) throws Exception; + + /** + * 根据路径添加资源 + * @param path 路径 + * @throws Exception 添加资源异常 + */ + void addResource(Path path) throws Exception; + + /** + * 根据 URL 添加资源 + * @param url URL + * @throws Exception 添加资源异常 + */ + void addResource(URL url) throws Exception; + + /** + * 根据资源加载器添加资源 + * @param resourceLoader 资源加载者 + * @throws Exception 添加资源异常 + */ + void addResource(ResourceLoader resourceLoader) throws Exception; + + /** + * 根据资源名称获取第一个资源 + * @param name 资源名称 + * @return Resource + */ + Resource findResource(String name); + + /** + * 根据资源名称获取资源集合 + * @param name 资源名称 + * @return Resource + */ + Enumeration findResources(String name); + + /** + * 根据资源名称获取第一个资源的 InputStream + * @param name 资源名称 + * @return Resource + */ + InputStream getInputStream(String name); + + /** + * 获取所有URL + * @return URL + */ + List getUrls(); + + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/CacheResourceStorage.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/CacheResourceStorage.java new file mode 100644 index 0000000000000000000000000000000000000000..9f3872f347628271890caaab792185eb79125041 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/CacheResourceStorage.java @@ -0,0 +1,76 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.classloader.resource.storage; + +import com.gitee.starblues.loader.classloader.resource.loader.DefaultResource; +import com.gitee.starblues.loader.classloader.resource.Resource; +import com.gitee.starblues.loader.classloader.resource.ResourceByteGetter; + +import java.net.URL; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 可缓存的资源存储者 + * + * @author starBlues + * @version 3.0.0 + */ +public class CacheResourceStorage extends DefaultResourceStorage{ + + protected final Map resourceStorage = new ConcurrentHashMap<>(); + + public CacheResourceStorage(URL baseUrl) { + super(baseUrl); + } + + @Override + public void add(String name, URL url, ResourceByteGetter byteGetter) throws Exception{ + name = formatResourceName(name); + if(resourceStorage.containsKey(name)){ + return; + } + CacheResource cacheResource = new CacheResource(name, baseUrl, url); + cacheResource.setBytes(byteGetter); + addResource(name, cacheResource); + } + + private static class CacheResource extends DefaultResource { + + private byte[] bytes; + + public CacheResource(String name, URL baseUrl, URL url) { + super(name, baseUrl, url); + } + + @Override + public void setBytes(ResourceByteGetter byteGetter) throws Exception{ + if(byteGetter == null){ + return; + } + // 忽略 + bytes = byteGetter.get(); + } + + @Override + public byte[] getBytes() { + return bytes; + } + } + + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/DefaultResourceStorage.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/DefaultResourceStorage.java new file mode 100644 index 0000000000000000000000000000000000000000..84a7733f89ff4f512e7bd856ae00b3d20bad3d89 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/DefaultResourceStorage.java @@ -0,0 +1,134 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.classloader.resource.storage; + +import com.gitee.starblues.loader.classloader.resource.loader.DefaultResource; +import com.gitee.starblues.loader.classloader.resource.Resource; +import com.gitee.starblues.loader.classloader.resource.ResourceByteGetter; +import com.gitee.starblues.loader.utils.Assert; +import com.gitee.starblues.loader.utils.IOUtils; +import com.gitee.starblues.loader.utils.ObjectUtils; +import com.gitee.starblues.loader.utils.ResourceUtils; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 默认的资源存储者 + * + * @author starBlues + * @version 3.0.0 + */ +public class DefaultResourceStorage extends SameRootResourceStorage{ + + protected final Map resourceStorage = new ConcurrentHashMap<>(); + + public DefaultResourceStorage(URL baseUrl) { + super(baseUrl); + } + + @Override + public void add(String name, URL url, ResourceByteGetter byteGetter) throws Exception{ + Assert.isNotEmpty(name, "name 不能为空"); + Assert.isNotNull(url, "url 不能为空"); + name = formatResourceName(name); + if(resourceStorage.containsKey(name)){ + return; + } + DefaultResource defaultResource = new DefaultResource(name, baseUrl, url); + resourceStorage.put(name, defaultResource); + } + + @Override + public void add(String name, URL url) throws Exception{ + this.add(name, url, null); + } + + @Override + public boolean exist(String name) { + if(ObjectUtils.isEmpty(name)){ + return false; + } + name = formatResourceName(name); + return resourceStorage.containsKey(name); + } + + protected void addResource(String name, Resource resource){ + Assert.isNotEmpty(name, "name 不能为空"); + Assert.isNotNull(resource, "resource 不能为空"); + resourceStorage.put(name, resource); + } + + @Override + public Resource get(String name) { + if(ObjectUtils.isEmpty(name)){ + return null; + } + name = formatResourceName(name); + return resourceStorage.get(name); + } + + @Override + public InputStream getInputStream(String name) { + if(ObjectUtils.isEmpty(name)){ + return null; + } + name = formatResourceName(name); + Resource resourceInfo = resourceStorage.get(name); + if (resourceInfo != null) { + try (InputStream inputStream = resourceInfo.getUrl().openStream(); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()){ + IOUtils.copy(inputStream, byteArrayOutputStream); + return new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); + } catch (Exception e){ + e.printStackTrace(); + return null; + } + } else { + return null; + } + } + + @Override + public List getAll() { + return new ArrayList<>(resourceStorage.values()); + } + + @Override + public boolean isEmpty() { + return resourceStorage.isEmpty(); + } + + @Override + public void close() throws Exception { + for (Resource resource : resourceStorage.values()) { + IOUtils.closeQuietly(resource); + } + resourceStorage.clear(); + } + + protected final String formatResourceName(String name) { + return ResourceUtils.formatStandardName(name); + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/ResourceStorage.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/ResourceStorage.java new file mode 100644 index 0000000000000000000000000000000000000000..0720a4ea47fd4798a7b0344545cea78ed3430fd1 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/ResourceStorage.java @@ -0,0 +1,85 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.classloader.resource.storage; + +import com.gitee.starblues.loader.classloader.resource.Resource; +import com.gitee.starblues.loader.classloader.resource.ResourceByteGetter; + +import java.io.InputStream; +import java.net.URL; +import java.util.List; + +/** + * 资源存储者 + * + * @author starBlues + * @version 3.0.0 + */ +public interface ResourceStorage extends AutoCloseable{ + + /** + * 添加资源 + * @param name 资源名称 + * @param url 资源url + * @param byteGetter 资源字节获取者 + * @throws Exception 添加资源异常 + */ + void add(String name, URL url, ResourceByteGetter byteGetter) throws Exception; + + /** + * 添加资源 + * @param name 资源名称 + * @param url 资源url + * @throws Exception 添加资源异常 + */ + void add(String name, URL url) throws Exception; + + /** + * 存在资源 + * @param name 资源名称 + * @return 存在 true, 不存在 false + */ + boolean exist(String name); + + /** + * 获取资源 + * @param name 资源名称 + * @return Resource + */ + Resource get(String name); + + /** + * 获取资源的 InputStream + * @param name 资源名称 + * @return InputStream + */ + InputStream getInputStream(String name); + + /** + * 得到全部资源 + * @return 全部资源列表 + */ + List getAll(); + + /** + * 是否为空 + * @return boolean + */ + boolean isEmpty(); + + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/SameRootResourceStorage.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/SameRootResourceStorage.java new file mode 100644 index 0000000000000000000000000000000000000000..b53b93504e51fa4a795fd9e1d78610aec7629e34 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/SameRootResourceStorage.java @@ -0,0 +1,44 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.classloader.resource.storage; + +import java.net.URL; + +/** + * 抽象的资源存储 + * + * @author starBlues + * @version 3.0.0 + */ +public abstract class SameRootResourceStorage implements ResourceStorage { + + protected final URL baseUrl; + + public SameRootResourceStorage(URL baseUrl) { + this.baseUrl = baseUrl; + } + + /** + * 获取 base url + * @return URL + */ + public URL getBaseUrl() { + return baseUrl; + } + + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/ShareResourceStorage.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/ShareResourceStorage.java new file mode 100644 index 0000000000000000000000000000000000000000..6cd1cc9ee9fb8a56074a2352f0ac38ac78211ac3 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/ShareResourceStorage.java @@ -0,0 +1,154 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.classloader.resource.storage; + +import com.gitee.starblues.loader.classloader.resource.loader.DefaultResource; +import com.gitee.starblues.loader.classloader.resource.ResourceByteGetter; +import com.gitee.starblues.loader.utils.Assert; + +import java.net.URL; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 共享资源存储者 + * + * @author starBlues + * @version 3.0.0 + */ +public class ShareResourceStorage extends DefaultResourceStorage{ + + private final String key; + + public ShareResourceStorage(String key, URL baseUrl) { + super(baseUrl); + this.key = key; + } + + @Override + public void add(String name, URL url, ResourceByteGetter byteGetter) throws Exception{ + name = formatResourceName(name); + if(resourceStorage.containsKey(name)){ + return; + } + ShareResource shareResource = new ShareResource(key, name, baseUrl, url); + shareResource.setBytes(byteGetter); + super.addResource(name, shareResource); + } + + private static class ShareResource extends DefaultResource { + + private final static Map BYTE_STORE_MAP = new ConcurrentHashMap<>(); + private final String key; + + public ShareResource(String key, String name, URL baseUrl, URL url) { + super(name, baseUrl, url); + this.key = key; + } + + @Override + public void setBytes(ResourceByteGetter byteGetter) throws Exception{ + if(byteGetter == null){ + return; + } + byte[] bytes = byteGetter.get(); + String name = getName(); + ByteStore byteStore = BYTE_STORE_MAP.get(name); + if(byteStore == null){ + byteStore = new ByteStore(name); + byteStore.addByte(key, bytes); + BYTE_STORE_MAP.put(getName(), byteStore); + } else { + byteStore.addByte(key, bytes); + } + } + + @Override + public byte[] getBytes() { + String name = getName(); + ByteStore byteStore = BYTE_STORE_MAP.get(name); + if(byteStore == null){ + return null; + } + return byteStore.getByte(key); + } + + @Override + public void close() throws Exception { + String name = getName(); + ByteStore byteStore = BYTE_STORE_MAP.get(name); + if(byteStore == null){ + return; + } + if(byteStore.remove(key)){ + BYTE_STORE_MAP.remove(name); + } + } + } + + private static class ByteStore{ + + private final String commonByteKey; + + private final Map bytesMap = new HashMap<>(); + + private ByteStore(String resourceName) { + this.commonByteKey = resourceName + "_ByteStoreCommon"; + } + + public synchronized void addByte(String key, byte[] bytes){ + Assert.isNotEmpty(key, "classLoaderName 不能为空"); + if(bytes == null || bytes.length == 0){ + return; + } + byte[] bytesOfMap = bytesMap.get(commonByteKey); + if(bytesOfMap != null && bytesOfMap.length > 0){ + if(Arrays.equals(bytesOfMap, bytes)){ + bytesMap.put(key, new byte[]{}); + } else { + bytesMap.put(key, bytes); + } + return; + } + // common 不存在, 则往 common 存储一份 + bytesMap.put(commonByteKey, bytes); + bytesMap.put(key, new byte[]{}); + } + + public synchronized byte[] getByte(String classLoaderName){ + byte[] bytes = bytesMap.get(classLoaderName); + if(bytes == null || bytes.length == 0){ + bytes = bytesMap.get(commonByteKey); + } + return bytes; + } + + public boolean remove(String classLoaderName){ + bytesMap.remove(classLoaderName); + if(bytesMap.size() == 1 && bytesMap.containsKey(commonByteKey)){ + // 只存在一个common + bytesMap.clear(); + return true; + } else { + return false; + } + } + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/AbstractJarFile.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/AbstractJarFile.java new file mode 100644 index 0000000000000000000000000000000000000000..ae88cddfef45ce18f8ba2ae80255f97963085301 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/AbstractJarFile.java @@ -0,0 +1,91 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.jar; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.Permission; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public abstract class AbstractJarFile extends java.util.jar.JarFile{ + + /** + * Create a new {@link AbstractJarFile}. + * @param file the root jar file. + * @throws IOException on IO error + */ + AbstractJarFile(File file) throws IOException { + super(file); + } + + /** + * Return a URL that can be used to access this JAR file. NOTE: the specified URL + * cannot be serialized and or cloned. + * @return the URL + * @throws MalformedURLException if the URL is malformed + */ + abstract URL getUrl() throws MalformedURLException; + + /** + * Return the {@link JarFileType} of this instance. + * @return the jar file type + */ + abstract JarFileType getType(); + + /** + * Return the security permission for this JAR. + * @return the security permission. + */ + abstract Permission getPermission(); + + /** + * Return an {@link InputStream} for the entire jar contents. + * @return the contents input stream + * @throws IOException on IO error + */ + abstract InputStream getInputStream() throws IOException; + + /** + * The type of a {@link JarFile}. + */ + enum JarFileType { + + /** + * DIRECT + */ + DIRECT, + + /** + * NESTED_DIRECTORY + */ + NESTED_DIRECTORY, + + /** + * NESTED_JAR + */ + NESTED_JAR + + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/AsciiBytes.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/AsciiBytes.java new file mode 100644 index 0000000000000000000000000000000000000000..be75721440c1efa01830a8190a2a2e52785ace13 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/AsciiBytes.java @@ -0,0 +1,251 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.jar; + +import java.nio.charset.StandardCharsets; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public class AsciiBytes { + + private static final String EMPTY_STRING = ""; + + private static final int[] INITIAL_BYTE_BITMASK = { 0x7F, 0x1F, 0x0F, 0x07 }; + + private static final int SUBSEQUENT_BYTE_BITMASK = 0x3F; + + private final byte[] bytes; + + private final int offset; + + private final int length; + + private String string; + + private int hash; + + /** + * Create a new {@link AsciiBytes} from the specified String. + * @param string the source string + */ + AsciiBytes(String string) { + this(string.getBytes(StandardCharsets.UTF_8)); + this.string = string; + } + + /** + * Create a new {@link AsciiBytes} from the specified bytes. NOTE: underlying bytes + * are not expected to change. + * @param bytes the source bytes + */ + AsciiBytes(byte[] bytes) { + this(bytes, 0, bytes.length); + } + + /** + * Create a new {@link AsciiBytes} from the specified bytes. NOTE: underlying bytes + * are not expected to change. + * @param bytes the source bytes + * @param offset the offset + * @param length the length + */ + AsciiBytes(byte[] bytes, int offset, int length) { + if (offset < 0 || length < 0 || (offset + length) > bytes.length) { + throw new IndexOutOfBoundsException(); + } + this.bytes = bytes; + this.offset = offset; + this.length = length; + } + + int length() { + return this.length; + } + + boolean startsWith(AsciiBytes prefix) { + if (this == prefix) { + return true; + } + if (prefix.length > this.length) { + return false; + } + for (int i = 0; i < prefix.length; i++) { + if (this.bytes[i + this.offset] != prefix.bytes[i + prefix.offset]) { + return false; + } + } + return true; + } + + boolean endsWith(AsciiBytes postfix) { + if (this == postfix) { + return true; + } + if (postfix.length > this.length) { + return false; + } + for (int i = 0; i < postfix.length; i++) { + if (this.bytes[this.offset + (this.length - 1) - i] != postfix.bytes[postfix.offset + (postfix.length - 1) + - i]) { + return false; + } + } + return true; + } + + AsciiBytes substring(int beginIndex) { + return substring(beginIndex, this.length); + } + + AsciiBytes substring(int beginIndex, int endIndex) { + int length = endIndex - beginIndex; + if (this.offset + length > this.bytes.length) { + throw new IndexOutOfBoundsException(); + } + return new AsciiBytes(this.bytes, this.offset + beginIndex, length); + } + + boolean matches(CharSequence name, char suffix) { + int charIndex = 0; + int nameLen = name.length(); + int totalLen = nameLen + ((suffix != 0) ? 1 : 0); + for (int i = this.offset; i < this.offset + this.length; i++) { + int b = this.bytes[i]; + int remainingUtfBytes = getNumberOfUtfBytes(b) - 1; + b &= INITIAL_BYTE_BITMASK[remainingUtfBytes]; + for (int j = 0; j < remainingUtfBytes; j++) { + b = (b << 6) + (this.bytes[++i] & SUBSEQUENT_BYTE_BITMASK); + } + char c = getChar(name, suffix, charIndex++); + if (b <= 0xFFFF) { + if (c != b) { + return false; + } + } else { + if (c != ((b >> 0xA) + 0xD7C0)) { + return false; + } + c = getChar(name, suffix, charIndex++); + if (c != ((b & 0x3FF) + 0xDC00)) { + return false; + } + } + } + return charIndex == totalLen; + } + + private char getChar(CharSequence name, char suffix, int index) { + if (index < name.length()) { + return name.charAt(index); + } + if (index == name.length()) { + return suffix; + } + return 0; + } + + private int getNumberOfUtfBytes(int b) { + if ((b & 0x80) == 0) { + return 1; + } + int numberOfUtfBytes = 0; + while ((b & 0x80) != 0) { + b <<= 1; + numberOfUtfBytes++; + } + return numberOfUtfBytes; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (obj.getClass() == AsciiBytes.class) { + AsciiBytes other = (AsciiBytes) obj; + if (this.length == other.length) { + for (int i = 0; i < this.length; i++) { + if (this.bytes[this.offset + i] != other.bytes[other.offset + i]) { + return false; + } + } + return true; + } + } + return false; + } + + @Override + public int hashCode() { + int hash = this.hash; + if (hash == 0 && this.bytes.length > 0) { + for (int i = this.offset; i < this.offset + this.length; i++) { + int b = this.bytes[i]; + int remainingUtfBytes = getNumberOfUtfBytes(b) - 1; + b &= INITIAL_BYTE_BITMASK[remainingUtfBytes]; + for (int j = 0; j < remainingUtfBytes; j++) { + b = (b << 6) + (this.bytes[++i] & SUBSEQUENT_BYTE_BITMASK); + } + if (b <= 0xFFFF) { + hash = 31 * hash + b; + } else { + hash = 31 * hash + ((b >> 0xA) + 0xD7C0); + hash = 31 * hash + ((b & 0x3FF) + 0xDC00); + } + } + this.hash = hash; + } + return hash; + } + + @Override + public String toString() { + if (this.string == null) { + if (this.length == 0) { + this.string = EMPTY_STRING; + } else { + this.string = new String(this.bytes, this.offset, this.length, StandardCharsets.UTF_8); + } + } + return this.string; + } + + static String toString(byte[] bytes) { + return new String(bytes, StandardCharsets.UTF_8); + } + + static int hashCode(CharSequence charSequence) { + // We're compatible with String's hashCode() + if (charSequence instanceof StringSequence) { + // ... but save making an unnecessary String for StringSequence + return charSequence.hashCode(); + } + return charSequence.toString().hashCode(); + } + + static int hashCode(int hash, char suffix) { + return (suffix != 0) ? (31 * hash + suffix) : hash; + } + +} + diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/Bytes.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/Bytes.java new file mode 100644 index 0000000000000000000000000000000000000000..6b193528e1c3d656288b36ab84e3aeeb951e2290 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/Bytes.java @@ -0,0 +1,38 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.jar; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public class Bytes { + + private Bytes() { + } + + static long littleEndianValue(byte[] bytes, int offset, int length) { + long value = 0; + for (int i = length - 1; i >= 0; i--) { + value = ((value << 8) | (bytes[offset + i] & 0xFF)); + } + return value; + } + + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryEndRecord.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryEndRecord.java new file mode 100644 index 0000000000000000000000000000000000000000..3e941a34ec9cf0275908526a848517d19cb5e935 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryEndRecord.java @@ -0,0 +1,246 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.jar; + +import java.io.IOException; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public class CentralDirectoryEndRecord { + + private static final int MINIMUM_SIZE = 22; + + private static final int MAXIMUM_COMMENT_LENGTH = 0xFFFF; + + private static final int ZIP64_MAGICCOUNT = 0xFFFF; + + private static final int MAXIMUM_SIZE = MINIMUM_SIZE + MAXIMUM_COMMENT_LENGTH; + + private static final int SIGNATURE = 0x06054b50; + + private static final int COMMENT_LENGTH_OFFSET = 20; + + private static final int READ_BLOCK_SIZE = 256; + + private final Zip64End zip64End; + + private byte[] block; + + private int offset; + + private int size; + + /** + * Create a new {@link CentralDirectoryEndRecord} instance from the specified + * {@link RandomAccessData}, searching backwards from the end until a valid block is + * located. + * @param data the source data + * @throws IOException in case of I/O errors + */ + CentralDirectoryEndRecord(RandomAccessData data) throws IOException { + this.block = createBlockFromEndOfData(data, READ_BLOCK_SIZE); + this.size = MINIMUM_SIZE; + this.offset = this.block.length - this.size; + while (!isValid()) { + this.size++; + if (this.size > this.block.length) { + if (this.size >= MAXIMUM_SIZE || this.size > data.getSize()) { + throw new IOException( + "Unable to find ZIP central directory records after reading " + this.size + " bytes"); + } + this.block = createBlockFromEndOfData(data, this.size + READ_BLOCK_SIZE); + } + this.offset = this.block.length - this.size; + } + int startOfCentralDirectoryEndRecord = (int) (data.getSize() - this.size); + this.zip64End = isZip64() ? new Zip64End(data, startOfCentralDirectoryEndRecord) : null; + } + + private byte[] createBlockFromEndOfData(RandomAccessData data, int size) throws IOException { + int length = (int) Math.min(data.getSize(), size); + return data.read(data.getSize() - length, length); + } + + private boolean isValid() { + if (this.block.length < MINIMUM_SIZE || + Bytes.littleEndianValue(this.block, this.offset + 0, 4) != SIGNATURE) { + return false; + } + // Total size must be the structure size + comment + long commentLength = Bytes.littleEndianValue(this.block, this.offset + COMMENT_LENGTH_OFFSET, 2); + return this.size == MINIMUM_SIZE + commentLength; + } + + private boolean isZip64() { + return (int) Bytes.littleEndianValue(this.block, this.offset + 10, 2) == ZIP64_MAGICCOUNT; + } + + /** + * Returns the location in the data that the archive actually starts. For most files + * the archive data will start at 0, however, it is possible to have prefixed bytes + * (often used for startup scripts) at the beginning of the data. + * @param data the source data + * @return the offset within the data where the archive begins + */ + long getStartOfArchive(RandomAccessData data) { + long length = Bytes.littleEndianValue(this.block, this.offset + 12, 4); + long specifiedOffset = Bytes.littleEndianValue(this.block, this.offset + 16, 4); + long zip64EndSize = (this.zip64End != null) ? this.zip64End.getSize() : 0L; + int zip64LocSize = (this.zip64End != null) ? Zip64Locator.ZIP64_LOCSIZE : 0; + long actualOffset = data.getSize() - this.size - length - zip64EndSize - zip64LocSize; + return actualOffset - specifiedOffset; + } + + /** + * Return the bytes of the "Central directory" based on the offset indicated in this + * record. + * @param data the source data + * @return the central directory data + */ + RandomAccessData getCentralDirectory(RandomAccessData data) { + if (this.zip64End != null) { + return this.zip64End.getCentralDirectory(data); + } + long offset = Bytes.littleEndianValue(this.block, this.offset + 16, 4); + long length = Bytes.littleEndianValue(this.block, this.offset + 12, 4); + return data.getSubsection(offset, length); + } + + /** + * Return the number of ZIP entries in the file. + * @return the number of records in the zip + */ + int getNumberOfRecords() { + if (this.zip64End != null) { + return this.zip64End.getNumberOfRecords(); + } + long numberOfRecords = Bytes.littleEndianValue(this.block, this.offset + 10, 2); + return (int) numberOfRecords; + } + + String getComment() { + int commentLength = (int) Bytes.littleEndianValue(this.block, this.offset + COMMENT_LENGTH_OFFSET, 2); + AsciiBytes comment = new AsciiBytes(this.block, this.offset + COMMENT_LENGTH_OFFSET + 2, commentLength); + return comment.toString(); + } + + /** + * A Zip64 end of central directory record. + * + * @see Chapter + * 4.3.14 of Zip64 specification + */ + private static final class Zip64End { + + private static final int ZIP64_ENDTOT = 32; // total number of entries + + private static final int ZIP64_ENDSIZ = 40; // central directory size in bytes + + private static final int ZIP64_ENDOFF = 48; // offset of first CEN header + + private final Zip64Locator locator; + + private final long centralDirectoryOffset; + + private final long centralDirectoryLength; + + private final int numberOfRecords; + + private Zip64End(RandomAccessData data, int centralDirectoryEndOffset) throws IOException { + this(data, new Zip64Locator(data, centralDirectoryEndOffset)); + } + + private Zip64End(RandomAccessData data, Zip64Locator locator) throws IOException { + this.locator = locator; + byte[] block = data.read(locator.getZip64EndOffset(), 56); + this.centralDirectoryOffset = Bytes.littleEndianValue(block, ZIP64_ENDOFF, 8); + this.centralDirectoryLength = Bytes.littleEndianValue(block, ZIP64_ENDSIZ, 8); + this.numberOfRecords = (int) Bytes.littleEndianValue(block, ZIP64_ENDTOT, 8); + } + + /** + * Return the size of this zip 64 end of central directory record. + * @return size of this zip 64 end of central directory record + */ + private long getSize() { + return this.locator.getZip64EndSize(); + } + + /** + * Return the bytes of the "Central directory" based on the offset indicated in + * this record. + * @param data the source data + * @return the central directory data + */ + private RandomAccessData getCentralDirectory(RandomAccessData data) { + return data.getSubsection(this.centralDirectoryOffset, this.centralDirectoryLength); + } + + /** + * Return the number of entries in the zip64 archive. + * @return the number of records in the zip + */ + private int getNumberOfRecords() { + return this.numberOfRecords; + } + + } + + /** + * A Zip64 end of central directory locator. + * + * @see Chapter + * 4.3.15 of Zip64 specification + */ + private static final class Zip64Locator { + + static final int ZIP64_LOCSIZE = 20; // locator size + static final int ZIP64_LOCOFF = 8; // offset of zip64 end + + private final long zip64EndOffset; + + private final int offset; + + private Zip64Locator(RandomAccessData data, int centralDirectoryEndOffset) throws IOException { + this.offset = centralDirectoryEndOffset - ZIP64_LOCSIZE; + byte[] block = data.read(this.offset, ZIP64_LOCSIZE); + this.zip64EndOffset = Bytes.littleEndianValue(block, ZIP64_LOCOFF, 8); + } + + /** + * Return the size of the zip 64 end record located by this zip64 end locator. + * @return size of the zip 64 end record located by this zip64 end locator + */ + private long getZip64EndSize() { + return this.offset - this.zip64EndOffset; + } + + /** + * Return the offset to locate {@link Zip64End}. + * @return offset of the Zip64 end of central directory record + */ + private long getZip64EndOffset() { + return this.zip64EndOffset; + } + + } + + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryFileHeader.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryFileHeader.java new file mode 100644 index 0000000000000000000000000000000000000000..be3a47369b2f0a72230828d3a0f22ec4871e099f --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryFileHeader.java @@ -0,0 +1,187 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.jar; + +import java.io.IOException; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; +import java.time.temporal.ValueRange; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public class CentralDirectoryFileHeader implements FileHeader { + + private static final AsciiBytes SLASH = new AsciiBytes("/"); + + private static final byte[] NO_EXTRA = {}; + + private static final AsciiBytes NO_COMMENT = new AsciiBytes(""); + + private byte[] header; + + private int headerOffset; + + private AsciiBytes name; + + private byte[] extra; + + private AsciiBytes comment; + + private long localHeaderOffset; + + CentralDirectoryFileHeader() { + } + + CentralDirectoryFileHeader(byte[] header, int headerOffset, AsciiBytes name, byte[] extra, AsciiBytes comment, + long localHeaderOffset) { + this.header = header; + this.headerOffset = headerOffset; + this.name = name; + this.extra = extra; + this.comment = comment; + this.localHeaderOffset = localHeaderOffset; + } + + void load(byte[] data, int dataOffset, RandomAccessData variableData, int variableOffset, JarEntryFilter filter) + throws IOException { + // Load fixed part + this.header = data; + this.headerOffset = dataOffset; + long nameLength = Bytes.littleEndianValue(data, dataOffset + 28, 2); + long extraLength = Bytes.littleEndianValue(data, dataOffset + 30, 2); + long commentLength = Bytes.littleEndianValue(data, dataOffset + 32, 2); + this.localHeaderOffset = Bytes.littleEndianValue(data, dataOffset + 42, 4); + // Load variable part + dataOffset += 46; + if (variableData != null) { + data = variableData.read(variableOffset + 46, nameLength + extraLength + commentLength); + dataOffset = 0; + } + this.name = new AsciiBytes(data, dataOffset, (int) nameLength); + if (filter != null) { + this.name = filter.apply(this.name); + } + this.extra = NO_EXTRA; + this.comment = NO_COMMENT; + if (extraLength > 0) { + this.extra = new byte[(int) extraLength]; + System.arraycopy(data, (int) (dataOffset + nameLength), this.extra, 0, this.extra.length); + } + if (commentLength > 0) { + this.comment = new AsciiBytes(data, (int) (dataOffset + nameLength + extraLength), (int) commentLength); + } + } + + AsciiBytes getName() { + return this.name; + } + + @Override + public boolean hasName(CharSequence name, char suffix) { + return this.name.matches(name, suffix); + } + + boolean isDirectory() { + return this.name.endsWith(SLASH); + } + + @Override + public int getMethod() { + return (int) Bytes.littleEndianValue(this.header, this.headerOffset + 10, 2); + } + + long getTime() { + long datetime = Bytes.littleEndianValue(this.header, this.headerOffset + 12, 4); + return decodeMsDosFormatDateTime(datetime); + } + + /** + * Decode MS-DOS Date Time details. See + * Microsoft's documentation for more details of the format. + * @param datetime the date and time + * @return the date and time as milliseconds since the epoch + */ + private long decodeMsDosFormatDateTime(long datetime) { + int year = getChronoValue(((datetime >> 25) & 0x7f) + 1980, ChronoField.YEAR); + int month = getChronoValue((datetime >> 21) & 0x0f, ChronoField.MONTH_OF_YEAR); + int day = getChronoValue((datetime >> 16) & 0x1f, ChronoField.DAY_OF_MONTH); + int hour = getChronoValue((datetime >> 11) & 0x1f, ChronoField.HOUR_OF_DAY); + int minute = getChronoValue((datetime >> 5) & 0x3f, ChronoField.MINUTE_OF_HOUR); + int second = getChronoValue((datetime << 1) & 0x3e, ChronoField.SECOND_OF_MINUTE); + return ZonedDateTime.of(year, month, day, hour, minute, second, 0, ZoneId.systemDefault()).toInstant() + .truncatedTo(ChronoUnit.SECONDS).toEpochMilli(); + } + + long getCrc() { + return Bytes.littleEndianValue(this.header, this.headerOffset + 16, 4); + } + + @Override + public long getCompressedSize() { + return Bytes.littleEndianValue(this.header, this.headerOffset + 20, 4); + } + + @Override + public long getSize() { + return Bytes.littleEndianValue(this.header, this.headerOffset + 24, 4); + } + + byte[] getExtra() { + return this.extra; + } + + boolean hasExtra() { + return this.extra.length > 0; + } + + AsciiBytes getComment() { + return this.comment; + } + + @Override + public long getLocalHeaderOffset() { + return this.localHeaderOffset; + } + + @Override + public CentralDirectoryFileHeader clone() { + byte[] header = new byte[46]; + System.arraycopy(this.header, this.headerOffset, header, 0, header.length); + return new CentralDirectoryFileHeader(header, 0, this.name, header, this.comment, this.localHeaderOffset); + } + + static CentralDirectoryFileHeader fromRandomAccessData(RandomAccessData data, int offset, JarEntryFilter filter) + throws IOException { + CentralDirectoryFileHeader fileHeader = new CentralDirectoryFileHeader(); + byte[] bytes = data.read(offset, 46); + fileHeader.load(bytes, 0, data, offset, filter); + return fileHeader; + } + + private static int getChronoValue(long value, ChronoField field) { + ValueRange range = field.range(); + return Math.toIntExact(Math.min(Math.max(value, range.getMinimum()), range.getMaximum())); + } + +} + diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryParser.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryParser.java new file mode 100644 index 0000000000000000000000000000000000000000..e589c37526923106bb85520ad2e3dca7cf405013 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryParser.java @@ -0,0 +1,97 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.jar; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public class CentralDirectoryParser { + + private static final int CENTRAL_DIRECTORY_HEADER_BASE_SIZE = 46; + + private final List visitors = new ArrayList<>(); + + T addVisitor(T visitor) { + this.visitors.add(visitor); + return visitor; + } + + /** + * Parse the source data, triggering {@link CentralDirectoryVisitor visitors}. + * @param data the source data + * @param skipPrefixBytes if prefix bytes should be skipped + * @return the actual archive data without any prefix bytes + * @throws IOException on error + */ + RandomAccessData parse(RandomAccessData data, boolean skipPrefixBytes) throws IOException { + CentralDirectoryEndRecord endRecord = new CentralDirectoryEndRecord(data); + if (skipPrefixBytes) { + data = getArchiveData(endRecord, data); + } + RandomAccessData centralDirectoryData = endRecord.getCentralDirectory(data); + visitStart(endRecord, centralDirectoryData); + parseEntries(endRecord, centralDirectoryData); + visitEnd(); + return data; + } + + private void parseEntries(CentralDirectoryEndRecord endRecord, RandomAccessData centralDirectoryData) + throws IOException { + byte[] bytes = centralDirectoryData.read(0, centralDirectoryData.getSize()); + CentralDirectoryFileHeader fileHeader = new CentralDirectoryFileHeader(); + int dataOffset = 0; + for (int i = 0; i < endRecord.getNumberOfRecords(); i++) { + fileHeader.load(bytes, dataOffset, null, 0, null); + visitFileHeader(dataOffset, fileHeader); + dataOffset += CENTRAL_DIRECTORY_HEADER_BASE_SIZE + fileHeader.getName().length() + + fileHeader.getComment().length() + fileHeader.getExtra().length; + } + } + + private RandomAccessData getArchiveData(CentralDirectoryEndRecord endRecord, RandomAccessData data) { + long offset = endRecord.getStartOfArchive(data); + if (offset == 0) { + return data; + } + return data.getSubsection(offset, data.getSize() - offset); + } + + private void visitStart(CentralDirectoryEndRecord endRecord, RandomAccessData centralDirectoryData) { + for (CentralDirectoryVisitor visitor : this.visitors) { + visitor.visitStart(endRecord, centralDirectoryData); + } + } + + private void visitFileHeader(int dataOffset, CentralDirectoryFileHeader fileHeader) { + for (CentralDirectoryVisitor visitor : this.visitors) { + visitor.visitFileHeader(fileHeader, dataOffset); + } + } + + private void visitEnd() { + for (CentralDirectoryVisitor visitor : this.visitors) { + visitor.visitEnd(); + } + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryVisitor.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryVisitor.java new file mode 100644 index 0000000000000000000000000000000000000000..046120ce0e118cad130b78795e557c938d82e436 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryVisitor.java @@ -0,0 +1,45 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.jar; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public interface CentralDirectoryVisitor { + + /** + * visitStart + * @param endRecord endRecord + * @param centralDirectoryData centralDirectoryData + */ + void visitStart(CentralDirectoryEndRecord endRecord, RandomAccessData centralDirectoryData); + + /** + * visitFileHeader + * @param fileHeader fileHeader + * @param dataOffset dataOffset + */ + void visitFileHeader(CentralDirectoryFileHeader fileHeader, int dataOffset); + + /** + * visitEnd + */ + void visitEnd(); + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/FileHeader.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/FileHeader.java new file mode 100644 index 0000000000000000000000000000000000000000..7c500e4631a4cb705891944dc5bf676cc1f3c963 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/FileHeader.java @@ -0,0 +1,63 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.jar; + +import java.util.zip.ZipEntry; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public interface FileHeader { + + /** + * Returns {@code true} if the header has the given name. + * @param name the name to test + * @param suffix an additional suffix (or {@code 0}) + * @return {@code true} if the header has the given name + */ + boolean hasName(CharSequence name, char suffix); + + /** + * Return the offset of the load file header within the archive data. + * @return the local header offset + */ + long getLocalHeaderOffset(); + + /** + * Return the compressed size of the entry. + * @return the compressed size. + */ + long getCompressedSize(); + + /** + * Return the uncompressed size of the entry. + * @return the uncompressed size. + */ + long getSize(); + + /** + * Return the method used to compress the data. + * @return the zip compression method + * @see ZipEntry#STORED + * @see ZipEntry#DEFLATED + */ + int getMethod(); + + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/Handler.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/Handler.java new file mode 100644 index 0000000000000000000000000000000000000000..558bb1b3c562fcedf0233fd0533e475f4ef6e53e --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/Handler.java @@ -0,0 +1,450 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.jar; + +import com.gitee.starblues.loader.PluginResourceStorage; + +import java.io.File; +import java.io.IOException; +import java.lang.ref.SoftReference; +import java.net.*; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public class Handler extends URLStreamHandler { + + // NOTE: in order to be found as a URL protocol handler, this class must be public, + // must be named Handler and must be in a package ending '.jar' + + private static final String JAR_PROTOCOL = "jar:"; + + private static final String FILE_PROTOCOL = "file:"; + + private static final String TOMCAT_WARFILE_PROTOCOL = "war:file:"; + + private static final String SEPARATOR = "!/"; + + private static final Pattern SEPARATOR_PATTERN = Pattern.compile(SEPARATOR, Pattern.LITERAL); + + private static final String CURRENT_DIR = "/./"; + + private static final Pattern CURRENT_DIR_PATTERN = Pattern.compile(CURRENT_DIR, Pattern.LITERAL); + + private static final String PARENT_DIR = "/../"; + + private static final String PROTOCOL_HANDLER = "java.protocol.handler.pkgs"; + + private static final String[] FALLBACK_HANDLERS = { "sun.net.www.protocol.jar.Handler" }; + + private static URL jarContextUrl; + + private static SoftReference> rootFileCache; + + static { + rootFileCache = new SoftReference<>(null); + } + + private final JarFile jarFile; + + private URLStreamHandler fallbackHandler; + + public Handler() { + this(null); + } + + public Handler(JarFile jarFile) { + this.jarFile = jarFile; + } + + @Override + protected URLConnection openConnection(URL url) throws IOException { + URLConnection jarURLConnection = null; + if (this.jarFile != null && isUrlInJarFile(url, this.jarFile)) { + jarURLConnection = JarURLConnection.get(url, this.jarFile); + } else { + try { + jarURLConnection = JarURLConnection.get(url, getRootJarFileFromUrl(url)); + } catch (Exception ex) { + jarURLConnection = openFallbackConnection(url, ex); + } + } + return jarURLConnection; + } + + private boolean isUrlInJarFile(URL url, JarFile jarFile) throws MalformedURLException { + // Try the path first to save building a new url string each time + return url.getPath().startsWith(jarFile.getUrl().getPath()) + && url.toString().startsWith(jarFile.getUrlString()); + } + + private URLConnection openFallbackConnection(URL url, Exception reason) throws IOException { + try { + URLConnection connection = openFallbackTomcatConnection(url); + connection = (connection != null) ? connection : openFallbackContextConnection(url); + return (connection != null) ? connection : openFallbackHandlerConnection(url); + } catch (Exception ex) { + if (reason instanceof IOException) { + log(false, "Unable to open fallback handler", ex); + throw (IOException) reason; + } + log(true, "Unable to open fallback handler", ex); + if (reason instanceof RuntimeException) { + throw (RuntimeException) reason; + } + throw new IllegalStateException(reason); + } + } + + /** + * Attempt to open a Tomcat formatted 'jar:war:file:...' URL. This method allows us to + * use our own nested JAR support to open the content rather than the logic in + * {@code sun.net.www.protocol.jar.URLJarFile} which will extract the nested jar to + * the temp folder to that its content can be accessed. + * @param url the URL to open + * @return a {@link URLConnection} or {@code null} + */ + private URLConnection openFallbackTomcatConnection(URL url) { + String file = url.getFile(); + if (isTomcatWarUrl(file)) { + file = file.substring(TOMCAT_WARFILE_PROTOCOL.length()); + file = file.replaceFirst("\\*/", "!/"); + try { + URLConnection connection = openConnection(new URL("jar:file:" + file)); + connection.getInputStream().close(); + return connection; + } catch (IOException ex) { + // ignore + } + } + return null; + } + + private boolean isTomcatWarUrl(String file) { + if (file.startsWith(TOMCAT_WARFILE_PROTOCOL) || !file.contains("*/")) { + try { + URLConnection connection = new URL(file).openConnection(); + if (connection.getClass().getName().startsWith("org.apache.catalina")) { + return true; + } + } catch (Exception ex) { + // ignore + } + } + return false; + } + + /** + * Attempt to open a fallback connection by using a context URL captured before the + * jar handler was replaced with our own version. Since this method doesn't use + * reflection it won't trigger "illegal reflective access operation has occurred" + * warnings on Java 13+. + * @param url the URL to open + * @return a {@link URLConnection} or {@code null} + */ + private URLConnection openFallbackContextConnection(URL url) { + try { + if (jarContextUrl != null) { + return new URL(jarContextUrl, url.toExternalForm()).openConnection(); + } + } catch (Exception ex) { + // ignore + } + return null; + } + + /** + * Attempt to open a fallback connection by using reflection to access Java's default + * jar {@link URLStreamHandler}. + * @param url the URL to open + * @return the {@link URLConnection} + * @throws Exception if not connection could be opened + */ + private URLConnection openFallbackHandlerConnection(URL url) throws Exception { + URLStreamHandler fallbackHandler = getFallbackHandler(); + return new URL(null, url.toExternalForm(), fallbackHandler).openConnection(); + } + + private URLStreamHandler getFallbackHandler() { + if (this.fallbackHandler != null) { + return this.fallbackHandler; + } + for (String handlerClassName : FALLBACK_HANDLERS) { + try { + Class handlerClass = Class.forName(handlerClassName); + this.fallbackHandler = (URLStreamHandler) handlerClass.getDeclaredConstructor().newInstance(); + return this.fallbackHandler; + } catch (Exception ex) { + // Ignore + } + } + throw new IllegalStateException("Unable to find fallback handler"); + } + + private void log(boolean warning, String message, Exception cause) { + try { + Level level = warning ? Level.WARNING : Level.FINEST; + Logger.getLogger(getClass().getName()).log(level, message, cause); + } catch (Exception ex) { + if (warning) { + System.err.println("WARNING: " + message); + } + } + } + + @Override + protected void parseURL(URL context, String spec, int start, int limit) { + if (spec.regionMatches(true, 0, JAR_PROTOCOL, 0, JAR_PROTOCOL.length())) { + setFile(context, getFileFromSpec(spec.substring(start, limit))); + } else { + setFile(context, getFileFromContext(context, spec.substring(start, limit))); + } + } + + private String getFileFromSpec(String spec) { + int separatorIndex = spec.lastIndexOf("!/"); + if (separatorIndex == -1) { + throw new IllegalArgumentException("No !/ in spec '" + spec + "'"); + } + try { + new URL(spec.substring(0, separatorIndex)); + return spec; + } catch (MalformedURLException ex) { + throw new IllegalArgumentException("Invalid spec URL '" + spec + "'", ex); + } + } + + private String getFileFromContext(URL context, String spec) { + String file = context.getFile(); + if (spec.startsWith("/")) { + return trimToJarRoot(file) + SEPARATOR + spec.substring(1); + } + if (file.endsWith("/")) { + return file + spec; + } + int lastSlashIndex = file.lastIndexOf('/'); + if (lastSlashIndex == -1) { + throw new IllegalArgumentException("No / found in context URL's file '" + file + "'"); + } + return file.substring(0, lastSlashIndex + 1) + spec; + } + + private String trimToJarRoot(String file) { + int lastSeparatorIndex = file.lastIndexOf(SEPARATOR); + if (lastSeparatorIndex == -1) { + throw new IllegalArgumentException("No !/ found in context URL's file '" + file + "'"); + } + return file.substring(0, lastSeparatorIndex); + } + + private void setFile(URL context, String file) { + String path = normalize(file); + String query = null; + int queryIndex = path.lastIndexOf('?'); + if (queryIndex != -1) { + query = path.substring(queryIndex + 1); + path = path.substring(0, queryIndex); + } + setURL(context, JAR_PROTOCOL, null, -1, null, null, path, query, context.getRef()); + } + + private String normalize(String file) { + if (!file.contains(CURRENT_DIR) && !file.contains(PARENT_DIR)) { + return file; + } + int afterLastSeparatorIndex = file.lastIndexOf(SEPARATOR) + SEPARATOR.length(); + String afterSeparator = file.substring(afterLastSeparatorIndex); + afterSeparator = replaceParentDir(afterSeparator); + afterSeparator = replaceCurrentDir(afterSeparator); + return file.substring(0, afterLastSeparatorIndex) + afterSeparator; + } + + private String replaceParentDir(String file) { + int parentDirIndex; + while ((parentDirIndex = file.indexOf(PARENT_DIR)) >= 0) { + int precedingSlashIndex = file.lastIndexOf('/', parentDirIndex - 1); + if (precedingSlashIndex >= 0) { + file = file.substring(0, precedingSlashIndex) + file.substring(parentDirIndex + 3); + } else { + file = file.substring(parentDirIndex + 4); + } + } + return file; + } + + private String replaceCurrentDir(String file) { + return CURRENT_DIR_PATTERN.matcher(file).replaceAll("/"); + } + + @Override + protected int hashCode(URL u) { + return hashCode(u.getProtocol(), u.getFile()); + } + + private int hashCode(String protocol, String file) { + int result = (protocol != null) ? protocol.hashCode() : 0; + int separatorIndex = file.indexOf(SEPARATOR); + if (separatorIndex == -1) { + return result + file.hashCode(); + } + String source = file.substring(0, separatorIndex); + String entry = canonicalize(file.substring(separatorIndex + 2)); + try { + result += new URL(source).hashCode(); + } catch (MalformedURLException ex) { + result += source.hashCode(); + } + result += entry.hashCode(); + return result; + } + + @Override + protected boolean sameFile(URL u1, URL u2) { + if (!"jar".equals(u1.getProtocol()) || !"jar".equals(u2.getProtocol())) { + return false; + } + int separator1 = u1.getFile().indexOf(SEPARATOR); + int separator2 = u2.getFile().indexOf(SEPARATOR); + if (separator1 == -1 || separator2 == -1) { + return super.sameFile(u1, u2); + } + String nested1 = u1.getFile().substring(separator1 + SEPARATOR.length()); + String nested2 = u2.getFile().substring(separator2 + SEPARATOR.length()); + if (!nested1.equals(nested2)) { + String canonical1 = canonicalize(nested1); + String canonical2 = canonicalize(nested2); + if (!canonical1.equals(canonical2)) { + return false; + } + } + String root1 = u1.getFile().substring(0, separator1); + String root2 = u2.getFile().substring(0, separator2); + try { + return super.sameFile(new URL(root1), new URL(root2)); + } catch (MalformedURLException ex) { + // Continue + } + return super.sameFile(u1, u2); + } + + private String canonicalize(String path) { + return SEPARATOR_PATTERN.matcher(path).replaceAll("/"); + } + + public JarFile getRootJarFileFromUrl(URL url) throws IOException { + String spec = url.getFile(); + int separatorIndex = spec.indexOf(SEPARATOR); + if (separatorIndex == -1) { + throw new MalformedURLException("Jar URL does not contain !/ separator"); + } + String name = spec.substring(0, separatorIndex); + return getRootJarFile(name); + } + + private JarFile getRootJarFile(String name) throws IOException { + try { + if (!name.startsWith(FILE_PROTOCOL)) { + throw new IllegalStateException("Not a file URL"); + } + File file = new File(URI.create(name)); + JarFile jarFile = PluginResourceStorage.getRootJarFile(file); + if (jarFile == null) { + jarFile = new JarFile(file); + PluginResourceStorage.addRootJarFile(file, jarFile); + } + return jarFile; + } catch (Exception ex) { + throw new IOException("Unable to open root Jar file '" + name + "'", ex); + } + } + + /** + * Add the given {@link JarFile} to the root file cache. + * @param sourceFile the source file to add + * @param jarFile the jar file. + */ + static void addToRootFileCache(File sourceFile, JarFile jarFile) { + Map cache = rootFileCache.get(); + if (cache == null) { + cache = new ConcurrentHashMap<>(); + rootFileCache = new SoftReference<>(cache); + } + cache.put(sourceFile, jarFile); + } + + /** + * If possible, capture a URL that is configured with the original jar handler so that + * we can use it as a fallback context later. We can only do this if we know that we + * can reset the handlers after. + */ + static void captureJarContextUrl() { + if (canResetCachedUrlHandlers()) { + String handlers = System.getProperty(PROTOCOL_HANDLER, ""); + try { + System.clearProperty(PROTOCOL_HANDLER); + try { + resetCachedUrlHandlers(); + jarContextUrl = new URL("jar:file:context.jar!/"); + URLConnection connection = jarContextUrl.openConnection(); + if (connection instanceof JarURLConnection) { + jarContextUrl = null; + } + } catch (Exception ex) { + // ignore + } + } finally { + if (handlers == null) { + System.clearProperty(PROTOCOL_HANDLER); + } else { + System.setProperty(PROTOCOL_HANDLER, handlers); + } + } + resetCachedUrlHandlers(); + } + } + + private static boolean canResetCachedUrlHandlers() { + try { + resetCachedUrlHandlers(); + return true; + } catch (Error ex) { + return false; + } + } + + private static void resetCachedUrlHandlers() { + URL.setURLStreamHandlerFactory(null); + } + + /** + * Set if a generic static exception can be thrown when a URL cannot be connected. + * This optimization is used during class loading to save creating lots of exceptions + * which are then swallowed. + * @param useFastConnectionExceptions if fast connection exceptions can be used. + */ + public static void setUseFastConnectionExceptions(boolean useFastConnectionExceptions) { + JarURLConnection.setUseFastExceptions(useFastConnectionExceptions); + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarEntry.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarEntry.java new file mode 100644 index 0000000000000000000000000000000000000000..b5c0a7c0501e7a9c6126847932f058a763572f65 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarEntry.java @@ -0,0 +1,120 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.jar; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.CodeSigner; +import java.security.cert.Certificate; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public class JarEntry extends java.util.jar.JarEntry implements FileHeader { + + private final int index; + + private final AsciiBytes name; + + private final AsciiBytes headerName; + + private final JarFile jarFile; + + private long localHeaderOffset; + + private volatile JarEntryCertification certification; + + JarEntry(JarFile jarFile, int index, CentralDirectoryFileHeader header, AsciiBytes nameAlias) { + super((nameAlias != null) ? nameAlias.toString() : header.getName().toString()); + this.index = index; + this.name = (nameAlias != null) ? nameAlias : header.getName(); + this.headerName = header.getName(); + this.jarFile = jarFile; + this.localHeaderOffset = header.getLocalHeaderOffset(); + setCompressedSize(header.getCompressedSize()); + setMethod(header.getMethod()); + setCrc(header.getCrc()); + setComment(header.getComment().toString()); + setSize(header.getSize()); + setTime(header.getTime()); + if (header.hasExtra()) { + setExtra(header.getExtra()); + } + } + + int getIndex() { + return this.index; + } + + AsciiBytes getAsciiBytesName() { + return this.name; + } + + @Override + public boolean hasName(CharSequence name, char suffix) { + return this.headerName.matches(name, suffix); + } + + /** + * Return a {@link URL} for this {@link JarEntry}. + * @return the URL for the entry + * @throws MalformedURLException if the URL is not valid + */ + URL getUrl() throws MalformedURLException { + return new URL(this.jarFile.getUrl(), getName()); + } + + @Override + public Attributes getAttributes() throws IOException { + Manifest manifest = this.jarFile.getManifest(); + return (manifest != null) ? manifest.getAttributes(getName()) : null; + } + + @Override + public Certificate[] getCertificates() { + return getCertification().getCertificates(); + } + + @Override + public CodeSigner[] getCodeSigners() { + return getCertification().getCodeSigners(); + } + + private JarEntryCertification getCertification() { + if (!this.jarFile.isSigned()) { + return JarEntryCertification.NONE; + } + JarEntryCertification certification = this.certification; + if (certification == null) { + certification = this.jarFile.getCertification(this); + this.certification = certification; + } + return certification; + } + + @Override + public long getLocalHeaderOffset() { + return this.localHeaderOffset; + } + +} + diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarEntryCertification.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarEntryCertification.java new file mode 100644 index 0000000000000000000000000000000000000000..2313ef8bf899a141404eae4792673af3c7f327cb --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarEntryCertification.java @@ -0,0 +1,58 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.jar; + +import java.security.CodeSigner; +import java.security.cert.Certificate; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public class JarEntryCertification { + + static final JarEntryCertification NONE = new JarEntryCertification(null, null); + + private final Certificate[] certificates; + + private final CodeSigner[] codeSigners; + + JarEntryCertification(Certificate[] certificates, CodeSigner[] codeSigners) { + this.certificates = certificates; + this.codeSigners = codeSigners; + } + + Certificate[] getCertificates() { + return (this.certificates != null) ? this.certificates.clone() : null; + } + + CodeSigner[] getCodeSigners() { + return (this.codeSigners != null) ? this.codeSigners.clone() : null; + } + + static JarEntryCertification from(java.util.jar.JarEntry certifiedEntry) { + Certificate[] certificates = (certifiedEntry != null) ? certifiedEntry.getCertificates() : null; + CodeSigner[] codeSigners = (certifiedEntry != null) ? certifiedEntry.getCodeSigners() : null; + if (certificates == null && codeSigners == null) { + return NONE; + } + return new JarEntryCertification(certificates, codeSigners); + } + + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarEntryFilter.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarEntryFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..defa2252f546dc788a46ca49f7ba2f489d44ff8a --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarEntryFilter.java @@ -0,0 +1,36 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.jar; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public interface JarEntryFilter { + + /** + * Apply the jar entry filter. + * @param name the current entry name. This may be different that the original entry + * name if a previous filter has been applied + * @return the new name of the entry or {@code null} if the entry should not be + * included. + */ + AsciiBytes apply(AsciiBytes name); + + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarFile.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarFile.java new file mode 100644 index 0000000000000000000000000000000000000000..e22c64d7d44028286dd1a861c5278340fd5259aa --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarFile.java @@ -0,0 +1,443 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.jar; + +import java.io.File; +import java.io.FilePermission; +import java.io.IOException; +import java.io.InputStream; +import java.lang.ref.SoftReference; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLStreamHandler; +import java.net.URLStreamHandlerFactory; +import java.security.Permission; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Supplier; +import java.util.jar.Manifest; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import java.util.zip.ZipEntry; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public class JarFile extends AbstractJarFile implements Iterable { + + + private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF"; + + private static final String PROTOCOL_HANDLER = "java.protocol.handler.pkgs"; + + private static final String HANDLERS_PACKAGE = "com.gitee.starblues.loader"; + + private static final AsciiBytes META_INF = new AsciiBytes("META-INF/"); + + private static final AsciiBytes SIGNATURE_FILE_EXTENSION = new AsciiBytes(".SF"); + + private static final String READ_ACTION = "read"; + + private final RandomAccessDataFile rootFile; + + private final String pathFromRoot; + + private final RandomAccessData data; + + private final JarFileType type; + + private URL url; + + private String urlString; + + private JarFileEntries entries; + + private Supplier manifestSupplier; + + private SoftReference manifest; + + private boolean signed; + + private String comment; + + private volatile boolean closed; + + /** + * Create a new {@link JarFile} backed by the specified file. + * @param file the root jar file + * @throws IOException if the file cannot be read + */ + public JarFile(File file) throws IOException { + this(new RandomAccessDataFile(file)); + } + + /** + * Create a new {@link JarFile} backed by the specified file. + * @param file the root jar file + * @throws IOException if the file cannot be read + */ + JarFile(RandomAccessDataFile file) throws IOException { + this(file, "", file, JarFileType.DIRECT); + } + + /** + * Private constructor used to create a new {@link JarFile} either directly or from a + * nested entry. + * @param rootFile the root jar file + * @param pathFromRoot the name of this file + * @param data the underlying data + * @param type the type of the jar file + * @throws IOException if the file cannot be read + */ + private JarFile(RandomAccessDataFile rootFile, String pathFromRoot, RandomAccessData data, JarFileType type) + throws IOException { + this(rootFile, pathFromRoot, data, null, type, null); + } + + private JarFile(RandomAccessDataFile rootFile, String pathFromRoot, RandomAccessData data, JarEntryFilter filter, + JarFileType type, Supplier manifestSupplier) throws IOException { + super(rootFile.getFile()); + super.close(); + this.rootFile = rootFile; + this.pathFromRoot = pathFromRoot; + CentralDirectoryParser parser = new CentralDirectoryParser(); + this.entries = parser.addVisitor(new JarFileEntries(this, filter)); + this.type = type; + parser.addVisitor(centralDirectoryVisitor()); + try { + this.data = parser.parse(data, filter == null); + } catch (RuntimeException ex) { + close(); + throw ex; + } + this.manifestSupplier = (manifestSupplier != null) ? manifestSupplier : () -> { + try (InputStream inputStream = getInputStream(MANIFEST_NAME)) { + if (inputStream == null) { + return null; + } + return new Manifest(inputStream); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + }; + } + + private CentralDirectoryVisitor centralDirectoryVisitor() { + return new CentralDirectoryVisitor() { + + @Override + public void visitStart(CentralDirectoryEndRecord endRecord, RandomAccessData centralDirectoryData) { + JarFile.this.comment = endRecord.getComment(); + } + + @Override + public void visitFileHeader(CentralDirectoryFileHeader fileHeader, int dataOffset) { + AsciiBytes name = fileHeader.getName(); + if (name.startsWith(META_INF) && name.endsWith(SIGNATURE_FILE_EXTENSION)) { + JarFile.this.signed = true; + } + } + + @Override + public void visitEnd() { + } + + }; + } + + @Override + Permission getPermission() { + return new FilePermission(this.rootFile.getFile().getPath(), READ_ACTION); + } + + protected final RandomAccessDataFile getRootJarFile() { + return this.rootFile; + } + + RandomAccessData getData() { + return this.data; + } + + @Override + public Manifest getManifest() throws IOException { + Manifest manifest = (this.manifest != null) ? this.manifest.get() : null; + if (manifest == null) { + try { + manifest = this.manifestSupplier.get(); + } catch (RuntimeException ex) { + throw new IOException(ex); + } + this.manifest = new SoftReference<>(manifest); + } + return manifest; + } + + @Override + public Enumeration entries() { + return new JarEntryEnumeration(this.entries.iterator()); + } + + @Override + public Stream stream() { + Spliterator spliterator = Spliterators.spliterator(iterator(), size(), + Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.IMMUTABLE | Spliterator.NONNULL); + return StreamSupport.stream(spliterator, false); + } + + /** + * Return an iterator for the contained entries. + * @see java.lang.Iterable#iterator() + * @since 2.3.0 + */ + @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Iterator iterator() { + return (Iterator) this.entries.iterator(this::ensureOpen); + } + + public JarEntry getJarEntry(CharSequence name) { + return this.entries.getEntry(name); + } + + @Override + public JarEntry getJarEntry(String name) { + return (JarEntry) getEntry(name); + } + + public boolean containsEntry(String name) { + return this.entries.containsEntry(name); + } + + @Override + public ZipEntry getEntry(String name) { + ensureOpen(); + return this.entries.getEntry(name); + } + + @Override + InputStream getInputStream() throws IOException { + return this.data.getInputStream(); + } + + @Override + public synchronized InputStream getInputStream(ZipEntry entry) throws IOException { + ensureOpen(); + if (entry instanceof JarEntry) { + return this.entries.getInputStream((JarEntry) entry); + } + return getInputStream((entry != null) ? entry.getName() : null); + } + + InputStream getInputStream(String name) throws IOException { + return this.entries.getInputStream(name); + } + + /** + * Return a nested {@link JarFile} loaded from the specified entry. + * @param entry the zip entry + * @return a {@link JarFile} for the entry + * @throws IOException if the nested jar file cannot be read + */ + public synchronized JarFile getNestedJarFile(ZipEntry entry) throws IOException { + return getNestedJarFile((JarEntry) entry); + } + + /** + * Return a nested {@link JarFile} loaded from the specified entry. + * @param entry the zip entry + * @return a {@link JarFile} for the entry + * @throws IOException if the nested jar file cannot be read + */ + public synchronized JarFile getNestedJarFile(JarEntry entry) throws IOException { + try { + return createJarFileFromEntry(entry); + } catch (Exception ex) { + throw new IOException("Unable to open nested jar file '" + entry.getName() + "'", ex); + } + } + + private JarFile createJarFileFromEntry(JarEntry entry) throws IOException { + if (entry.isDirectory()) { + return createJarFileFromDirectoryEntry(entry); + } + return createJarFileFromFileEntry(entry); + } + + private JarFile createJarFileFromDirectoryEntry(JarEntry entry) throws IOException { + AsciiBytes name = entry.getAsciiBytesName(); + JarEntryFilter filter = (candidate) -> { + if (candidate.startsWith(name) && !candidate.equals(name)) { + return candidate.substring(name.length()); + } + return null; + }; + return new JarFile(this.rootFile, this.pathFromRoot + "!/" + entry.getName().substring(0, name.length() - 1), + this.data, filter, JarFileType.NESTED_DIRECTORY, this.manifestSupplier); + } + + private JarFile createJarFileFromFileEntry(JarEntry entry) throws IOException { + if (entry.getMethod() != ZipEntry.STORED) { + throw new IllegalStateException( + "Unable to open nested entry '" + entry.getName() + "'. It has been compressed and nested " + + "jar files must be stored without compression. Please check the " + + "mechanism used to create your executable jar file"); + } + RandomAccessData entryData = this.entries.getEntryData(entry.getName()); + return new JarFile(this.rootFile, this.pathFromRoot + "!/" + entry.getName(), entryData, + JarFileType.NESTED_JAR); + } + + @Override + public String getComment() { + ensureOpen(); + return this.comment; + } + + @Override + public int size() { + ensureOpen(); + return this.entries.getSize(); + } + + @Override + public void close() throws IOException { + if (this.closed) { + return; + } + this.closed = true; + if (this.type == JarFileType.DIRECT) { + this.rootFile.close(); + } + entries.clearCache(); + } + + private void ensureOpen() { + if (this.closed) { + throw new IllegalStateException("zip file closed"); + } + } + + boolean isClosed() { + return this.closed; + } + + String getUrlString() throws MalformedURLException { + if (this.urlString == null) { + this.urlString = getUrl().toString(); + } + return this.urlString; + } + + @Override + public URL getUrl() throws MalformedURLException { + if (this.url == null) { + String file = this.rootFile.getFile().toURI() + this.pathFromRoot + "!/"; + file = file.replace("file:////", "file://"); // Fix UNC paths + this.url = new URL("jar", "", -1, file, new Handler(this)); + } + return this.url; + } + + @Override + public String toString() { + return getName(); + } + + @Override + public String getName() { + return this.rootFile.getFile() + this.pathFromRoot; + } + + boolean isSigned() { + return this.signed; + } + + JarEntryCertification getCertification(JarEntry entry) { + try { + return this.entries.getCertification(entry); + } catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + + public void clearCache() { + this.entries.clearCache(); + } + + protected String getPathFromRoot() { + return this.pathFromRoot; + } + + @Override + JarFileType getType() { + return this.type; + } + + /** + * Register a {@literal 'java.protocol.handler.pkgs'} property so that a + * {@link URLStreamHandler} will be located to deal with jar URLs. + */ + public static void registerUrlProtocolHandler() { + Handler.captureJarContextUrl(); + String handlers = System.getProperty(PROTOCOL_HANDLER, ""); + System.setProperty(PROTOCOL_HANDLER, + ((handlers == null || handlers.isEmpty()) ? HANDLERS_PACKAGE : handlers + "|" + HANDLERS_PACKAGE)); + resetCachedUrlHandlers(); + } + + /** + * Reset any cached handlers just in case a jar protocol has already been used. We + * reset the handler by trying to set a null {@link URLStreamHandlerFactory} which + * should have no effect other than clearing the handlers cache. + */ + private static void resetCachedUrlHandlers() { + try { + URL.setURLStreamHandlerFactory(null); + } catch (Error ex) { + // Ignore + } + } + + /** + * An {@link Enumeration} on {@linkplain java.util.jar.JarEntry jar entries}. + */ + private static class JarEntryEnumeration implements Enumeration { + + private final Iterator iterator; + + JarEntryEnumeration(Iterator iterator) { + this.iterator = iterator; + } + + @Override + public boolean hasMoreElements() { + return this.iterator.hasNext(); + } + + @Override + public java.util.jar.JarEntry nextElement() { + return this.iterator.next(); + } + + } + + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarFileEntries.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarFileEntries.java new file mode 100644 index 0000000000000000000000000000000000000000..9a45c20473d144152b49ea139e99359a239b4cb8 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarFileEntries.java @@ -0,0 +1,403 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.jar; + +import java.io.IOException; +import java.io.InputStream; +import java.util.*; +import java.util.jar.Attributes; +import java.util.jar.JarInputStream; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public class JarFileEntries implements CentralDirectoryVisitor, Iterable { + + private static final Runnable NO_VALIDATION = () -> { + }; + + private static final String META_INF_PREFIX = "META-INF/"; + + private static final Attributes.Name MULTI_RELEASE = new Attributes.Name("Multi-Release"); + + private static final int BASE_VERSION = 8; + + private static final int RUNTIME_VERSION; + + static { + int version; + try { + Object runtimeVersion = Runtime.class.getMethod("version").invoke(null); + version = (int) runtimeVersion.getClass().getMethod("major").invoke(runtimeVersion); + } + catch (Throwable ex) { + version = BASE_VERSION; + } + RUNTIME_VERSION = version; + } + + private static final long LOCAL_FILE_HEADER_SIZE = 30; + + private static final char SLASH = '/'; + + private static final char NO_SUFFIX = 0; + + protected static final int ENTRY_CACHE_SIZE = 25; + + private final JarFile jarFile; + + private final JarEntryFilter filter; + + private RandomAccessData centralDirectoryData; + + private int size; + + private int[] hashCodes; + + private int[] centralDirectoryOffsets; + + private int[] positions; + + private Boolean multiReleaseJar; + + private JarEntryCertification[] certifications; + + private final Map entriesCache = Collections + .synchronizedMap(new LinkedHashMap(16, 0.75f, true) { + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() >= ENTRY_CACHE_SIZE; + } + + }); + + JarFileEntries(JarFile jarFile, JarEntryFilter filter) { + this.jarFile = jarFile; + this.filter = filter; + if (RUNTIME_VERSION == BASE_VERSION) { + this.multiReleaseJar = false; + } + } + + @Override + public void visitStart(CentralDirectoryEndRecord endRecord, RandomAccessData centralDirectoryData) { + int maxSize = endRecord.getNumberOfRecords(); + this.centralDirectoryData = centralDirectoryData; + this.hashCodes = new int[maxSize]; + this.centralDirectoryOffsets = new int[maxSize]; + this.positions = new int[maxSize]; + } + + @Override + public void visitFileHeader(CentralDirectoryFileHeader fileHeader, int dataOffset) { + AsciiBytes name = applyFilter(fileHeader.getName()); + if (name != null) { + add(name, dataOffset); + } + } + + private void add(AsciiBytes name, int dataOffset) { + this.hashCodes[this.size] = name.hashCode(); + this.centralDirectoryOffsets[this.size] = dataOffset; + this.positions[this.size] = this.size; + this.size++; + } + + @Override + public void visitEnd() { + sort(0, this.size - 1); + int[] positions = this.positions; + this.positions = new int[positions.length]; + for (int i = 0; i < this.size; i++) { + this.positions[positions[i]] = i; + } + } + + int getSize() { + return this.size; + } + + private void sort(int left, int right) { + // Quick sort algorithm, uses hashCodes as the source but sorts all arrays + if (left < right) { + int pivot = this.hashCodes[left + (right - left) / 2]; + int i = left; + int j = right; + while (i <= j) { + while (this.hashCodes[i] < pivot) { + i++; + } + while (this.hashCodes[j] > pivot) { + j--; + } + if (i <= j) { + swap(i, j); + i++; + j--; + } + } + if (left < j) { + sort(left, j); + } + if (right > i) { + sort(i, right); + } + } + } + + private void swap(int i, int j) { + swap(this.hashCodes, i, j); + swap(this.centralDirectoryOffsets, i, j); + swap(this.positions, i, j); + } + + private void swap(int[] array, int i, int j) { + int temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } + + @Override + public Iterator iterator() { + return new EntryIterator(NO_VALIDATION); + } + + Iterator iterator(Runnable validator) { + return new EntryIterator(validator); + } + + boolean containsEntry(CharSequence name) { + return getEntry(name, FileHeader.class, true) != null; + } + + JarEntry getEntry(CharSequence name) { + return getEntry(name, JarEntry.class, true); + } + + InputStream getInputStream(String name) throws IOException { + FileHeader entry = getEntry(name, FileHeader.class, false); + return getInputStream(entry); + } + + InputStream getInputStream(FileHeader entry) throws IOException { + if (entry == null) { + return null; + } + InputStream inputStream = getEntryData(entry).getInputStream(); + if (entry.getMethod() == ZipEntry.DEFLATED) { + inputStream = new ZipInflaterInputStream(inputStream, (int) entry.getSize()); + } + return inputStream; + } + + RandomAccessData getEntryData(String name) throws IOException { + FileHeader entry = getEntry(name, FileHeader.class, false); + if (entry == null) { + return null; + } + return getEntryData(entry); + } + + private RandomAccessData getEntryData(FileHeader entry) throws IOException { + // aspectjrt-1.7.4.jar has a different ext bytes length in the + // local directory to the central directory. We need to re-read + // here to skip them + RandomAccessData data = this.jarFile.getData(); + byte[] localHeader = data.read(entry.getLocalHeaderOffset(), LOCAL_FILE_HEADER_SIZE); + long nameLength = Bytes.littleEndianValue(localHeader, 26, 2); + long extraLength = Bytes.littleEndianValue(localHeader, 28, 2); + return data.getSubsection(entry.getLocalHeaderOffset() + LOCAL_FILE_HEADER_SIZE + nameLength + extraLength, + entry.getCompressedSize()); + } + + private T getEntry(CharSequence name, Class type, boolean cacheEntry) { + T entry = doGetEntry(name, type, cacheEntry, null); + if (!isMetaInfEntry(name) && isMultiReleaseJar()) { + int version = RUNTIME_VERSION; + AsciiBytes nameAlias = (entry instanceof JarEntry) ? ((JarEntry) entry).getAsciiBytesName() + : new AsciiBytes(name.toString()); + while (version > BASE_VERSION) { + T versionedEntry = doGetEntry("META-INF/versions/" + version + "/" + name, type, cacheEntry, nameAlias); + if (versionedEntry != null) { + return versionedEntry; + } + version--; + } + } + return entry; + } + + private boolean isMetaInfEntry(CharSequence name) { + return name.toString().startsWith(META_INF_PREFIX); + } + + private boolean isMultiReleaseJar() { + Boolean multiRelease = this.multiReleaseJar; + if (multiRelease != null) { + return multiRelease; + } + try { + Manifest manifest = this.jarFile.getManifest(); + if (manifest == null) { + multiRelease = false; + } else { + Attributes attributes = manifest.getMainAttributes(); + multiRelease = attributes.containsKey(MULTI_RELEASE); + } + } catch (IOException ex) { + multiRelease = false; + } + this.multiReleaseJar = multiRelease; + return multiRelease; + } + + private T doGetEntry(CharSequence name, Class type, boolean cacheEntry, + AsciiBytes nameAlias) { + int hashCode = AsciiBytes.hashCode(name); + T entry = getEntry(hashCode, name, NO_SUFFIX, type, cacheEntry, nameAlias); + if (entry == null) { + hashCode = AsciiBytes.hashCode(hashCode, SLASH); + entry = getEntry(hashCode, name, SLASH, type, cacheEntry, nameAlias); + } + return entry; + } + + private T getEntry(int hashCode, CharSequence name, char suffix, Class type, + boolean cacheEntry, AsciiBytes nameAlias) { + int index = getFirstIndex(hashCode); + while (index >= 0 && index < this.size && this.hashCodes[index] == hashCode) { + T entry = getEntry(index, type, cacheEntry, nameAlias); + if (entry.hasName(name, suffix)) { + return entry; + } + index++; + } + return null; + } + + @SuppressWarnings("unchecked") + private T getEntry(int index, Class type, boolean cacheEntry, AsciiBytes nameAlias) { + try { + FileHeader cached = this.entriesCache.get(index); + FileHeader entry = (cached != null) ? cached : CentralDirectoryFileHeader + .fromRandomAccessData(this.centralDirectoryData, this.centralDirectoryOffsets[index], this.filter); + if (CentralDirectoryFileHeader.class.equals(entry.getClass()) && type.equals(JarEntry.class)) { + entry = new JarEntry(this.jarFile, index, (CentralDirectoryFileHeader) entry, nameAlias); + } + if (cacheEntry && cached != entry) { + this.entriesCache.put(index, entry); + } + return (T) entry; + } catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + + private int getFirstIndex(int hashCode) { + int index = Arrays.binarySearch(this.hashCodes, 0, this.size, hashCode); + if (index < 0) { + return -1; + } + while (index > 0 && this.hashCodes[index - 1] == hashCode) { + index--; + } + return index; + } + + void clearCache() { + this.entriesCache.clear(); + } + + private AsciiBytes applyFilter(AsciiBytes name) { + return (this.filter != null) ? this.filter.apply(name) : name; + } + + JarEntryCertification getCertification(JarEntry entry) throws IOException { + JarEntryCertification[] certifications = this.certifications; + if (certifications == null) { + certifications = new JarEntryCertification[this.size]; + // We fallback to use JarInputStream to obtain the certs. This isn't that + // fast, but hopefully doesn't happen too often. + try (JarInputStream certifiedJarStream = new JarInputStream(this.jarFile.getData().getInputStream())) { + java.util.jar.JarEntry certifiedEntry = null; + while ((certifiedEntry = certifiedJarStream.getNextJarEntry()) != null) { + // Entry must be closed to trigger a read and set entry certificates + certifiedJarStream.closeEntry(); + int index = getEntryIndex(certifiedEntry.getName()); + if (index != -1) { + certifications[index] = JarEntryCertification.from(certifiedEntry); + } + } + } + this.certifications = certifications; + } + JarEntryCertification certification = certifications[entry.getIndex()]; + return (certification != null) ? certification : JarEntryCertification.NONE; + } + + private int getEntryIndex(CharSequence name) { + int hashCode = AsciiBytes.hashCode(name); + int index = getFirstIndex(hashCode); + while (index >= 0 && index < this.size && this.hashCodes[index] == hashCode) { + FileHeader candidate = getEntry(index, FileHeader.class, false, null); + if (candidate.hasName(name, NO_SUFFIX)) { + return index; + } + index++; + } + return -1; + } + + /** + * Iterator for contained entries. + */ + private final class EntryIterator implements Iterator { + + private final Runnable validator; + + private int index = 0; + + private EntryIterator(Runnable validator) { + this.validator = validator; + validator.run(); + } + + @Override + public boolean hasNext() { + this.validator.run(); + return this.index < JarFileEntries.this.size; + } + + @Override + public JarEntry next() { + this.validator.run(); + if (!hasNext()) { + throw new NoSuchElementException(); + } + int entryIndex = JarFileEntries.this.positions[this.index]; + this.index++; + return getEntry(entryIndex, JarEntry.class, false, null); + } + + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarFileWrapper.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarFileWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..212f4c491e3f4a3ebe12b2ba865d95d5a5b46344 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarFileWrapper.java @@ -0,0 +1,173 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.jar; + + +import com.gitee.starblues.loader.utils.IOUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.Permission; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.jar.JarEntry; +import java.util.jar.Manifest; +import java.util.stream.Stream; +import java.util.zip.ZipEntry; +import com.gitee.starblues.loader.utils.ObjectUtils; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public class JarFileWrapper extends AbstractJarFile { + + private final String parentName; + + private final JarFile parent; + + private final Map> inputStreamCache; + + private final AtomicBoolean canClosed = new AtomicBoolean(false); + + JarFileWrapper(JarFile parent) throws IOException { + super(parent.getRootJarFile().getFile()); + this.parent = parent; + this.parentName = UUID.randomUUID().toString() + parent.getName(); + this.inputStreamCache = new ConcurrentHashMap<>(); + } + + @Override + URL getUrl() throws MalformedURLException { + return this.parent.getUrl(); + } + + @Override + JarFileType getType() { + return this.parent.getType(); + } + + @Override + Permission getPermission() { + return this.parent.getPermission(); + } + + @Override + public Manifest getManifest() throws IOException { + return this.parent.getManifest(); + } + + @Override + public Enumeration entries() { + return this.parent.entries(); + } + + @Override + public Stream stream() { + return this.parent.stream(); + } + + @Override + public JarEntry getJarEntry(String name) { + return this.parent.getJarEntry(name); + } + + @Override + public ZipEntry getEntry(String name) { + return this.parent.getEntry(name); + } + + @Override + InputStream getInputStream() throws IOException { + InputStream inputStream = this.parent.getInputStream(); + addInputStream(parentName, inputStream); + return inputStream; + } + + @Override + public synchronized InputStream getInputStream(ZipEntry ze) throws IOException { + InputStream inputStream = this.parent.getInputStream(ze); + addInputStream(ze.getName(), inputStream); + return inputStream; + } + + @Override + public String getComment() { + return this.parent.getComment(); + } + + @Override + public int size() { + return this.parent.size(); + } + + @Override + public String toString() { + return this.parent.toString(); + } + + @Override + public String getName() { + return this.parent.getName(); + } + + @Override + public void close() throws IOException { + super.close(); + if(canClosed.get()){ + for (List inputStreams : inputStreamCache.values()) { + if(ObjectUtils.isEmpty(inputStreams)){ + continue; + } + for (InputStream inputStream : inputStreams) { + if(inputStream == null){ + continue; + } + IOUtils.closeQuietly(inputStream); + } + } + parent.close(); + } + } + + public void canClosed(){ + canClosed.set(true); + } + + private void addInputStream(String name, InputStream inputStream){ + if(inputStream != null){ + List inputStreams = inputStreamCache.computeIfAbsent(name, k -> new ArrayList<>()); + inputStreams.add(inputStream); + } + } + + static JarFile unwrap(java.util.jar.JarFile jarFile) { + if (jarFile instanceof JarFile) { + return (JarFile) jarFile; + } + if (jarFile instanceof JarFileWrapper) { + return unwrap(((JarFileWrapper) jarFile).parent); + } + throw new IllegalStateException("Not a JarFile or Wrapper"); + } + +} + diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarURLConnection.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarURLConnection.java new file mode 100644 index 0000000000000000000000000000000000000000..b2aec95dc1b7f6e43adfbe82fd47e4f1d0b3a735 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarURLConnection.java @@ -0,0 +1,392 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.jar; + +import com.gitee.starblues.loader.PluginResourceStorage; +import com.gitee.starblues.loader.utils.IOUtils; + +import java.io.*; +import java.net.*; +import java.security.Permission; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public class JarURLConnection extends java.net.JarURLConnection { + + private static ThreadLocal useFastExceptions = new ThreadLocal<>(); + + private static final FileNotFoundException FILE_NOT_FOUND_EXCEPTION = new FileNotFoundException( + "Jar file or entry not found"); + + private static final IllegalStateException NOT_FOUND_CONNECTION_EXCEPTION = new IllegalStateException( + FILE_NOT_FOUND_EXCEPTION); + + private static final String SEPARATOR = "!/"; + + private static final URL EMPTY_JAR_URL; + + static { + try { + EMPTY_JAR_URL = new URL("jar:", null, 0, "file:!/", new URLStreamHandler() { + @Override + protected URLConnection openConnection(URL u) throws IOException { + // Stub URLStreamHandler to prevent the wrong JAR Handler from being + // Instantiated and cached. + return null; + } + }); + } catch (MalformedURLException ex) { + throw new IllegalStateException(ex); + } + } + + private static final JarEntryName EMPTY_JAR_ENTRY_NAME = new JarEntryName(new StringSequence("")); + + private static final JarURLConnection NOT_FOUND_CONNECTION = JarURLConnection.notFound(); + + private final AbstractJarFile jarFile; + + private Permission permission; + + private URL jarFileUrl; + + private final JarEntryName jarEntryName; + + private java.util.jar.JarEntry jarEntry; + + private JarURLConnection(URL url, AbstractJarFile jarFile, JarEntryName jarEntryName) throws IOException { + // What we pass to super is ultimately ignored + super(EMPTY_JAR_URL); + this.url = url; + this.jarFile = jarFile; + this.jarEntryName = jarEntryName; + } + + @Override + public void connect() throws IOException { + if (this.jarFile == null) { + throw FILE_NOT_FOUND_EXCEPTION; + } + if (!this.jarEntryName.isEmpty() && this.jarEntry == null) { + this.jarEntry = this.jarFile.getJarEntry(getEntryName()); + if (this.jarEntry == null) { + throwFileNotFound(this.jarEntryName, this.jarFile); + } + } + this.connected = true; + } + + @Override + public java.util.jar.JarFile getJarFile() throws IOException { + connect(); + return this.jarFile; + } + + @Override + public URL getJarFileURL() { + if (this.jarFile == null) { + throw NOT_FOUND_CONNECTION_EXCEPTION; + } + if (this.jarFileUrl == null) { + this.jarFileUrl = buildJarFileUrl(); + } + return this.jarFileUrl; + } + + private URL buildJarFileUrl() { + try { + String spec = this.jarFile.getUrl().getFile(); + if (spec.endsWith(SEPARATOR)) { + spec = spec.substring(0, spec.length() - SEPARATOR.length()); + } + if (!spec.contains(SEPARATOR)) { + return new URL(spec); + } + return new URL("jar:" + spec); + } catch (MalformedURLException ex) { + throw new IllegalStateException(ex); + } + } + + @Override + public java.util.jar.JarEntry getJarEntry() throws IOException { + if (this.jarEntryName == null || this.jarEntryName.isEmpty()) { + return null; + } + connect(); + return this.jarEntry; + } + + @Override + public String getEntryName() { + if (this.jarFile == null) { + throw NOT_FOUND_CONNECTION_EXCEPTION; + } + return this.jarEntryName.toString(); + } + + @Override + public synchronized InputStream getInputStream() throws IOException { + if (this.jarFile == null) { + throw FILE_NOT_FOUND_EXCEPTION; + } + if (this.jarEntryName.isEmpty() && this.jarFile.getType() == JarFile.JarFileType.DIRECT) { + throw new IOException("no entry name specified"); + } + connect(); + InputStream inputStream = (this.jarEntryName.isEmpty() ? this.jarFile.getInputStream() + : this.jarFile.getInputStream(this.jarEntry)); + if (inputStream == null) { + throwFileNotFound(this.jarEntryName, this.jarFile); + return null; + } + return inputStream; + } + + private void throwFileNotFound(Object entry, AbstractJarFile jarFile) throws FileNotFoundException { + if (Boolean.TRUE.equals(useFastExceptions.get())) { + throw FILE_NOT_FOUND_EXCEPTION; + } + throw new FileNotFoundException("JAR entry " + entry + " not found in " + jarFile.getName()); + } + + @Override + public int getContentLength() { + long length = getContentLengthLong(); + if (length > Integer.MAX_VALUE) { + return -1; + } + return (int) length; + } + + @Override + public long getContentLengthLong() { + if (this.jarFile == null) { + return -1; + } + try { + if (this.jarEntryName.isEmpty()) { + return this.jarFile.size(); + } + java.util.jar.JarEntry entry = getJarEntry(); + return (entry != null) ? (int) entry.getSize() : -1; + } catch (IOException ex) { + return -1; + } + } + + @Override + public Object getContent() throws IOException { + connect(); + return this.jarEntryName.isEmpty() ? this.jarFile : super.getContent(); + } + + @Override + public String getContentType() { + return (this.jarEntryName != null) ? this.jarEntryName.getContentType() : null; + } + + @Override + public Permission getPermission() throws IOException { + if (this.jarFile == null) { + throw FILE_NOT_FOUND_EXCEPTION; + } + if (this.permission == null) { + this.permission = this.jarFile.getPermission(); + } + return this.permission; + } + + @Override + public long getLastModified() { + if (this.jarFile == null || this.jarEntryName.isEmpty()) { + return 0; + } + try { + java.util.jar.JarEntry entry = getJarEntry(); + return (entry != null) ? entry.getTime() : 0; + } catch (IOException ex) { + return 0; + } + } + + static void setUseFastExceptions(boolean useFastExceptions) { + JarURLConnection.useFastExceptions.set(useFastExceptions); + } + + static JarURLConnection get(URL url, JarFile jarFile) throws IOException { + StringSequence spec = new StringSequence(url.getFile()); + int index = indexOfRootSpec(spec, jarFile.getPathFromRoot()); + if (index == -1) { + return (Boolean.TRUE.equals(useFastExceptions.get()) ? NOT_FOUND_CONNECTION + : new JarURLConnection(url, null, EMPTY_JAR_ENTRY_NAME)); + } + int separator; + while ((separator = spec.indexOf(SEPARATOR, index)) > 0) { + JarEntryName entryName = JarEntryName.get(spec.subSequence(index, separator)); + JarEntry jarEntry = jarFile.getJarEntry(entryName.toCharSequence()); + if (jarEntry == null) { + return JarURLConnection.notFound(jarFile, entryName); + } + jarFile = jarFile.getNestedJarFile(jarEntry); + index = separator + SEPARATOR.length(); + } + JarEntryName jarEntryName = JarEntryName.get(spec, index); + if (Boolean.TRUE.equals(useFastExceptions.get()) && !jarEntryName.isEmpty() + && !jarFile.containsEntry(jarEntryName.toString())) { + return NOT_FOUND_CONNECTION; + } + JarFileWrapper jarFileWrapper = new JarFileWrapper(jarFile); + PluginResourceStorage.addJarFile(jarFileWrapper); + return new JarURLConnection(url, jarFileWrapper, jarEntryName); + } + + private static int indexOfRootSpec(StringSequence file, String pathFromRoot) { + int separatorIndex = file.indexOf(SEPARATOR); + if (separatorIndex < 0 || !file.startsWith(pathFromRoot, separatorIndex)) { + return -1; + } + return separatorIndex + SEPARATOR.length() + pathFromRoot.length(); + } + + private static JarURLConnection notFound() { + try { + return notFound(null, null); + } + catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + + private static JarURLConnection notFound(JarFile jarFile, JarEntryName jarEntryName) throws IOException { + if (Boolean.TRUE.equals(useFastExceptions.get())) { + return NOT_FOUND_CONNECTION; + } + return new JarURLConnection(null, jarFile, jarEntryName); + } + + /** + * A JarEntryName parsed from a URL String. + */ + static class JarEntryName { + + private final StringSequence name; + + private String contentType; + + JarEntryName(StringSequence spec) { + this.name = decode(spec); + } + + private StringSequence decode(StringSequence source) { + if (source.isEmpty() || (source.indexOf('%') < 0)) { + return source; + } + ByteArrayOutputStream bos = null; + try { + bos = new ByteArrayOutputStream(source.length()); + write(source.toString(), bos); + // AsciiBytes is what is used to store the JarEntries so make it symmetric + return new StringSequence(AsciiBytes.toString(bos.toByteArray())); + } finally { + if(bos != null){ + IOUtils.closeQuietly(bos); + } + } + } + + private void write(String source, ByteArrayOutputStream outputStream) { + int length = source.length(); + for (int i = 0; i < length; i++) { + int c = source.charAt(i); + if (c > 127) { + try { + String encoded = URLEncoder.encode(String.valueOf((char) c), "UTF-8"); + write(encoded, outputStream); + } + catch (UnsupportedEncodingException ex) { + throw new IllegalStateException(ex); + } + } + else { + if (c == '%') { + if ((i + 2) >= length) { + throw new IllegalArgumentException( + "Invalid encoded sequence \"" + source.substring(i) + "\""); + } + c = decodeEscapeSequence(source, i); + i += 2; + } + outputStream.write(c); + } + } + } + + private char decodeEscapeSequence(String source, int i) { + int hi = Character.digit(source.charAt(i + 1), 16); + int lo = Character.digit(source.charAt(i + 2), 16); + if (hi == -1 || lo == -1) { + throw new IllegalArgumentException("Invalid encoded sequence \"" + source.substring(i) + "\""); + } + return ((char) ((hi << 4) + lo)); + } + + CharSequence toCharSequence() { + return this.name; + } + + @Override + public String toString() { + return this.name.toString(); + } + + boolean isEmpty() { + return this.name.isEmpty(); + } + + String getContentType() { + if (this.contentType == null) { + this.contentType = deduceContentType(); + } + return this.contentType; + } + + private String deduceContentType() { + // Guess the content type, don't bother with streams as mark is not supported + String type = isEmpty() ? "x-java/jar" : null; + type = (type != null) ? type : guessContentTypeFromName(toString()); + type = (type != null) ? type : "content/unknown"; + return type; + } + + static JarEntryName get(StringSequence spec) { + return get(spec, 0); + } + + static JarEntryName get(StringSequence spec, int beginIndex) { + if (spec.length() <= beginIndex) { + return EMPTY_JAR_ENTRY_NAME; + } + return new JarEntryName(spec.subSequence(beginIndex)); + } + + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/RandomAccessData.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/RandomAccessData.java new file mode 100644 index 0000000000000000000000000000000000000000..52bc2608db71b0794d1152596434ff4c0097af92 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/RandomAccessData.java @@ -0,0 +1,74 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.jar; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public interface RandomAccessData { + + + /** + * Returns an {@link InputStream} that can be used to read the underlying data. The + * caller is responsible close the underlying stream. + * @return a new input stream that can be used to read the underlying data. + * @throws IOException if the stream cannot be opened + */ + InputStream getInputStream() throws IOException; + + /** + * Returns a new {@link RandomAccessData} for a specific subsection of this data. + * @param offset the offset of the subsection + * @param length the length of the subsection + * @return the subsection data + */ + RandomAccessData getSubsection(long offset, long length); + + /** + * Reads all the data and returns it as a byte array. + * @return the data + * @throws IOException if the data cannot be read + */ + byte[] read() throws IOException; + + /** + * Reads the {@code length} bytes of data starting at the given {@code offset}. + * @param offset the offset from which data should be read + * @param length the number of bytes to be read + * @return the data + * @throws IOException if the data cannot be read + * @throws IndexOutOfBoundsException if offset is beyond the end of the file or + * subsection + * @throws EOFException if offset plus length is greater than the length of the file + * or subsection + */ + byte[] read(long offset, long length) throws IOException; + + /** + * Returns the size of the data. + * @return the size + */ + long getSize(); + + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/RandomAccessDataFile.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/RandomAccessDataFile.java new file mode 100644 index 0000000000000000000000000000000000000000..8c97592f57263990c6c104e8884837433dd9d596 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/RandomAccessDataFile.java @@ -0,0 +1,250 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.jar; + +import java.io.*; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public class RandomAccessDataFile implements RandomAccessData{ + + private final FileAccess fileAccess; + + private final long offset; + + private final long length; + + /** + * Create a new {@link RandomAccessDataFile} backed by the specified file. + * @param file the underlying file + * @throws IllegalArgumentException if the file is null or does not exist + */ + public RandomAccessDataFile(File file) { + if (file == null) { + throw new IllegalArgumentException("File must not be null"); + } + this.fileAccess = new FileAccess(file); + this.offset = 0L; + this.length = file.length(); + } + + /** + * Private constructor used to create a {@link #getSubsection(long, long) subsection}. + * @param fileAccess provides access to the underlying file + * @param offset the offset of the section + * @param length the length of the section + */ + private RandomAccessDataFile(FileAccess fileAccess, long offset, long length) { + this.fileAccess = fileAccess; + this.offset = offset; + this.length = length; + } + + /** + * Returns the underlying File. + * @return the underlying file + */ + public File getFile() { + return this.fileAccess.file; + } + + @Override + public InputStream getInputStream() throws IOException { + return new DataInputStream(); + } + + @Override + public RandomAccessData getSubsection(long offset, long length) { + if (offset < 0 || length < 0 || offset + length > this.length) { + throw new IndexOutOfBoundsException(); + } + return new RandomAccessDataFile(this.fileAccess, this.offset + offset, length); + } + + @Override + public byte[] read() throws IOException { + return read(0, this.length); + } + + @Override + public byte[] read(long offset, long length) throws IOException { + if (offset > this.length) { + throw new IndexOutOfBoundsException(); + } + if (offset + length > this.length) { + throw new EOFException(); + } + byte[] bytes = new byte[(int) length]; + read(bytes, offset, 0, bytes.length); + return bytes; + } + + private int readByte(long position) throws IOException { + if (position >= this.length) { + return -1; + } + return this.fileAccess.readByte(this.offset + position); + } + + private int read(byte[] bytes, long position, int offset, int length) throws IOException { + if (position > this.length) { + return -1; + } + return this.fileAccess.read(bytes, this.offset + position, offset, length); + } + + @Override + public long getSize() { + return this.length; + } + + public void close() throws IOException { + this.fileAccess.close(); + } + + /** + * {@link InputStream} implementation for the {@link RandomAccessDataFile}. + */ + private class DataInputStream extends InputStream { + + private int position; + + @Override + public int read() throws IOException { + int read = RandomAccessDataFile.this.readByte(this.position); + if (read > -1) { + moveOn(1); + } + return read; + } + + @Override + public int read(byte[] b) throws IOException { + return read(b, 0, (b != null) ? b.length : 0); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (b == null) { + throw new NullPointerException("Bytes must not be null"); + } + return doRead(b, off, len); + } + + /** + * Perform the actual read. + * @param b the bytes to read or {@code null} when reading a single byte + * @param off the offset of the byte array + * @param len the length of data to read + * @return the number of bytes read into {@code b} or the actual read byte if + * {@code b} is {@code null}. Returns -1 when the end of the stream is reached + * @throws IOException in case of I/O errors + */ + int doRead(byte[] b, int off, int len) throws IOException { + if (len == 0) { + return 0; + } + int cappedLen = cap(len); + if (cappedLen <= 0) { + return -1; + } + return (int) moveOn(RandomAccessDataFile.this.read(b, this.position, off, cappedLen)); + } + + @Override + public long skip(long n) throws IOException { + return (n <= 0) ? 0 : moveOn(cap(n)); + } + + /** + * Cap the specified value such that it cannot exceed the number of bytes + * remaining. + * @param n the value to cap + * @return the capped value + */ + private int cap(long n) { + return (int) Math.min(RandomAccessDataFile.this.length - this.position, n); + } + + /** + * Move the stream position forwards the specified amount. + * @param amount the amount to move + * @return the amount moved + */ + private long moveOn(int amount) { + this.position += amount; + return amount; + } + + } + + private static final class FileAccess { + + private final Object monitor = new Object(); + + private final File file; + + private RandomAccessFile randomAccessFile; + + private FileAccess(File file) { + this.file = file; + openIfNecessary(); + } + + private int read(byte[] bytes, long position, int offset, int length) throws IOException { + synchronized (this.monitor) { + openIfNecessary(); + this.randomAccessFile.seek(position); + return this.randomAccessFile.read(bytes, offset, length); + } + } + + private void openIfNecessary() { + if (this.randomAccessFile == null) { + try { + this.randomAccessFile = new RandomAccessFile(this.file, "r"); + } catch (FileNotFoundException ex) { + throw new IllegalArgumentException( + String.format("File %s must exist", this.file.getAbsolutePath())); + } + } + } + + private void close() throws IOException { + synchronized (this.monitor) { + if (this.randomAccessFile != null) { + this.randomAccessFile.close(); + this.randomAccessFile = null; + } + } + } + + private int readByte(long position) throws IOException { + synchronized (this.monitor) { + openIfNecessary(); + this.randomAccessFile.seek(position); + return this.randomAccessFile.read(); + } + } + + } + + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/StringSequence.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/StringSequence.java new file mode 100644 index 0000000000000000000000000000000000000000..0cc1e8128fc8e062f53fae367b5404aeb1b2f0bd --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/StringSequence.java @@ -0,0 +1,156 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.jar; + +import java.util.Objects; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public class StringSequence implements CharSequence { + + private final String source; + + private final int start; + + private final int end; + + private int hash; + + StringSequence(String source) { + this(source, 0, (source != null) ? source.length() : -1); + } + + StringSequence(String source, int start, int end) { + Objects.requireNonNull(source, "Source must not be null"); + if (start < 0) { + throw new StringIndexOutOfBoundsException(start); + } + if (end > source.length()) { + throw new StringIndexOutOfBoundsException(end); + } + this.source = source; + this.start = start; + this.end = end; + } + + StringSequence subSequence(int start) { + return subSequence(start, length()); + } + + @Override + public StringSequence subSequence(int start, int end) { + int subSequenceStart = this.start + start; + int subSequenceEnd = this.start + end; + if (subSequenceStart > this.end) { + throw new StringIndexOutOfBoundsException(start); + } + if (subSequenceEnd > this.end) { + throw new StringIndexOutOfBoundsException(end); + } + if (start == 0 && subSequenceEnd == this.end) { + return this; + } + return new StringSequence(this.source, subSequenceStart, subSequenceEnd); + } + + /** + * Returns {@code true} if the sequence is empty. Public to be compatible with JDK 15. + * @return {@code true} if {@link #length()} is {@code 0}, otherwise {@code false} + */ + public boolean isEmpty() { + return length() == 0; + } + + @Override + public int length() { + return this.end - this.start; + } + + @Override + public char charAt(int index) { + return this.source.charAt(this.start + index); + } + + int indexOf(char ch) { + return this.source.indexOf(ch, this.start) - this.start; + } + + int indexOf(String str) { + return this.source.indexOf(str, this.start) - this.start; + } + + int indexOf(String str, int fromIndex) { + return this.source.indexOf(str, this.start + fromIndex) - this.start; + } + + boolean startsWith(String prefix) { + return startsWith(prefix, 0); + } + + boolean startsWith(String prefix, int offset) { + int prefixLength = prefix.length(); + int length = length(); + if (length - prefixLength - offset < 0) { + return false; + } + return this.source.startsWith(prefix, this.start + offset); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof CharSequence)) { + return false; + } + CharSequence other = (CharSequence) obj; + int n = length(); + if (n != other.length()) { + return false; + } + int i = 0; + while (n-- != 0) { + if (charAt(i) != other.charAt(i)) { + return false; + } + i++; + } + return true; + } + + @Override + public int hashCode() { + int hash = this.hash; + if (hash == 0 && length() > 0) { + for (int i = this.start; i < this.end; i++) { + hash = 31 * hash + this.source.charAt(i); + } + this.hash = hash; + } + return hash; + } + + @Override + public String toString() { + return this.source.substring(this.start, this.end); + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/ZipInflaterInputStream.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/ZipInflaterInputStream.java new file mode 100644 index 0000000000000000000000000000000000000000..05c8bf4127d6d49f2b59e56b4830bc22fabfd5ca --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/ZipInflaterInputStream.java @@ -0,0 +1,87 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.jar; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; + +/** + * copy from spring-boot-loader + * @author starBlues + * @version 3.0.0 + */ +public class ZipInflaterInputStream extends InflaterInputStream { + + private int available; + + private boolean extraBytesWritten; + + ZipInflaterInputStream(InputStream inputStream, int size) { + super(inputStream, new Inflater(true), getInflaterBufferSize(size)); + this.available = size; + } + + @Override + public int available() throws IOException { + if (this.available < 0) { + return super.available(); + } + return this.available; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + int result = super.read(b, off, len); + if (result != -1) { + this.available -= result; + } + return result; + } + + @Override + public void close() throws IOException { + super.close(); + this.inf.end(); + } + + @Override + protected void fill() throws IOException { + try { + super.fill(); + } catch (EOFException ex) { + if (this.extraBytesWritten) { + throw ex; + } + this.len = 1; + this.buf[0] = 0x0; + this.extraBytesWritten = true; + this.inf.setInput(this.buf, 0, this.len); + } + } + + private static int getInflaterBufferSize(long size) { + // inflater likes some space + size += 2; + size = (size > 65536) ? 8192 : size; + size = (size <= 0) ? 4096 : size; + return (int) size; + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/AbstractLauncher.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/AbstractLauncher.java new file mode 100644 index 0000000000000000000000000000000000000000..573ce907e2dfd0a0deb2d1b4c91527c66e8cb91f --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/AbstractLauncher.java @@ -0,0 +1,55 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.launcher; + +/** + * 抽象的启动引导者 + * @author starBlues + * @version 3.0.0 + */ +public abstract class AbstractLauncher implements Launcher { + + @Override + public R run(String... args) throws Exception { + ClassLoader classLoader = createClassLoader(args); + Thread thread = Thread.currentThread(); + ClassLoader oldClassLoader = thread.getContextClassLoader(); + try { + thread.setContextClassLoader(classLoader); + return launch(classLoader, args); + } finally { + thread.setContextClassLoader(oldClassLoader); + } + } + + /** + * 创建classloader + * @return ClassLoader + * @throws Exception 创建异常 + */ + protected abstract ClassLoader createClassLoader(String... args) throws Exception; + + /** + * 子类实现具体的启动方法 + * @param classLoader 当前的 ClassLoader + * @param args 启动参数 + * @return 启动返回值 + * @throws Exception 启动异常 + */ + protected abstract R launch(ClassLoader classLoader, String... args) throws Exception; + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/Launcher.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/Launcher.java new file mode 100644 index 0000000000000000000000000000000000000000..cee4f124dcf06a3ed5167727a83f3eca6e94a2d3 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/Launcher.java @@ -0,0 +1,34 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.launcher; + +/** + * 启动引导器 + * @author starBlues + * @version 3.0.0 + */ +public interface Launcher { + + /** + * 启动运行 + * @param args 启动传入的参数 + * @return 启动后的返回值 + * @throws Exception 启动异常 + */ + R run(String... args) throws Exception; + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/MainJarProgramLauncher.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/MainJarProgramLauncher.java new file mode 100644 index 0000000000000000000000000000000000000000..f9bd4e95fc6bd182ac14c65796e5e63899372ea4 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/MainJarProgramLauncher.java @@ -0,0 +1,102 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.launcher; + +import com.gitee.starblues.loader.archive.Archive; +import com.gitee.starblues.loader.archive.ExplodedArchive; +import com.gitee.starblues.loader.archive.JarFileArchive; +import com.gitee.starblues.loader.classloader.GenericClassLoader; +import com.gitee.starblues.loader.classloader.resource.loader.JarResourceLoader; +import com.gitee.starblues.loader.classloader.resource.storage.ResourceStorage; +import com.gitee.starblues.loader.launcher.runner.MethodRunner; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.Iterator; +import java.util.Objects; + +/** + * 主程序jar in jar 模式启动者 + * @author starBlues + * @version 3.0.0 + */ +public class MainJarProgramLauncher extends MainProgramLauncher{ + + private static final String PROD_CLASSES_PATH = "classes/"; + private static final String PROD_CLASSES_URL_SIGN = "/classes!/"; + + private static final String PROD_LIB_PATH = "lib/"; + + private final static Archive.EntryFilter ENTRY_FILTER = (entry)->{ + String name = entry.getName(); + return name.startsWith(PROD_CLASSES_PATH) || name.startsWith(PROD_LIB_PATH); + }; + + private final static Archive.EntryFilter INCLUDE_FILTER = (entry) -> { + if (entry.isDirectory()) { + return entry.getName().equals(PROD_CLASSES_PATH); + } + return entry.getName().startsWith(PROD_LIB_PATH); + }; + + private final File rootJarFile; + + public MainJarProgramLauncher(MethodRunner methodRunner, File rootJarFile) { + super(methodRunner); + this.rootJarFile = Objects.requireNonNull(rootJarFile, "参数 rootJarFile 不能为空"); + } + + @Override + protected void addResource(GenericClassLoader classLoader) throws Exception { + super.addResource(classLoader); + Archive archive = getArchive(); + Iterator archiveIterator = archive.getNestedArchives(ENTRY_FILTER, INCLUDE_FILTER); + addLibResource(archiveIterator, classLoader); + } + + private Archive getArchive() throws IOException { + return (rootJarFile.isDirectory() ? new ExplodedArchive(rootJarFile) : new JarFileArchive(rootJarFile)); + } + + private void addLibResource(Iterator archives, GenericClassLoader classLoader) throws Exception { + while (archives.hasNext()){ + Archive archive = archives.next(); + URL url = archive.getUrl(); + String path = url.getPath(); + if(path.contains(PROD_CLASSES_URL_SIGN)){ + classLoader.addResource(new MainJarResourceLoader(url)); + } else { + classLoader.addResource(new JarResourceLoader(url)); + } + } + } + + private static class MainJarResourceLoader extends JarResourceLoader { + + public MainJarResourceLoader(URL url) throws Exception { + super(url); + } + + @Override + protected String resolveName(String name) { + return name.replace(PROD_CLASSES_PATH, ""); + } + } + + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/MainProgramLauncher.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/MainProgramLauncher.java new file mode 100644 index 0000000000000000000000000000000000000000..3c9a1bda6aae04e4a4d61ebf0e7bacaf5c6e21cd --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/MainProgramLauncher.java @@ -0,0 +1,84 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.launcher; + +import com.gitee.starblues.loader.classloader.GenericClassLoader; +import com.gitee.starblues.loader.classloader.resource.loader.ResourceLoaderFactory; +import com.gitee.starblues.loader.launcher.runner.MethodRunner; +import com.gitee.starblues.loader.utils.ObjectUtils; + + +import java.lang.management.ManagementFactory; +import java.net.URL; +import java.net.URLClassLoader; + +/** + * 主程序启动者 + * @author starBlues + * @version 3.0.0 + */ +public class MainProgramLauncher extends AbstractLauncher{ + + public static final String MAIN_CLASS_LOADER_NAME = "MainProgramLauncherClassLoader"; + + private final MethodRunner methodRunner; + + public MainProgramLauncher(MethodRunner methodRunner) { + this.methodRunner = methodRunner; + } + + @Override + protected ClassLoader createClassLoader(String... args) throws Exception { + GenericClassLoader classLoader = new GenericClassLoader(MAIN_CLASS_LOADER_NAME, getParentClassLoader(), + getResourceLoaderFactory()); + addResource(classLoader); + return classLoader; + } + + @Override + protected ClassLoader launch(ClassLoader classLoader, String... args) throws Exception { + methodRunner.run(classLoader); + return classLoader; + } + + protected ResourceLoaderFactory getResourceLoaderFactory(String... args){ + return ResourceLoaderFactoryGetter.get(MAIN_CLASS_LOADER_NAME, args); + } + + protected ClassLoader getParentClassLoader(){ + return MainProgramLauncher.class.getClassLoader(); + } + + protected void addResource(GenericClassLoader classLoader) throws Exception{ + String classPath = ManagementFactory.getRuntimeMXBean().getClassPath(); + if(!ObjectUtils.isEmpty(classPath)){ + String[] classPathStr = classPath.split(";"); + for (String path : classPathStr) { + classLoader.addResource(path); + } + } + ClassLoader sourceClassLoader = Thread.currentThread().getContextClassLoader(); + if(sourceClassLoader instanceof URLClassLoader){ + URLClassLoader urlClassLoader = (URLClassLoader) sourceClassLoader; + final URL[] urLs = urlClassLoader.getURLs(); + for (URL url : urLs) { + classLoader.addResource(url); + } + } + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/ResourceLoaderFactoryGetter.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/ResourceLoaderFactoryGetter.java new file mode 100644 index 0000000000000000000000000000000000000000..61dbbd94883a0fdd3b179c87493558acf95fa05b --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/ResourceLoaderFactoryGetter.java @@ -0,0 +1,93 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.launcher; + +import com.gitee.starblues.loader.classloader.resource.loader.DefaultResourceLoaderFactory; +import com.gitee.starblues.loader.classloader.resource.loader.ResourceLoaderFactory; +import com.gitee.starblues.loader.classloader.resource.storage.*; + +import java.net.URL; +import java.util.Objects; + +/** + * 获取ResourceLoaderFactory + * + * @author starBlues + * @version 3.0.0 + */ +public class ResourceLoaderFactoryGetter { + + private static final String PARAMS_KEY = "--resource.store.mode"; + + + /** + * 资源模式--缓存隔离模式 + */ + private static final String RESOURCE_MODE_CACHE_ISOLATION = "cache-isolation"; + + + /** + * 资源模式--缓存共享模式 + */ + private static final String RESOURCE_MODE_CACHE_SHARE = "cache-share"; + + + /** + * 资源模式--不缓存模式 + */ + private static final String RESOURCE_MODE_NO_CACHE = "no-cache"; + + private static volatile String resourceMode; + + + static ResourceLoaderFactory get(String classLoaderName, String... args){ + if(resourceMode == null){ + synchronized (ResourceLoaderFactory.class){ + if(resourceMode == null){ + resourceMode = parseArg(args); + } + } + } + return new DefaultResourceLoaderFactory(classLoaderName); + } + + private static String parseArg(String... args){ + for (String arg : args) { + if(arg.startsWith(PARAMS_KEY)){ + String[] split = arg.split("="); + if(split.length != 2){ + return null; + } + return split[1]; + } + } + return null; + } + + public static SameRootResourceStorage getResourceStorage(String key, URL baseUrl){ + SameRootResourceStorage resourceStorage = null; + if(Objects.equals(resourceMode, RESOURCE_MODE_NO_CACHE)){ + resourceStorage = new DefaultResourceStorage(baseUrl); + } else if(Objects.equals(resourceMode, RESOURCE_MODE_CACHE_SHARE)){ + resourceStorage = new ShareResourceStorage(key, baseUrl); + } else { + resourceStorage = new CacheResourceStorage(baseUrl); + } + return resourceStorage; + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/SpringBootstrap.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/SpringBootstrap.java new file mode 100644 index 0000000000000000000000000000000000000000..2a346604420d6c84c784fca5506f0c31e2f9ee7e --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/SpringBootstrap.java @@ -0,0 +1,33 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.launcher; + +/** + * 主程序实现该接口引导启动SpringBoot + * @author starBlues + * @version 3.0.0 + */ +public interface SpringBootstrap { + + /** + * 启动 + * @param args 启动参数 + * @throws Exception 启动异常 + */ + void run(String[] args) throws Exception; + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/SpringMainBootstrap.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/SpringMainBootstrap.java new file mode 100644 index 0000000000000000000000000000000000000000..19db6ba11ad0fe97b3075d11bf52e4172c3b6a2f --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/SpringMainBootstrap.java @@ -0,0 +1,93 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.launcher; + +import com.gitee.starblues.loader.jar.JarFile; +import com.gitee.starblues.loader.launcher.runner.MainMethodRunner; +import com.gitee.starblues.loader.launcher.runner.MethodRunner; + +import java.io.File; +import java.util.Objects; +import java.util.concurrent.CountDownLatch; + +/** + * 主程序引导器 + * @author starBlues + * @version 3.0.0 + */ +public class SpringMainBootstrap { + + static final String MAIN_RUN_METHOD = "main"; + static final String SPRING_BOOTSTRAP_RUN_METHOD = "run"; + + private static final CountDownLatch COUNT_DOWN_LATCH = new CountDownLatch(1); + + private static SpringBootstrap springBootstrap; + + + public static void launch(Class bootstrapClass, String[] args) { + try { + SpringBootstrap springBootstrap = bootstrapClass.getConstructor().newInstance(); + launch(springBootstrap, args); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void launch(SpringBootstrap springBootstrap, String[] args) { + SpringMainBootstrap.springBootstrap = Objects.requireNonNull(springBootstrap, "springBootBootstrap 不能为空"); + MainMethodRunner mainMethodRunner = new MainMethodRunner(SpringMainBootstrap.class.getName(), + MAIN_RUN_METHOD, args); + JarFile.registerUrlProtocolHandler(); + Thread launchThread = new Thread(new Runner(mainMethodRunner)); + launchThread.start(); + try { + COUNT_DOWN_LATCH.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private static class Runner implements Runnable{ + + private final MethodRunner methodRunner; + + public Runner(MethodRunner methodRunner) { + this.methodRunner = methodRunner; + } + + @Override + public void run() { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + try { + methodRunner.run(contextClassLoader); + } catch (Exception e) { + e.printStackTrace(); + } finally { + COUNT_DOWN_LATCH.countDown(); + } + } + } + + private static void main(String[] args) throws Exception { + Objects.requireNonNull(springBootstrap, "springBootBootstrap 不能为空"); + MethodRunner run = new MethodRunner(springBootstrap.getClass().getName(), SPRING_BOOTSTRAP_RUN_METHOD, args); + Launcher launcher = new MainProgramLauncher(run); + launcher.run(args); + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/SpringMainProdBootstrap.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/SpringMainProdBootstrap.java new file mode 100644 index 0000000000000000000000000000000000000000..5e79f5af1c80f5171010394fcc7aecc458920003 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/SpringMainProdBootstrap.java @@ -0,0 +1,78 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.launcher; + +import com.gitee.starblues.loader.jar.JarFile; +import com.gitee.starblues.loader.launcher.runner.MethodRunner; +import com.gitee.starblues.loader.utils.ObjectUtils; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.CodeSource; +import java.security.ProtectionDomain; +import java.util.jar.Manifest; + +/** + * 主程序生成环境启动引导器 + * @author starBlues + * @version 3.0.0 + */ +public class SpringMainProdBootstrap { + + private static final String START_CLASS = "Start-Class"; + + public static void main(String[] args) throws Exception { + JarFile.registerUrlProtocolHandler(); + new SpringMainProdBootstrap().run(args); + } + + private void run(String[] args) throws Exception{ + File rootJarFile = getRootJarFile(); + String startClass = null; + try (JarFile jarFile = new JarFile(rootJarFile)){ + Manifest manifest = jarFile.getManifest(); + IllegalStateException exception = new IllegalStateException("当前启动包非法包!"); + if(manifest == null || manifest.getMainAttributes() == null){ + throw exception; + } + startClass = manifest.getMainAttributes().getValue(START_CLASS); + if (ObjectUtils.isEmpty(startClass)) { + throw exception; + } + } + MethodRunner methodRunner = new MethodRunner(startClass, SpringMainBootstrap.SPRING_BOOTSTRAP_RUN_METHOD, args); + Launcher launcher = new MainJarProgramLauncher(methodRunner, rootJarFile); + launcher.run(args); + } + + private File getRootJarFile() throws URISyntaxException { + ProtectionDomain protectionDomain = SpringMainBootstrap.class.getProtectionDomain(); + CodeSource codeSource = protectionDomain.getCodeSource(); + URI location = (codeSource != null) ? codeSource.getLocation().toURI() : null; + String path = (location != null) ? location.getSchemeSpecificPart() : null; + if (path == null) { + throw new IllegalStateException("Unable to determine code source archive"); + } + File root = new File(path); + if (!root.exists()) { + throw new IllegalStateException("Unable to determine code source archive from " + root); + } + return root; + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/runner/MainMethodRunner.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/runner/MainMethodRunner.java new file mode 100644 index 0000000000000000000000000000000000000000..2695ac3fcde070748749e21057c20e58ed4bacce --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/runner/MainMethodRunner.java @@ -0,0 +1,34 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.launcher.runner; + +/** + * 主程序方法启动者 + * @author starBlues + * @version 3.0.0 + */ +public class MainMethodRunner extends MethodRunner{ + + public MainMethodRunner(String mainClass, String mainRunMethod, String[] args) { + super(mainClass, mainRunMethod, args); + } + + @Override + protected Object getInstance(Class mainClass) throws Exception { + return null; + } +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/runner/MethodRunner.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/runner/MethodRunner.java new file mode 100644 index 0000000000000000000000000000000000000000..06e4adbbb2d1164759b18f2fa03b83bacb3dc605 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/runner/MethodRunner.java @@ -0,0 +1,114 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.launcher.runner; + +import com.gitee.starblues.loader.utils.CompareClassTypeUtils; +import com.gitee.starblues.loader.utils.ObjectUtils; + +import java.lang.reflect.Method; +import java.util.Objects; + +/** + * 反射运行方法 + * @author starBlues + * @version 3.0.0 + */ +public class MethodRunner { + + protected final String className; + protected final String runMethodName; + + protected String[] args; + + public MethodRunner(String className, String runMethodName, String[] args) { + this.className = checkEmpty(className, "className 不能为空"); + this.runMethodName = checkEmpty(runMethodName, "runMethod 不能为空"); + this.args = (args != null) ? args.clone() : null; + } + + public Object run() throws Exception { + return run(null); + } + + public Object run(ClassLoader classLoader) throws Exception { + Class runClass = loadRunClass(classLoader); + return runMethod(runClass); + } + + protected Class loadRunClass(ClassLoader classLoader) throws Exception{ + if(classLoader == null){ + classLoader = Thread.currentThread().getContextClassLoader(); + } + return Class.forName(this.className, false, classLoader); + } + + protected Object runMethod(Class runClass) throws Exception { + Method runMethod = findRunMethod(runClass); + if(runMethod == null) { + throw new NoSuchMethodException(runClass.getName() + "." + runMethodName + "(String[] args)"); + } + Object instance = getInstance(runClass); + runMethod.setAccessible(true); + runMethod.invoke(instance, new Object[] { this.args }); + return instance; + } + + protected Object getInstance(Class runClass) throws Exception { + return runClass.getConstructor().newInstance(); + } + + private String checkEmpty(String value, String msg){ + if(ObjectUtils.isEmpty(value)){ + throw new IllegalArgumentException(msg); + } + return value; + } + + private Method findRunMethod(Class runClass){ + Class searchType = runClass; + Class[] argClasses = new Class[]{ String[].class }; + while (searchType != null) { + Method[] methods = searchType.isInterface() ? searchType.getMethods() : searchType.getDeclaredMethods(); + for (Method method : methods) { + if(!Objects.equals(method.getName(), runMethodName)){ + continue; + } + if(hasSameParams(method, argClasses)){ + return method; + } + } + searchType = searchType.getSuperclass(); + } + return null; + } + + private boolean hasSameParams(Method method, Class[] paramTypes) { + if(paramTypes.length != method.getParameterCount()){ + return false; + } + Class[] parameterTypes = method.getParameterTypes(); + for (int i = 0; i < paramTypes.length; i++) { + Class paramType = paramTypes[i]; + Class methodParamType = parameterTypes[i]; + if(CompareClassTypeUtils.compare(methodParamType, paramType)){ + return true; + } + } + return false; + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/Assert.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/Assert.java new file mode 100644 index 0000000000000000000000000000000000000000..bf8d51c63267a8957e9131d5ef10bbe1930dc0e0 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/Assert.java @@ -0,0 +1,87 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.utils; + + +import java.util.function.Supplier; + +/** + * 参数校验工具类 + * @author starBlues + * @version 3.0.0 + */ +public abstract class Assert { + + private Assert(){}; + + public static void isTrue(boolean expression, String message) { + if (!expression) { + throw new IllegalArgumentException(message); + } + } + + public static void isTrue(boolean expression, Supplier messageSupplier) { + if (!expression) { + throw new IllegalArgumentException(nullSafeGet(messageSupplier)); + } + } + + public static void state(boolean expression, String message) { + if (!expression) { + throw new IllegalStateException(message); + } + } + + public static void state(boolean expression, Supplier messageSupplier) { + if (!expression) { + throw new IllegalStateException(nullSafeGet(messageSupplier)); + } + } + + public static T isNotNull(T t, String message) { + if (t == null) { + throw new IllegalArgumentException(message); + } + return t; + } + + public static T isNotNull(T t, Supplier messageSupplier) { + if (t == null) { + throw new IllegalArgumentException(nullSafeGet(messageSupplier)); + } + return t; + } + + public static T isNotEmpty(T t, String message) { + if (ObjectUtils.isEmpty(t)) { + throw new IllegalArgumentException(message); + } + return t; + } + + public static T isNotEmpty(T t, Supplier messageSupplier) { + if (ObjectUtils.isEmpty(t)) { + throw new IllegalArgumentException(nullSafeGet(messageSupplier)); + } + return t; + } + + private static String nullSafeGet(Supplier messageSupplier) { + return (messageSupplier != null ? messageSupplier.get() : null); + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/CompareClassTypeUtils.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/CompareClassTypeUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..2b0d7465f4453d421167e5f16bb0077fea34e2e9 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/CompareClassTypeUtils.java @@ -0,0 +1,100 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.utils; + +/** + * 比较两个类类型 + * + * @author starBlues + * @version 3.0.0 + */ +public class CompareClassTypeUtils { + + private CompareClassTypeUtils(){} + + public static boolean compare(Class class1, Class class2){ + if(class1.isAssignableFrom(class2)){ + return true; + } + if(isBoolean(class1) && isBoolean(class2)){ + return true; + } + if(isChar(class1) && isChar(class2)){ + return true; + } + if(isByte(class1) && isByte(class2)){ + return true; + } + if(isShort(class1) && isShort(class2)){ + return true; + } + if(isInt(class1) && isInt(class2)){ + return true; + } + if(isLong(class1) && isLong(class2)){ + return true; + } + if(isFloat(class1) && isFloat(class2)){ + return true; + } + if(isDouble(class1) && isDouble(class2)){ + return true; + } + if(isVoid(class1) && isVoid(class2)){ + return true; + } + return false; + } + + + public static boolean isBoolean(Class class1){ + return class1.isAssignableFrom(Boolean.class) || class1.isAssignableFrom(boolean.class); + } + + public static boolean isChar(Class class1){ + return class1.isAssignableFrom(Character.class) || class1.isAssignableFrom(char.class); + } + + public static boolean isByte(Class class1){ + return class1.isAssignableFrom(Byte.class) || class1.isAssignableFrom(byte.class); + } + + public static boolean isShort(Class class1){ + return class1.isAssignableFrom(Short.class) || class1.isAssignableFrom(short.class); + } + + public static boolean isInt(Class class1){ + return class1.isAssignableFrom(Integer.class) || class1.isAssignableFrom(int.class); + } + + public static boolean isLong(Class class1){ + return class1.isAssignableFrom(Long.class) || class1.isAssignableFrom(long.class); + } + + public static boolean isFloat(Class class1){ + return class1.isAssignableFrom(Float.class) || class1.isAssignableFrom(float.class); + } + + public static boolean isDouble(Class class1){ + return class1.isAssignableFrom(Double.class) || class1.isAssignableFrom(double.class); + } + + public static boolean isVoid(Class class1){ + return class1.isAssignableFrom(Void.class) || class1.isAssignableFrom(void.class); + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/IOUtils.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/IOUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..483cda56cf8191815ad8871b18312b0cae008954 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/IOUtils.java @@ -0,0 +1,75 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gitee.starblues.loader.utils; + +import java.io.*; +import java.util.function.Consumer; + +/** + * io utils + * + * @author starBlues + * @version 3.0.0 + */ +public class IOUtils { + + public static void copy(InputStream inputStream, OutputStream outputStream) throws IOException { + if(inputStream == null){ + throw new IllegalArgumentException("参数inputStream不能为空"); + } + if(outputStream == null){ + throw new IllegalArgumentException("参数inputStream不能为空"); + } + BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream); + BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream); + byte[] arr = new byte[1024]; + int len ; + while ((len = bufferedInputStream.read(arr)) != -1) { + bufferedOutputStream.write(arr, 0, len); + } + bufferedOutputStream.flush(); + } + + public static byte[] read(InputStream inputStream) throws IOException { + if(inputStream == null){ + throw new IllegalArgumentException("参数inputStream不能为空"); + } + ByteArrayOutputStream output = new ByteArrayOutputStream(); + byte[] buffer = new byte[4096]; + int n = 0; + while (-1 != (n = inputStream.read(buffer))) { + output.write(buffer, 0, n); + } + return output.toByteArray(); + } + + public static void closeQuietly(final AutoCloseable closeable) { + closeQuietly(closeable, null); + } + + public static void closeQuietly(final AutoCloseable closeable, final Consumer consumer) { + if (closeable != null) { + try { + closeable.close(); + } catch (final Exception e) { + if (consumer != null) { + consumer.accept(e); + } + } + } + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/ObjectUtils.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/ObjectUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..8490c72b7a964f18d14309f8ee3deb62726a176a --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/ObjectUtils.java @@ -0,0 +1,61 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.utils; + +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.Map; +import java.util.Optional; + +/** + * object utils + * + * @author starBlues + * @version 3.0.0 + */ +public class ObjectUtils { + + private ObjectUtils(){} + + public static boolean isEmpty(CharSequence cs) { + return cs == null || cs.length() == 0; + } + + public static boolean isEmpty(Object obj) { + if (obj == null) { + return true; + } + + if (obj instanceof Optional) { + return !((Optional) obj).isPresent(); + } + if (obj instanceof CharSequence) { + return ((CharSequence) obj).length() == 0; + } + if (obj.getClass().isArray()) { + return Array.getLength(obj) == 0; + } + if (obj instanceof Collection) { + return ((Collection) obj).isEmpty(); + } + if (obj instanceof Map) { + return ((Map) obj).isEmpty(); + } + return false; + } + +} diff --git a/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/ResourceUtils.java b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/ResourceUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..30dceae5a6153490ea8d2b46d91c68fe309dbba8 --- /dev/null +++ b/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/ResourceUtils.java @@ -0,0 +1,112 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.loader.utils; + +import java.net.URL; + +/** + * 资源工具 + * @author starBlues + * @version 3.0.0 + */ +public class ResourceUtils { + + public static final String URL_PROTOCOL_FILE = "file"; + public static final String URL_PROTOCOL_JAR_FILE = "jar"; + public static final String JAR_FILE_EXTENSION = ".jar"; + public static final String ZIP_FILE_EXTENSION = ".zip"; + + public static final String URL_PROTOCOL_VFSFILE = "vfsfile"; + public static final String URL_PROTOCOL_VFS = "vfs"; + + public static final String PACKAGE_SPLIT = "/"; + + private ResourceUtils(){} + + /** + * 是否为jar文件 + * @param url url + * @return boolean + */ + public static boolean isJarFileUrl(URL url) { + String protocol = url.getProtocol(); + boolean extensionIsJar = url.getPath().toLowerCase().endsWith(JAR_FILE_EXTENSION); + return (URL_PROTOCOL_FILE.equals(protocol) && extensionIsJar) + || (URL_PROTOCOL_JAR_FILE.equals(protocol) || extensionIsJar); + } + + /** + * 是否为zip文件 + * @param url url + * @return boolean + */ + public static boolean isZipFileUrl(URL url) { + String protocol = url.getProtocol(); + boolean extensionIsZip = url.getPath().toLowerCase().endsWith(ZIP_FILE_EXTENSION); + return (URL_PROTOCOL_FILE.equals(protocol) && extensionIsZip); + } + + /** + * 是否为jar协议的文件 + * @param url url + * @return boolean + */ + public static boolean isJarProtocolUrl(URL url) { + return URL_PROTOCOL_JAR_FILE.equals(url.getProtocol()); + } + + + /** + * 是否为普通文件 + * @param url url + * @return boolean + */ + public static boolean isFileUrl(URL url) { + String protocol = url.getProtocol(); + return (URL_PROTOCOL_FILE.equals(protocol) || URL_PROTOCOL_VFSFILE.equals(protocol) || + URL_PROTOCOL_VFS.equals(protocol)); + } + + /** + * 将资源名称统一格式化为标准格式 + * 标准格式为 a/b/c + * @param name 原始资源名称 + * @return 标准资源名称 + */ + public static String formatStandardName(String name){ + if(ObjectUtils.isEmpty(name)) { + return PACKAGE_SPLIT; + } + String[] split = name.split(PACKAGE_SPLIT); + StringBuilder newPath = null; + for (String s : split) { + if ("".equals(s)) { + continue; + } + if (newPath == null) { + newPath = new StringBuilder(s); + } else { + newPath.append(PACKAGE_SPLIT).append(s); + } + } + if(newPath == null || newPath.length() == 0){ + return PACKAGE_SPLIT; + } + return newPath.toString(); + } + +} diff --git a/spring-brick-maven-packager/pom.xml b/spring-brick-maven-packager/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..0aebe4dc57a63dfc15963cc40a36572a9faae75c --- /dev/null +++ b/spring-brick-maven-packager/pom.xml @@ -0,0 +1,93 @@ + + + 4.0.0 + + + spring-brick-parent + com.gitee.starblues + 3.0.0 + + + spring-brick-maven-packager + jar + + 打包插件 + + + 8 + 3.0.0 + ${java.version} + ${java.version} + UTF-8 + UTF-8 + UTF-8 + + 3.8.4 + 3.6.2 + 3.2.0 + 1.21 + 2.11.0 + 1.18.20 + 3.8.1 + + 3.6.0 + + + + + com.gitee.starblues + spring-brick-common + ${project.version} + + + org.apache.maven + maven-plugin-api + ${maven-plugin-api.version} + + + org.apache.maven.plugin-tools + maven-plugin-annotations + ${maven-plugin-annotations.version} + + + org.apache.maven.shared + maven-common-artifact-filters + ${maven-common-artifact-filters.version} + + + org.apache.commons + commons-compress + ${commons-compress.version} + + + commons-io + commons-io + ${commons-io.version} + + + org.projectlombok + lombok + ${lombok.version} + provided + true + + + junit + junit + ${junit.version} + test + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + ${maven-plugin-plugin.version} + + + + \ No newline at end of file diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/AbstractDependencyFilterMojo.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/AbstractDependencyFilterMojo.java new file mode 100644 index 0000000000000000000000000000000000000000..42e0e14658cb59a5baa57a439cdc78572813f02d --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/AbstractDependencyFilterMojo.java @@ -0,0 +1,100 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack; + +import com.gitee.starblues.plugin.pack.filter.Exclude; +import com.gitee.starblues.plugin.pack.filter.ExcludeFilter; +import com.gitee.starblues.plugin.pack.filter.Include; +import com.gitee.starblues.plugin.pack.filter.IncludeFilter; +import com.gitee.starblues.plugin.pack.utils.CommonUtils; +import com.gitee.starblues.utils.ObjectUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException; +import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter; +import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * 抽象可过滤依赖的 mojo + * @author starBlues + * @version 3.0.0 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public abstract class AbstractDependencyFilterMojo extends AbstractMojo { + + + @Parameter(property = "spring-brick-packager.includes") + private List includes; + + @Parameter(property = "spring-brick-packager.excludes") + private List excludes; + + + protected final Set filterDependencies(Set dependencies, FilterArtifacts filters) + throws MojoExecutionException { + try { + Set filtered = new LinkedHashSet<>(dependencies); + filtered.retainAll(filters.filter(dependencies)); + return filtered; + } + catch (ArtifactFilterException ex) { + throw new MojoExecutionException(ex.getMessage(), ex); + } + } + + protected final FilterArtifacts getFilters(ArtifactsFilter... additionalFilters) { + FilterArtifacts filters = new FilterArtifacts(); + for (ArtifactsFilter additionalFilter : additionalFilters) { + filters.addFilter(additionalFilter); + } + if (!ObjectUtils.isEmpty(includes)) { + filters.addFilter(new IncludeFilter(this.includes)); + } + if(ObjectUtils.isEmpty(excludes)){ + excludes = new ArrayList<>(); + } + // 添加主框架排除 + addPluginFrameworkExclude(); + // 添加spring web 环境排除 + addSpringWebEnvExclude(); + filters.addFilter(new ExcludeFilter(this.excludes)); + return filters; + } + + private void addPluginFrameworkExclude(){ + excludes.add(CommonUtils.getPluginFrameworkExclude()); + } + + private void addSpringWebEnvExclude(){ + excludes.add(Exclude.get("org.springframework.boot", "spring-boot-starter-web")); + excludes.add(Exclude.get("org.springframework.boot", "spring-boot-starter-tomcat")); + excludes.add(Exclude.get("org.springframework.boot", "spring-boot-starter-json")); + excludes.add(Exclude.get("org.springframework", "spring-webmvc")); + excludes.add(Exclude.get("org.springframework", "spring-web")); + } + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/AbstractPackagerMojo.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/AbstractPackagerMojo.java new file mode 100644 index 0000000000000000000000000000000000000000..f169726e23a54ab8a0ac626da84f0588b15f66e3 --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/AbstractPackagerMojo.java @@ -0,0 +1,84 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; + +import java.io.File; +import java.util.Set; + +/** + * 抽象的重新打包 mojo + * @author starBlues + * @version 3.0.0 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo{ + + @Parameter(defaultValue = "${project}", readonly = true, required = true) + private MavenProject project; + + @Parameter(defaultValue = "${project.build.directory}", required = true) + private File outputDirectory; + + @Parameter(property = "spring-brick-packager.mode", defaultValue = "dev", required = true) + private String mode; + + @Parameter(property = "spring-brick-packager.skip", defaultValue = "false") + private boolean skip; + + @Parameter(property = "spring-brick-packager.pluginInfo") + private PluginInfo pluginInfo; + + @Parameter(property = "spring-brick-packager.loadMainResourcePattern", required = false) + private LoadMainResourcePattern loadMainResourcePattern; + + @Override + public final void execute() throws MojoExecutionException, MojoFailureException { + if(Constant.isPom(this.getProject().getPackaging())){ + getLog().debug("repackage goal could not be applied to pom project."); + return; + } + if (this.skip) { + getLog().debug("skipping plugin package."); + return; + } + pack(); + } + + /** + * 打包 + * @throws MojoExecutionException MojoExecutionException + * @throws MojoFailureException MojoFailureException + */ + protected abstract void pack() throws MojoExecutionException, MojoFailureException; + + public final Set getFilterDependencies() throws MojoExecutionException { + return filterDependencies(project.getArtifacts(), getFilters()); + } + + public final Set getSourceDependencies() throws MojoExecutionException { + return project.getArtifacts(); + } +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/BasicRepackager.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/BasicRepackager.java new file mode 100644 index 0000000000000000000000000000000000000000..ae6946000d87c1b95cce07c4c2432e50021c2cd7 --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/BasicRepackager.java @@ -0,0 +1,356 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack; + +import com.gitee.starblues.common.AbstractDependencyPlugin; +import com.gitee.starblues.common.ManifestKey; +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.common.PackageType; +import com.gitee.starblues.plugin.pack.dev.DevConfig; +import com.gitee.starblues.plugin.pack.utils.CommonUtils; +import com.gitee.starblues.utils.FilesUtils; +import com.gitee.starblues.utils.ObjectUtils; +import lombok.Getter; +import org.apache.commons.io.FileUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import static com.gitee.starblues.common.PackageStructure.*; +import static com.gitee.starblues.common.PluginDescriptorKey.*; + +/** + * 基础打包 + * @author starBlues + * @version 3.0.0 + */ +public class BasicRepackager implements Repackager{ + + @Getter + private String rootDir; + private String relativeManifestPath; + private String relativePluginMetaPath; + private String relativeResourcesDefinePath; + + protected File resourcesDefineFile; + + protected final RepackageMojo repackageMojo; + + public BasicRepackager(RepackageMojo repackageMojo) { + this.repackageMojo = repackageMojo; + } + + @Override + public void repackage() throws MojoExecutionException, MojoFailureException { + checkPluginInfo(); + rootDir = createRootDir(); + relativeManifestPath = getRelativeManifestPath(); + relativePluginMetaPath = getRelativePluginMetaPath(); + relativeResourcesDefinePath = getRelativeResourcesDefinePath(); + try { + Manifest manifest = getManifest(); + writeManifest(manifest); + } catch (Exception e) { + repackageMojo.getLog().error(e.getMessage(), e); + throw new MojoFailureException(e); + } + } + + private void checkPluginInfo() throws MojoExecutionException { + PluginInfo pluginInfo = repackageMojo.getPluginInfo(); + if(pluginInfo == null){ + throw new MojoExecutionException("configuration.pluginInfo config cannot be empty"); + } + if(ObjectUtils.isEmpty(pluginInfo.getId())){ + throw new MojoExecutionException("configuration.pluginInfo.id config cannot be empty"); + } else { + String id = pluginInfo.getId(); + String illegal = PackageStructure.getIllegal(id); + if(illegal != null){ + throw new MojoExecutionException("configuration.pluginInfo.id config can't contain: " + illegal); + } + } + if(ObjectUtils.isEmpty(pluginInfo.getBootstrapClass())){ + throw new MojoExecutionException("configuration.pluginInfo.bootstrapClass config cannot be empty"); + } + if(ObjectUtils.isEmpty(pluginInfo.getVersion())){ + throw new MojoExecutionException("configuration.pluginInfo.version config cannot be empty"); + } else { + String version = pluginInfo.getVersion(); + String illegal = PackageStructure.getIllegal(version); + if(illegal != null){ + throw new MojoExecutionException("configuration.pluginInfo.version config can't contain: " + illegal); + } + } + } + + protected String getRelativeManifestPath(){ + return MANIFEST; + } + + protected String getRelativeResourcesDefinePath(){ + return RESOURCES_DEFINE_NAME; + } + + protected String getRelativePluginMetaPath(){ + return PLUGIN_META_NAME; + } + + protected String createRootDir() throws MojoFailureException { + String rootDirPath = getBasicRootDir(); + File rootDir = new File(rootDirPath); + rootDir.deleteOnExit(); + if(rootDir.mkdir()){ + return rootDirPath; + } + throw new MojoFailureException("Failed to create the plugin root directory. " + rootDirPath); + } + + protected String getBasicRootDir(){ + File outputDirectory = repackageMojo.getOutputDirectory(); + return FilesUtils.joiningFilePath(outputDirectory.getPath(), PackageStructure.META_INF_NAME); + } + + protected void writeManifest(Manifest manifest) throws Exception { + String manifestPath = FilesUtils.joiningFilePath(rootDir, resolvePath(this.relativeManifestPath)); + File file = new File(manifestPath); + FileOutputStream outputStream = null; + try { + FileUtils.forceMkdirParent(file); + if(file.createNewFile()){ + outputStream = new FileOutputStream(file, false); + manifest.write(outputStream); + } + } finally { + if(outputStream != null){ + try { + outputStream.close(); + } catch (IOException e) { + repackageMojo.getLog().error(e.getMessage(), e); + } + } + } + } + + protected Manifest getManifest() throws Exception{ + Manifest manifest = new Manifest(); + Attributes attributes = manifest.getMainAttributes(); + attributes.putValue(ManifestKey.MANIFEST_VERSION, ManifestKey.MANIFEST_VERSION_1_0); + attributes.putValue(ManifestKey.PLUGIN_META_PATH, getPluginMetaInfoPath()); + attributes.putValue(ManifestKey.PLUGIN_PACKAGE_TYPE, PackageType.PLUGIN_PACKAGE_TYPE_DEV); + return manifest; + } + + /** + * 得到插件信息存储文件路径 + * @return 插件信息存储文件路径 + * @throws Exception Exception + */ + protected String getPluginMetaInfoPath() throws Exception { + Properties pluginMetaInfo = createPluginMetaInfo(); + return writePluginMetaInfo(pluginMetaInfo); + } + + /** + * 创建插件信息 + * @return Properties + * @throws Exception Exception + */ + protected Properties createPluginMetaInfo() throws Exception { + Properties properties = new Properties(); + PluginInfo pluginInfo = repackageMojo.getPluginInfo(); + properties.put(PLUGIN_ID, pluginInfo.getId()); + properties.put(PLUGIN_BOOTSTRAP_CLASS, pluginInfo.getBootstrapClass()); + properties.put(PLUGIN_VERSION, pluginInfo.getVersion()); + properties.put(PLUGIN_PATH, getPluginPath()); + + String resourcesDefineFilePath = writeResourcesDefineFile(); + if(!ObjectUtils.isEmpty(resourcesDefineFilePath)){ + properties.put(PLUGIN_RESOURCES_CONFIG, resourcesDefineFilePath); + } + String configFileName = pluginInfo.getConfigFileName(); + if(!ObjectUtils.isEmpty(configFileName)){ + properties.put(PLUGIN_CONFIG_FILE_NAME, configFileName); + } + String configFileLocation = pluginInfo.getConfigFileLocation(); + if(!ObjectUtils.isEmpty(configFileLocation)){ + properties.put(PLUGIN_CONFIG_FILE_LOCATION, configFileLocation); + } + String args = pluginInfo.getArgs(); + if(!ObjectUtils.isEmpty(args)){ + properties.put(PLUGIN_ARGS, args); + } + String provider = pluginInfo.getProvider(); + if(!ObjectUtils.isEmpty(provider)){ + properties.put(PLUGIN_PROVIDER, provider); + } + String requires = pluginInfo.getRequires(); + if(!ObjectUtils.isEmpty(requires)){ + properties.put(PLUGIN_REQUIRES, requires); + } + String dependencyPlugins = getDependencyPlugin(pluginInfo); + if(!ObjectUtils.isEmpty(dependencyPlugins)){ + properties.put(PLUGIN_DEPENDENCIES, dependencyPlugins); + } + String description = pluginInfo.getDescription(); + if(!ObjectUtils.isEmpty(description)){ + properties.put(PLUGIN_DESCRIPTION, description); + } + String license = pluginInfo.getLicense(); + if(!ObjectUtils.isEmpty(license)){ + properties.put(PLUGIN_LICENSE, license); + } + return properties; + } + + protected String getDependencyPlugin(PluginInfo pluginInfo){ + List dependencyPlugins = pluginInfo.getDependencyPlugins(); + return AbstractDependencyPlugin.toStr(dependencyPlugins); + } + + /** + * 写入插件信息 + * @param properties properties + * @return String + * @throws IOException IOException + */ + protected String writePluginMetaInfo(Properties properties) throws Exception { + File pluginMetaFile = createPluginMetaFile(); + try (OutputStreamWriter writer = new OutputStreamWriter( + new FileOutputStream(pluginMetaFile), StandardCharsets.UTF_8)){ + properties.store(writer, Constant.PLUGIN_METE_COMMENTS); + return pluginMetaFile.getPath(); + } + } + + /** + * 创建插件信息存储文件 + * @return File + * @throws IOException 创建文件异常 + */ + protected File createPluginMetaFile() throws IOException { + String path = FilesUtils.joiningFilePath(rootDir, resolvePath(relativePluginMetaPath)); + return FilesUtils.createFile(path); + } + + /** + * 获取插件路径 + * @return 插件路径 + */ + protected String getPluginPath(){ + return repackageMojo.getProject().getBuild().getOutputDirectory(); + } + + protected String writeResourcesDefineFile() throws Exception{ + resourcesDefineFile = createResourcesDefineFile(); + writeDependenciesIndex(); + writeLoadMainResources(); + return resourcesDefineFile.getPath(); + } + + protected File createResourcesDefineFile() throws IOException { + String path = FilesUtils.joiningFilePath(rootDir, resolvePath(relativeResourcesDefinePath)); + return FilesUtils.createFile(path); + } + + protected void writeDependenciesIndex() throws Exception { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(RESOURCES_DEFINE_DEPENDENCIES).append("\n"); + Set libIndex = getDependenciesIndexSet(); + for (String index : libIndex) { + stringBuilder.append(index).append("\n"); + } + String content = stringBuilder.toString(); + FileUtils.write(resourcesDefineFile, content, CHARSET_NAME, true); + } + + protected Set getDependenciesIndexSet() throws Exception { + Set dependencies = repackageMojo.getFilterDependencies(); + Set libPaths = new HashSet<>(dependencies.size()); + for (Artifact artifact : dependencies) { + if(filterArtifact(artifact)){ + continue; + } + libPaths.add(getLibIndex(artifact)); + } + return libPaths; + } + + protected String getLibIndex(Artifact artifact){ + return artifact.getFile().getPath() + repackageMojo.resolveLoadToMain(artifact); + } + + protected void writeLoadMainResources() throws Exception { + String loadMainResources = getLoadMainResources(); + if(ObjectUtils.isEmpty(loadMainResources)){ + return; + } + FileUtils.write(resourcesDefineFile, loadMainResources, CHARSET_NAME, true); + } + + protected String getLoadMainResources(){ + LoadMainResourcePattern loadMainResourcePattern = repackageMojo.getLoadMainResourcePattern(); + if(loadMainResourcePattern == null){ + return null; + } + String[] includes = loadMainResourcePattern.getIncludes(); + String[] excludes = loadMainResourcePattern.getExcludes(); + StringBuilder stringBuilder = new StringBuilder(); + addLoadMainResources(stringBuilder, RESOURCES_DEFINE_LOAD_MAIN_INCLUDES, includes); + addLoadMainResources(stringBuilder, RESOURCES_DEFINE_LOAD_MAIN_EXCLUDES, excludes); + return stringBuilder.toString(); + } + + private void addLoadMainResources(StringBuilder stringBuilder, String header, String[] patterns){ + if(ObjectUtils.isEmpty(patterns)){ + return; + } + Set patternSet = new HashSet<>(Arrays.asList(patterns)); + stringBuilder.append(header).append("\n"); + for (String patternStr : patternSet) { + if(ObjectUtils.isEmpty(patternStr)){ + continue; + } + stringBuilder.append(resolvePattern(patternStr)).append("\n"); + } + } + + protected String resolvePattern(String patternStr){ + return patternStr.replace(".", "/"); + } + + /** + * 过滤Artifact + * @param artifact Artifact + * @return 返回true表示被过滤掉 + */ + protected boolean filterArtifact(Artifact artifact){ + return Constant.scopeFilter(artifact.getScope()); + } + + + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/Constant.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/Constant.java new file mode 100644 index 0000000000000000000000000000000000000000..3358372d3de7bf558eac1c7f0e701f525b0d5593 --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/Constant.java @@ -0,0 +1,46 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack; + +/** + * 静态类 + * @author starBlues + * @version 3.0.0 + */ +public class Constant { + + public static final String PACKAGING_POM = "pom"; + public static final String SCOPE_PROVIDED = "provided"; + public static final String SCOPE_COMPILE = "compile"; + public static final String SCOPE_TEST = "test"; + + public static final String MODE_MAIN = "main"; + public static final String MODE_DEV = "dev"; + public static final String MODE_PROD = "prod"; + + public static final String PLUGIN_METE_COMMENTS = "plugin meta configuration"; + + public static boolean isPom(String packageType){ + return PACKAGING_POM.equalsIgnoreCase(packageType); + } + + public static boolean scopeFilter(String scope){ + return SCOPE_PROVIDED.equalsIgnoreCase(scope) + || SCOPE_TEST.equalsIgnoreCase(scope); + } + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/Dependency.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/Dependency.java new file mode 100644 index 0000000000000000000000000000000000000000..109a27e06fdba2cf273ffda6345befe1f68d1838 --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/Dependency.java @@ -0,0 +1,36 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack; + +import lombok.Data; +import org.apache.maven.plugins.annotations.Parameter; + +/** + * 依赖Bean + * @author starBlues + * @version 3.0.0 + */ +@Data +public class Dependency { + + @Parameter(required = true) + private String groupId; + + @Parameter(required = true) + private String artifactId; + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/DependencyPlugin.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/DependencyPlugin.java new file mode 100644 index 0000000000000000000000000000000000000000..10c916c8ffbb8ff5b3c7eb468e6b9124b711f768 --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/DependencyPlugin.java @@ -0,0 +1,71 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack; + +import com.gitee.starblues.common.AbstractDependencyPlugin; +import org.apache.maven.plugins.annotations.Parameter; + +/** + * 依赖的插件 + * @author starBlues + * @version 3.0.0 + */ +public class DependencyPlugin extends AbstractDependencyPlugin { + + @Parameter(required = true) + private String id; + + @Parameter(required = true) + private String version; + + @Parameter(required = false, defaultValue = "true") + private Boolean optional = false; + + @Override + public String getId() { + return id; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public Boolean getOptional() { + if(optional == null){ + return false; + } + return optional; + } + + @Override + public void setId(String id) { + this.id = id; + } + + @Override + public void setVersion(String version) { + this.version = version; + } + + @Override + public void setOptional(Boolean optional) { + this.optional = optional; + } + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/LoadMainResourcePattern.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/LoadMainResourcePattern.java new file mode 100644 index 0000000000000000000000000000000000000000..ed826269e45bc968149f529f4b553b316c6ed200 --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/LoadMainResourcePattern.java @@ -0,0 +1,36 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack; + +import lombok.Data; +import org.apache.maven.plugins.annotations.Parameter; + +/** + * 从主程序加载资源配置 + * @author starBlues + * @version 3.0.0 + */ +@Data +public class LoadMainResourcePattern { + + @Parameter(name = "includes") + private String[] includes; + + @Parameter(name = "excludes") + private String[] excludes; + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/LoadToMain.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/LoadToMain.java new file mode 100644 index 0000000000000000000000000000000000000000..2cc7e26f207d7dd2573735c44c77be7a8624aa4e --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/LoadToMain.java @@ -0,0 +1,33 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack; + +import lombok.Data; + +import java.util.List; + +/** + * 定义依赖加载到主程序中 + * @author starBlues + * @version 3.0.0 + */ +@Data +public class LoadToMain { + + private List dependencies; + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/PluginInfo.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/PluginInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..5d5066f2d8bc1f06ee79b374d872c567c908a341 --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/PluginInfo.java @@ -0,0 +1,90 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack; + +import lombok.Data; +import org.apache.maven.plugins.annotations.Parameter; + +import java.util.List; + +/** + * 插件信息 + * @author starBlues + * @version 3.0.0 + */ +@Data +public class PluginInfo { + + /** + * 插件id + */ + @Parameter(required = true) + private String id; + + /** + * 插件引导启动类 + */ + @Parameter(required = true) + private String bootstrapClass; + + /** + * 插件版本 + */ + @Parameter(required = true) + private String version; + + /** + * 插件配置文件名称。 + */ + private String configFileName; + + /** + * 插件配置文件所在目录。如果不填写, 默认从 target/classes 下读取 + */ + private String configFileLocation; + + /** + * 插件启动入口参数配置 + */ + private String args; + + /** + * 插件描述 + */ + private String description; + + /** + * 插件提供者 + */ + private String provider; + + /** + * 需要安装的主程序版本 + */ + private String requires; + + /** + * 插件 license + */ + private String license; + + /** + * 依赖的插件 + */ + private List dependencyPlugins; + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/RepackageMojo.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/RepackageMojo.java new file mode 100644 index 0000000000000000000000000000000000000000..5166f795aba89e0c76abddd61a208bba275a9d68 --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/RepackageMojo.java @@ -0,0 +1,105 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack; + +import com.gitee.starblues.common.Constants; +import com.gitee.starblues.plugin.pack.dev.DevConfig; +import com.gitee.starblues.plugin.pack.dev.DevRepackager; +import com.gitee.starblues.plugin.pack.main.MainConfig; +import com.gitee.starblues.plugin.pack.main.MainRepackager; +import com.gitee.starblues.plugin.pack.prod.ProdConfig; +import com.gitee.starblues.plugin.pack.prod.ProdRepackager; +import com.gitee.starblues.utils.ObjectUtils; +import lombok.Getter; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * 重新打包 mojo + * @author starBlues + * @version 3.0.0 + */ +@Mojo(name = "repackage", defaultPhase = LifecyclePhase.PACKAGE, requiresProject = true, threadSafe = true, + requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, + requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME) +@Getter +public class RepackageMojo extends AbstractPackagerMojo { + + + @Parameter(property = "spring-brick-packager.devConfig") + private DevConfig devConfig; + + @Parameter(property = "spring-brick-packager.prodConfig") + private ProdConfig prodConfig; + + @Parameter(property = "spring-brick-packager.mainConfig") + private MainConfig mainConfig; + + @Parameter(property = "spring-brick-packager.mainLoad") + private LoadToMain loadToMain; + + private final Set loadToMainSet = new HashSet<>(); + + @Override + protected void pack() throws MojoExecutionException, MojoFailureException { + initLoadToMainSet(); + String mode = getMode(); + if(Constant.MODE_PROD.equalsIgnoreCase(mode)){ + new ProdRepackager(this).repackage(); + } else if(Constant.MODE_DEV.equalsIgnoreCase(mode)){ + new DevRepackager(this).repackage(); + } else if(Constant.MODE_MAIN.equalsIgnoreCase(mode)){ + new MainRepackager(this).repackage(); + } else { + throw new MojoExecutionException(mode +" model not supported, mode support : " + + Constant.MODE_DEV + "/" + Constant.MODE_PROD); + } + } + + public String resolveLoadToMain(Artifact artifact){ + if(artifact == null){ + return ""; + } + if(loadToMainSet.contains(artifact.getGroupId() + artifact.getArtifactId())){ + return Constants.LOAD_TO_MAIN_SIGN; + } + return ""; + } + + private void initLoadToMainSet(){ + if(loadToMain == null){ + return; + } + List dependencies = loadToMain.getDependencies(); + if(ObjectUtils.isEmpty(dependencies)){ + return; + } + for (Dependency dependency : dependencies) { + loadToMainSet.add(dependency.getGroupId() + dependency.getArtifactId()); + } + } + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/Repackager.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/Repackager.java new file mode 100644 index 0000000000000000000000000000000000000000..d1ea896f9a40371099e0a95251dcd1fccb4c6aa6 --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/Repackager.java @@ -0,0 +1,37 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + +/** + * 重新打包j接口 + * @author starBlues + * @version 3.0.0 + */ +public interface Repackager { + + /** + * 重新打包 + * @throws MojoExecutionException MojoExecutionException + * @throws MojoFailureException MojoFailureException + */ + void repackage() throws MojoExecutionException, MojoFailureException; + + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/dev/Dependency.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/dev/Dependency.java new file mode 100644 index 0000000000000000000000000000000000000000..4e61d928f843373488f9d5d752d7c547e17ea2ef --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/dev/Dependency.java @@ -0,0 +1,39 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.dev; + +import lombok.Data; +import org.apache.maven.plugins.annotations.Parameter; + +/** + * 开发环境下配置本地依赖的Bean + * @author starBlues + * @version 3.0.0 + */ +@Data +public class Dependency { + + @Parameter(required = true) + private String groupId; + + @Parameter(required = true) + private String artifactId; + + @Parameter(required = true) + private String classesPath; + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/dev/DevConfig.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/dev/DevConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..20ed1b65d3642a0bf0c666f72f6bf7454e1f5da1 --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/dev/DevConfig.java @@ -0,0 +1,37 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.dev; + +import lombok.Data; + +import java.util.List; + +/** + * 开发模式配置 + * @author starBlues + * @version 3.0.0 + */ +@Data +public class DevConfig { + + /** + * 当前项目依赖其他模块的定义。 + * 主要定义依赖模块target->classes的目录, 方便开发调试 + */ + private List moduleDependencies; + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/dev/DevRepackager.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/dev/DevRepackager.java new file mode 100644 index 0000000000000000000000000000000000000000..9e436c24dad17ca38a6a4e0fefd3669ee971a0fb --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/dev/DevRepackager.java @@ -0,0 +1,92 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.dev; + +import com.gitee.starblues.plugin.pack.BasicRepackager; +import com.gitee.starblues.plugin.pack.Constant; +import com.gitee.starblues.plugin.pack.RepackageMojo; +import com.gitee.starblues.plugin.pack.utils.CommonUtils; +import com.gitee.starblues.utils.FilesUtils; +import com.gitee.starblues.utils.ObjectUtils; +import lombok.Getter; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; + +import java.util.*; + +/** + * 开发环境打包 + * @author starBlues + * @version 3.0.0 + */ +public class DevRepackager extends BasicRepackager { + + @Getter + private Map moduleDependencies = Collections.emptyMap(); + + public DevRepackager(RepackageMojo repackageMojo) { + super(repackageMojo); + } + + @Override + protected Set getDependenciesIndexSet() throws Exception { + moduleDependencies = getModuleDependencies(repackageMojo.getDevConfig()); + Set dependenciesIndexSet = super.getDependenciesIndexSet(); + for (Dependency dependency : moduleDependencies.values()) { + dependenciesIndexSet.add(dependency.getClassesPath()); + } + return dependenciesIndexSet; + } + + @Override + protected boolean filterArtifact(Artifact artifact) { + if(super.filterArtifact(artifact)){ + return true; + } + String moduleDependencyKey = getModuleDependencyKey(artifact.getGroupId(), artifact.getArtifactId()); + Dependency dependency = moduleDependencies.get(moduleDependencyKey); + return dependency != null && !ObjectUtils.isEmpty(dependency.getClassesPath()); + } + + protected Map getModuleDependencies(DevConfig devConfig) { + if(devConfig == null){ + return Collections.emptyMap(); + } + List moduleDependencies = devConfig.getModuleDependencies(); + if(ObjectUtils.isEmpty(moduleDependencies)){ + return Collections.emptyMap(); + } + Map moduleDependenciesMap = new HashMap<>(); + for (Dependency dependency : moduleDependencies) { + String moduleDependencyKey = getModuleDependencyKey(dependency.getGroupId(), + dependency.getArtifactId()); + if(moduleDependencyKey == null){ + continue; + } + moduleDependenciesMap.put(moduleDependencyKey, dependency); + } + return moduleDependenciesMap; + } + + protected String getModuleDependencyKey(String groupId, String artifactId){ + if(ObjectUtils.isEmpty(groupId) || ObjectUtils.isEmpty(artifactId)){ + return null; + } + return groupId + artifactId; + } + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/DependencyFilter.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/DependencyFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..f26792d572a7ae2aaeae4acc0f280dd209cdade5 --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/DependencyFilter.java @@ -0,0 +1,73 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.filter; + +import com.gitee.starblues.utils.ObjectUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.shared.artifact.filter.collection.AbstractArtifactsFilter; +import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * 依赖过滤 + * @author starBlues + * @version 3.0.0 + */ +public abstract class DependencyFilter extends AbstractArtifactsFilter { + + private final List filters; + + public DependencyFilter(List dependencies) { + this.filters = dependencies; + } + + @Override + public Set filter(Set artifacts) throws ArtifactFilterException { + if(ObjectUtils.isEmpty(artifacts)){ + return artifacts; + } + Set result = new HashSet<>(); + for (Artifact artifact : artifacts) { + if (!filter(artifact)) { + result.add(artifact); + } + } + return result; + } + + /** + * 子类过滤结果 + * @param artifact artifact + * @return boolean + */ + protected abstract boolean filter(Artifact artifact); + + protected final boolean equals(Artifact artifact, FilterableDependency dependency) { + if (!dependency.getGroupId().equals(artifact.getGroupId())) { + return false; + } + return dependency.getArtifactId().equals(artifact.getArtifactId()); + } + + protected final List getFilters() { + return this.filters; + } + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/Exclude.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/Exclude.java new file mode 100644 index 0000000000000000000000000000000000000000..5679c55a38dc3e26d4481ff40a403c6044aa2559 --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/Exclude.java @@ -0,0 +1,33 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.filter; + +/** + * 排除的依赖定义 + * @author starBlues + * @version 3.0.0 + */ +public class Exclude extends FilterableDependency{ + + public static Exclude get(String groupId, String artifactId){ + Exclude exclude = new Exclude(); + exclude.setGroupId(groupId); + exclude.setArtifactId(artifactId); + return exclude; + } + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/ExcludeFilter.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/ExcludeFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..dce74ece1a175bf738ae9507c4887d97a256bb4c --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/ExcludeFilter.java @@ -0,0 +1,49 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.filter; + +import org.apache.maven.artifact.Artifact; + +import java.util.Arrays; +import java.util.List; + +/** + * 排除过滤 + * @author starBlues + * @version 3.0.0 + */ +public class ExcludeFilter extends DependencyFilter { + + public ExcludeFilter(Exclude... excludes) { + this(Arrays.asList(excludes)); + } + + public ExcludeFilter(List excludes) { + super(excludes); + } + + @Override + protected boolean filter(Artifact artifact) { + for (FilterableDependency dependency : getFilters()) { + if (equals(artifact, dependency)) { + return true; + } + } + return false; + } + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/FilterableDependency.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/FilterableDependency.java new file mode 100644 index 0000000000000000000000000000000000000000..7f24259cf9b45c6dcf1a56ef35a5c9604c5dea42 --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/FilterableDependency.java @@ -0,0 +1,36 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.filter; + +import lombok.Data; +import org.apache.maven.plugins.annotations.Parameter; + +/** + * 可过滤依赖bean + * @author starBlues + * @version 3.0.0 + */ +@Data +public abstract class FilterableDependency { + + @Parameter(required = true) + private String groupId; + + @Parameter(required = true) + private String artifactId; + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/Include.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/Include.java new file mode 100644 index 0000000000000000000000000000000000000000..55ae558077176ec7581fc898c97a71dc3c45d91d --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/Include.java @@ -0,0 +1,25 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.filter; + +/** + * 包含的依赖定义 + * @author starBlues + * @version 3.0.0 + */ +public class Include extends FilterableDependency{ +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/IncludeFilter.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/IncludeFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..1ab20ea4633c9a65f0b090f7e99f290281ceef1b --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/IncludeFilter.java @@ -0,0 +1,44 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.filter; + +import org.apache.maven.artifact.Artifact; + +import java.util.List; + +/** + * 包含过滤器 + * @author starBlues + * @version 3.0.0 + */ +public class IncludeFilter extends DependencyFilter { + + public IncludeFilter(List includes) { + super(includes); + } + + @Override + protected boolean filter(Artifact artifact) { + for (FilterableDependency dependency : getFilters()) { + if (equals(artifact, dependency)) { + return false; + } + } + return true; + } + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/JarNestPackager.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/JarNestPackager.java new file mode 100644 index 0000000000000000000000000000000000000000..429c4b51ea9421f6154b58341839f304e8f89388 --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/JarNestPackager.java @@ -0,0 +1,120 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.main; + +import com.gitee.starblues.plugin.pack.Constant; +import com.gitee.starblues.plugin.pack.RepackageMojo; +import com.gitee.starblues.plugin.pack.Repackager; +import com.gitee.starblues.plugin.pack.utils.CommonUtils; +import com.gitee.starblues.plugin.pack.utils.PackageJar; +import com.gitee.starblues.utils.ObjectUtils; +import org.apache.commons.io.IOUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + +import java.io.File; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import static com.gitee.starblues.common.PackageStructure.*; +import static com.gitee.starblues.common.ManifestKey.*; + +/** + * 嵌套jar打包 + * @author starBlues + * @version 3.0.0 + */ +public class JarNestPackager implements Repackager { + + protected final MainConfig mainConfig; + protected final RepackageMojo repackageMojo; + + protected PackageJar packageJar; + + public JarNestPackager(MainRepackager mainRepackager) { + this.mainConfig = mainRepackager.getMainConfig(); + this.repackageMojo = mainRepackager.getRepackageMojo(); + } + + @Override + public void repackage() throws MojoExecutionException, MojoFailureException { + try { + packageJar = new PackageJar(mainConfig.getOutputDirectory(), mainConfig.getFileName()); + writeClasses(); + writeDependencies(); + writeManifest(); + } catch (Exception e) { + repackageMojo.getLog().error(e.getMessage(), e); + throw new MojoFailureException(e); + } finally { + if(packageJar != null){ + IOUtils.closeQuietly(packageJar); + } + } + } + + protected void writeManifest() throws Exception { + Manifest manifest = getManifest(); + packageJar.putDirEntry(META_INF_NAME + SEPARATOR); + packageJar.write(PROD_MANIFEST_PATH, manifest::write); + } + + protected Manifest getManifest() throws Exception{ + Manifest manifest = new Manifest(); + Attributes attributes = manifest.getMainAttributes(); + attributes.putValue(MANIFEST_VERSION, MANIFEST_VERSION_1_0); + attributes.putValue(START_CLASS, mainConfig.getMainClass()); + attributes.putValue(MAIN_CLASS, MAIN_CLASS_VALUE); + return manifest; + } + + protected void writeClasses() throws Exception { + String buildDir = repackageMojo.getProject().getBuild().getOutputDirectory(); + packageJar.copyDirToPackage(new File(buildDir), null); + } + + protected void writeDependencies() throws Exception { + Set dependencies = repackageMojo.getSourceDependencies(); + String libDirEntryName = createLibEntry(); + for (Artifact artifact : dependencies) { + if(filterArtifact(artifact)){ + continue; + } + if(CommonUtils.isPluginFrameworkLoader(artifact)){ + // 本框架loader依赖 + packageJar.copyZipToPackage(artifact.getFile()); + } else { + packageJar.writeDependency(artifact.getFile(), libDirEntryName); + } + } + } + + protected boolean filterArtifact(Artifact artifact) { + return Constant.scopeFilter(artifact.getScope()); + } + + protected String createLibEntry() throws Exception { + String libDirEntryName = PROD_LIB_PATH; + packageJar.putDirEntry(libDirEntryName); + return libDirEntryName; + } + + + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/JarOuterPackager.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/JarOuterPackager.java new file mode 100644 index 0000000000000000000000000000000000000000..3eef4d5b3390adb607b7508b826698bc38f144d1 --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/JarOuterPackager.java @@ -0,0 +1,110 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.main; + +import com.gitee.starblues.common.ManifestKey; +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.utils.FilesUtils; +import org.apache.commons.io.FileUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import static com.gitee.starblues.common.ManifestKey.*; +import static com.gitee.starblues.common.ManifestKey.MANIFEST_VERSION_1_0; + +/** + * jar 外置包 + * @author starBlues + * @version 3.0.0 + */ +public class JarOuterPackager extends JarNestPackager { + + private final List dependenciesName = new ArrayList<>(); + + public JarOuterPackager(MainRepackager mainRepackager) { + super(mainRepackager); + } + + @Override + public void repackage() throws MojoExecutionException, MojoFailureException { + // 生成依赖文件夹 + String rootDir = createRootDir(); + mainConfig.setOutputDirectory(rootDir); + super.repackage(); + } + + @Override + protected void writeClasses() throws Exception { + String buildDir = repackageMojo.getProject().getBuild().getOutputDirectory(); + packageJar.copyDirToPackage(new File(buildDir), ""); + } + + private String createRootDir() throws MojoFailureException{ + String outputDirectory = mainConfig.getOutputDirectory(); + String fileName = mainConfig.getFileName(); + String rootDirPath = FilesUtils.joiningFilePath(outputDirectory, fileName); + File rootFile = new File(rootDirPath); + if(rootFile.exists()){ + rootFile.deleteOnExit(); + } + if(rootFile.mkdirs()){ + return rootDirPath; + } else { + throw new MojoFailureException("Create dir failure : " + rootDirPath); + } + } + + @Override + protected Manifest getManifest() throws Exception { + Manifest manifest = new Manifest(); + Attributes attributes = manifest.getMainAttributes(); + attributes.putValue(MANIFEST_VERSION, MANIFEST_VERSION_1_0); + attributes.remove(START_CLASS); + attributes.putValue(MAIN_CLASS, mainConfig.getMainClass()); + if(dependenciesName.isEmpty()){ + return manifest; + } + String classPathStr = String.join(" ", dependenciesName); + attributes.putValue(ManifestKey.CLASS_PATH, classPathStr); + return manifest; + } + + @Override + protected void writeDependencies() throws Exception { + Set dependencies = repackageMojo.getSourceDependencies(); + for (Artifact artifact : dependencies) { + if(filterArtifact(artifact)){ + continue; + } + File artifactFile = artifact.getFile(); + String targetFilePath = FilesUtils.joiningFilePath( + mainConfig.getOutputDirectory(), PackageStructure.LIB_NAME, artifactFile.getName()); + + FileUtils.copyFile(artifactFile, new File(targetFilePath)); + dependenciesName.add(PackageStructure.LIB_NAME + "/" + artifactFile.getName()); + } + } + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/MainConfig.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/MainConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..8c5e58ba724b54965d8b9785ff2b29f0e55eec8b --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/MainConfig.java @@ -0,0 +1,57 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.main; + + +import lombok.Data; +import org.apache.maven.plugins.annotations.Parameter; +import com.gitee.starblues.plugin.pack.Constant; + +/** + * 主程序打包配置 + * @author starBlues + * @version 3.0.0 + */ +@Data +public class MainConfig { + + /** + * 主启动类 + */ + @Parameter(required = true) + private String mainClass; + + /** + * 打包类型。默认:jar + * + * {@link com.gitee.starblues.common.PackageType#MAIN_PACKAGE_TYPE_JAR} + * {@link com.gitee.starblues.common.PackageType#MAIN_PACKAGE_TYPE_JAR_OUTER} + */ + private String packageType; + + /** + * 文件名称。默认 artifactId-version-repackage + */ + private String fileName; + + /** + * 输出文件目录。默认target + */ + private String outputDirectory; + + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/MainRepackager.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/MainRepackager.java new file mode 100644 index 0000000000000000000000000000000000000000..a32d278c951951c0198ccb293ed2ecffdf78f745 --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/MainRepackager.java @@ -0,0 +1,80 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.main; + +import com.gitee.starblues.common.PackageType; +import com.gitee.starblues.plugin.pack.RepackageMojo; +import com.gitee.starblues.plugin.pack.Repackager; +import com.gitee.starblues.utils.ObjectUtils; +import lombok.Getter; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.project.MavenProject; + +/** + * 主程序打包 + * @author starBlues + * @version 3.0.0 + */ +@Getter +public class MainRepackager implements Repackager { + + private final RepackageMojo repackageMojo; + private final MainConfig mainConfig; + + public MainRepackager(RepackageMojo repackageMojo) { + this.repackageMojo = repackageMojo; + this.mainConfig = repackageMojo.getMainConfig(); + } + + @Override + public void repackage() throws MojoExecutionException, MojoFailureException { + checkConfig(); + String packageType = mainConfig.getPackageType(); + if(PackageType.MAIN_PACKAGE_TYPE_JAR.equalsIgnoreCase(packageType)){ + new JarNestPackager(this).repackage(); + } else if(PackageType.MAIN_PACKAGE_TYPE_JAR_OUTER.equalsIgnoreCase(packageType)){ + new JarOuterPackager(this).repackage(); + } else { + throw new MojoFailureException("Not found packageType : " + packageType); + } + } + + private void checkConfig() throws MojoFailureException { + if(mainConfig == null){ + throw new MojoFailureException("configuration.mainConfig config cannot be empty"); + } + if(ObjectUtils.isEmpty(mainConfig.getMainClass())) { + throw new MojoFailureException("configuration.mainConfig.mainClass config cannot be empty"); + } + String fileName = mainConfig.getFileName(); + if(ObjectUtils.isEmpty(fileName)) { + MavenProject project = repackageMojo.getProject(); + mainConfig.setFileName(project.getArtifactId() + "-" + project.getVersion() + "-repackage"); + } + String packageType = mainConfig.getPackageType(); + if(ObjectUtils.isEmpty(packageType)) { + mainConfig.setPackageType(PackageType.MAIN_PACKAGE_TYPE_JAR); + } + String outputDirectory = mainConfig.getOutputDirectory(); + if(ObjectUtils.isEmpty(outputDirectory)){ + mainConfig.setOutputDirectory(repackageMojo.getOutputDirectory().getPath()); + } + } + + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/DirProdRepackager.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/DirProdRepackager.java new file mode 100644 index 0000000000000000000000000000000000000000..bc06e6281e2690a84b0808c0490f3f31f086cc1f --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/DirProdRepackager.java @@ -0,0 +1,157 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.prod; + +import com.gitee.starblues.common.ManifestKey; +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.common.PackageType; +import com.gitee.starblues.common.PluginDescriptorKey; +import com.gitee.starblues.plugin.pack.RepackageMojo; +import com.gitee.starblues.plugin.pack.dev.DevRepackager; +import com.gitee.starblues.plugin.pack.utils.CommonUtils; +import com.gitee.starblues.utils.FilesUtils; +import org.apache.commons.io.FileUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import static com.gitee.starblues.common.PackageStructure.*; + +/** + * 文件夹包生成 + * @author starBlues + * @version 3.0.0 + */ +public class DirProdRepackager extends DevRepackager { + + protected final ProdConfig prodConfig; + + + public DirProdRepackager(RepackageMojo repackageMojo, ProdConfig prodConfig) { + super(repackageMojo); + this.prodConfig = prodConfig; + } + + @Override + public void repackage() throws MojoExecutionException, MojoFailureException { + super.repackage(); + try { + resolveClasses(); + } catch (Exception e) { + repackageMojo.getLog().error(e.getMessage(), e); + throw new MojoFailureException(e); + } + } + + @Override + protected String createRootDir() throws MojoFailureException { + String fileName = prodConfig.getFileName(); + String dirPath = FilesUtils.joiningFilePath(prodConfig.getOutputDirectory(), fileName); + File dirFile = new File(dirPath); + if(dirFile.exists() && dirFile.isFile()){ + int i = 0; + while (true){ + dirFile = new File(dirPath + "_" + i); + if(dirFile.exists() && dirFile.isFile()){ + i = i + 1; + continue; + } + break; + } + } + dirFile.deleteOnExit(); + if(!dirFile.mkdirs()){ + throw new MojoFailureException("Create package dir failure: " + dirFile.getPath()); + } + return dirFile.getPath(); + } + + @Override + protected String getRelativeManifestPath() { + return FilesUtils.joiningFilePath(META_INF_NAME, MANIFEST); + } + + @Override + protected String getRelativePluginMetaPath() { + return FilesUtils.joiningFilePath(META_INF_NAME, PLUGIN_META_NAME); + } + + @Override + protected String getRelativeResourcesDefinePath() { + return FilesUtils.joiningFilePath(META_INF_NAME, RESOURCES_DEFINE_NAME); + } + + @Override + protected Manifest getManifest() throws Exception { + Manifest manifest = super.getManifest(); + Attributes attributes = manifest.getMainAttributes(); + attributes.putValue(ManifestKey.PLUGIN_META_PATH, PROD_PLUGIN_META_PATH); + attributes.putValue(ManifestKey.PLUGIN_PACKAGE_TYPE, PackageType.PLUGIN_PACKAGE_TYPE_ZIP_OUTER); + return manifest; + } + + @Override + protected Properties createPluginMetaInfo() throws Exception { + Properties properties = super.createPluginMetaInfo(); + properties.put(PluginDescriptorKey.PLUGIN_PATH, CLASSES_NAME); + properties.put(PluginDescriptorKey.PLUGIN_RESOURCES_CONFIG, PROD_RESOURCES_DEFINE_PATH); + return properties; + } + + protected void resolveClasses() throws Exception { + String buildDir = repackageMojo.getProject().getBuild().getOutputDirectory(); + String path = FilesUtils.joiningFilePath(getRootDir(), CLASSES_NAME); + File file = new File(path); + FileUtils.forceMkdir(file); + FileUtils.copyDirectory(new File(buildDir), file); + } + + @Override + protected Set getDependenciesIndexSet() throws Exception { + Set dependencies = repackageMojo.getFilterDependencies(); + String libDir = createLibDir(); + Set dependencyIndexNames = new HashSet<>(dependencies.size()); + for (Artifact artifact : dependencies) { + if(filterArtifact(artifact)){ + continue; + } + File artifactFile = artifact.getFile(); + FileUtils.copyFile(artifactFile, new File(FilesUtils.joiningFilePath(libDir, artifactFile.getName()))); + dependencyIndexNames.add(PackageStructure.PROD_LIB_PATH + artifactFile.getName() + + repackageMojo.resolveLoadToMain(artifact)); + } + return dependencyIndexNames; + } + + protected String createLibDir() throws IOException { + String dir = FilesUtils.joiningFilePath(getRootDir(), PackageStructure.LIB_NAME); + File file = new File(dir); + if(file.mkdir()){ + return dir; + } + throw new IOException("Create " + PackageStructure.LIB_NAME + " dir failure"); + } + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/JarNestedProdRepackager.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/JarNestedProdRepackager.java new file mode 100644 index 0000000000000000000000000000000000000000..7fc9cf4a9369ce88a707a57b94231ec5c6d06be6 --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/JarNestedProdRepackager.java @@ -0,0 +1,70 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.prod; + +import com.gitee.starblues.common.ManifestKey; +import com.gitee.starblues.common.PackageType; +import com.gitee.starblues.common.PluginDescriptorKey; +import com.gitee.starblues.plugin.pack.RepackageMojo; +import com.gitee.starblues.plugin.pack.utils.PackageJar; +import com.gitee.starblues.plugin.pack.utils.PackageZip; +import org.apache.commons.compress.archivers.ArchiveOutputStream; +import org.apache.commons.compress.archivers.jar.JarArchiveEntry; +import org.apache.commons.compress.archivers.jar.JarArchiveOutputStream; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + +import java.io.*; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import static com.gitee.starblues.common.PackageStructure.PROD_CLASSES_PATH; +import static com.gitee.starblues.common.PackageStructure.PROD_RESOURCES_DEFINE_PATH; + +/** + * jar包生成 + * @author starBlues + * @version 3.0.0 + */ +public class JarNestedProdRepackager extends ZipProdRepackager { + + + public JarNestedProdRepackager(RepackageMojo repackageMojo, ProdConfig prodConfig) { + super(repackageMojo, prodConfig); + } + + @Override + public void repackage() throws MojoExecutionException, MojoFailureException { + super.repackage(); + } + + @Override + protected PackageZip getPackageZip() throws Exception { + return new PackageJar(prodConfig.getOutputDirectory(), prodConfig.getFileName()); + } + + @Override + protected Manifest getManifest() throws Exception { + Manifest manifest = super.getManifest(); + manifest.getMainAttributes().putValue( + ManifestKey.PLUGIN_PACKAGE_TYPE, PackageType.PLUGIN_PACKAGE_TYPE_JAR); + return manifest; + } + + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/JarOuterProdRepackager.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/JarOuterProdRepackager.java new file mode 100644 index 0000000000000000000000000000000000000000..0132c3bbbe930585729f2512ab1ee8aad744082b --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/JarOuterProdRepackager.java @@ -0,0 +1,53 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.prod; + +import com.gitee.starblues.common.ManifestKey; +import com.gitee.starblues.common.PackageType; +import com.gitee.starblues.common.PluginDescriptorKey; +import com.gitee.starblues.plugin.pack.RepackageMojo; +import com.gitee.starblues.plugin.pack.utils.PackageJar; +import com.gitee.starblues.plugin.pack.utils.PackageZip; + +import java.util.jar.Manifest; + +/** + * jar-outer包生成 + * @author starBlues + * @version 3.0.0 + */ +public class JarOuterProdRepackager extends ZipOuterProdRepackager { + + + public JarOuterProdRepackager(RepackageMojo repackageMojo, ProdConfig prodConfig) { + super(repackageMojo, prodConfig); + } + + @Override + protected PackageZip getPackageZip(String rootDir) throws Exception { + return new PackageJar(rootDir, super.prodConfig.getFileName()); + } + + @Override + protected Manifest getManifest() throws Exception { + Manifest manifest = super.getManifest(); + manifest.getMainAttributes().putValue(ManifestKey.PLUGIN_PACKAGE_TYPE, + PackageType.PLUGIN_PACKAGE_TYPE_JAR_OUTER); + return manifest; + } + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ProdConfig.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ProdConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..3943b38a23dcaac1410b19cc347db98fbfd7fdb6 --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ProdConfig.java @@ -0,0 +1,53 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.prod; + + +import lombok.Data; +import org.apache.maven.plugins.annotations.Parameter; + +/** + * 生产环境打包配置 + * @author starBlues + * @version 3.0.0 + */ +@Data +public class ProdConfig { + + /** + * 打包类型。默认jar包 + * + * {@link com.gitee.starblues.common.PackageType#PLUGIN_PACKAGE_TYPE_JAR} + * {@link com.gitee.starblues.common.PackageType#PLUGIN_PACKAGE_TYPE_JAR_OUTER} + * {@link com.gitee.starblues.common.PackageType#PLUGIN_PACKAGE_TYPE_ZIP} + * {@link com.gitee.starblues.common.PackageType#PLUGIN_PACKAGE_TYPE_ZIP_OUTER} + * {@link com.gitee.starblues.common.PackageType#PLUGIN_PACKAGE_TYPE_DIR} + */ + @Parameter(required = true, defaultValue = "jar") + private String packageType = "jar"; + + /** + * 文件名称。默认 pluginId-version-repackage + */ + private String fileName; + + /** + * 输出文件目录。默认target + */ + private String outputDirectory; + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ProdRepackager.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ProdRepackager.java new file mode 100644 index 0000000000000000000000000000000000000000..9db3b18386ec7bdf91e7b22fa4c0ecc255b23a27 --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ProdRepackager.java @@ -0,0 +1,94 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.prod; + +import com.gitee.starblues.common.PackageType; +import com.gitee.starblues.plugin.pack.Constant; +import com.gitee.starblues.plugin.pack.PluginInfo; +import com.gitee.starblues.plugin.pack.RepackageMojo; +import com.gitee.starblues.plugin.pack.Repackager; +import com.gitee.starblues.plugin.pack.dev.DevRepackager; +import com.gitee.starblues.utils.ObjectUtils; +import lombok.Getter; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + + +/** + * 生产环境打包 + * @author starBlues + * @version 3.0.0 + */ +public class ProdRepackager implements Repackager { + + @Getter + private ProdConfig prodConfig; + + private final RepackageMojo repackageMojo; + private final Repackager repackager; + + public ProdRepackager(RepackageMojo repackageMojo) { + this.repackageMojo = repackageMojo; + this.repackager = new DevRepackager(repackageMojo); + } + + @Override + public void repackage() throws MojoExecutionException, MojoFailureException { + repackager.repackage(); + this.prodConfig = getProdConfig(repackageMojo); + String packageType = prodConfig.getPackageType(); + Repackager repackager = null; + + if(PackageType.PLUGIN_PACKAGE_TYPE_ZIP.equalsIgnoreCase(packageType)){ + repackager = new ZipProdRepackager(repackageMojo, prodConfig); + } else if(PackageType.PLUGIN_PACKAGE_TYPE_JAR.equalsIgnoreCase(packageType)){ + repackager = new JarNestedProdRepackager(repackageMojo, prodConfig); + } else if(PackageType.PLUGIN_PACKAGE_TYPE_ZIP_OUTER.equalsIgnoreCase(packageType)){ + repackager = new ZipOuterProdRepackager(repackageMojo, prodConfig); + } else if(PackageType.PLUGIN_PACKAGE_TYPE_JAR_OUTER.equalsIgnoreCase(packageType)){ + repackager = new JarOuterProdRepackager(repackageMojo, prodConfig); + } else if(PackageType.PLUGIN_PACKAGE_TYPE_DIR.equalsIgnoreCase(packageType)){ + repackager = new DirProdRepackager(repackageMojo, prodConfig); + } else { + throw new MojoFailureException("Not found packageType : " + packageType); + } + repackager.repackage(); + } + + protected ProdConfig getProdConfig(RepackageMojo repackageMojo){ + ProdConfig prodConfig = repackageMojo.getProdConfig(); + if(prodConfig == null){ + prodConfig = new ProdConfig(); + } + if(ObjectUtils.isEmpty(prodConfig.getPackageType())){ + prodConfig.setPackageType(PackageType.PLUGIN_PACKAGE_TYPE_JAR); + } + String fileName = prodConfig.getFileName(); + if(ObjectUtils.isEmpty(fileName)) { + PluginInfo pluginInfo = repackageMojo.getPluginInfo(); + prodConfig.setFileName(pluginInfo.getId() + "-" + pluginInfo.getVersion() + "-repackage"); + } + String outputDirectory = prodConfig.getOutputDirectory(); + if(ObjectUtils.isEmpty(outputDirectory)){ + prodConfig.setOutputDirectory(repackageMojo.getOutputDirectory().getPath()); + } + return prodConfig; + } + + + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ZipOuterProdRepackager.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ZipOuterProdRepackager.java new file mode 100644 index 0000000000000000000000000000000000000000..d3938924ebbfb6d5b1b9c5f1c22124a6d391a37d --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ZipOuterProdRepackager.java @@ -0,0 +1,103 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.prod; + +import com.gitee.starblues.common.ManifestKey; +import com.gitee.starblues.common.PackageType; +import com.gitee.starblues.common.PluginDescriptorKey; +import com.gitee.starblues.plugin.pack.RepackageMojo; +import com.gitee.starblues.plugin.pack.utils.PackageJar; +import com.gitee.starblues.plugin.pack.utils.PackageZip; +import com.gitee.starblues.utils.FilesUtils; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + +import java.io.File; +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import static com.gitee.starblues.common.PackageStructure.*; + +/** + * zip-outer 包生成 + * @author starBlues + * @version 3.0.0 + */ +public class ZipOuterProdRepackager extends DirProdRepackager { + + protected PackageZip packageZip; + + public ZipOuterProdRepackager(RepackageMojo repackageMojo, ProdConfig prodConfig) { + super(repackageMojo, prodConfig); + } + + @Override + public void repackage() throws MojoExecutionException, MojoFailureException { + try { + super.repackage(); + } catch (Exception e){ + throw new MojoFailureException(e); + } finally { + if(packageZip != null){ + IOUtils.closeQuietly(packageZip); + } + } + } + + @Override + protected String createRootDir() throws MojoFailureException { + String rootDir = super.createRootDir(); + try { + packageZip = getPackageZip(rootDir); + return rootDir; + } catch (Exception e) { + throw new MojoFailureException(e); + } + } + + protected PackageZip getPackageZip(String rootDir) throws Exception { + return new PackageZip(rootDir, super.prodConfig.getFileName()); + } + + @Override + protected void resolveClasses() throws Exception { + String buildDir = repackageMojo.getProject().getBuild().getOutputDirectory(); + packageZip.copyDirToPackage(new File(buildDir), ""); + } + + @Override + protected Manifest getManifest() throws Exception { + Manifest manifest = super.getManifest(); + Attributes attributes = manifest.getMainAttributes(); + attributes.putValue(ManifestKey.PLUGIN_META_PATH, PROD_PLUGIN_META_PATH); + attributes.putValue(ManifestKey.PLUGIN_PACKAGE_TYPE, PackageType.PLUGIN_PACKAGE_TYPE_ZIP_OUTER); + return manifest; + } + + @Override + protected Properties createPluginMetaInfo() throws Exception { + Properties properties = super.createPluginMetaInfo(); + properties.put(PluginDescriptorKey.PLUGIN_PATH, packageZip.getFileName()); + return properties; + } +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ZipProdRepackager.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ZipProdRepackager.java new file mode 100644 index 0000000000000000000000000000000000000000..75f005303110b9fe0e4e577fdc597a8cbf4d125a --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ZipProdRepackager.java @@ -0,0 +1,191 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.prod; + +import com.gitee.starblues.common.ManifestKey; +import com.gitee.starblues.common.PackageType; +import com.gitee.starblues.common.PluginDescriptorKey; +import com.gitee.starblues.plugin.pack.Constant; +import com.gitee.starblues.plugin.pack.RepackageMojo; +import com.gitee.starblues.plugin.pack.dev.Dependency; +import com.gitee.starblues.plugin.pack.dev.DevConfig; +import com.gitee.starblues.plugin.pack.dev.DevRepackager; +import com.gitee.starblues.plugin.pack.utils.PackageZip; +import com.gitee.starblues.utils.FilesUtils; +import com.gitee.starblues.utils.ObjectUtils; +import org.apache.commons.compress.archivers.ArchiveOutputStream; +import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import static com.gitee.starblues.common.PackageStructure.*; + +/** + * zip 打包 + * @author starBlues + * @version 3.0.0 + */ +public class ZipProdRepackager extends DevRepackager { + + + protected final ProdConfig prodConfig; + + protected PackageZip packageZip; + + public ZipProdRepackager(RepackageMojo repackageMojo, ProdConfig prodConfig) { + super(repackageMojo); + this.prodConfig = prodConfig; + } + + @Override + public void repackage() throws MojoExecutionException, MojoFailureException { + try { + packageZip = getPackageZip(); + super.repackage(); + resolveClasses(); + resolveResourcesDefine(); + String rootDir = getRootDir(); + try { + FileUtils.deleteDirectory(new File(rootDir)); + } catch (IOException e) { + // 忽略 + } + repackageMojo.getLog().info("Success package prod zip file : " + + packageZip.getFile().getPath()); + } catch (Exception e){ + repackageMojo.getLog().error(e.getMessage(), e); + throw new MojoFailureException(e); + } finally { + if(packageZip != null){ + IOUtils.closeQuietly(packageZip); + } + } + } + + protected PackageZip getPackageZip() throws Exception { + return new PackageZip(prodConfig.getOutputDirectory(), prodConfig.getFileName()); + } + + @Override + protected String getBasicRootDir(){ + File outputDirectory = repackageMojo.getOutputDirectory(); + return FilesUtils.joiningFilePath(outputDirectory.getPath(), UUID.randomUUID().toString()); + } + + @Override + protected Map getModuleDependencies(DevConfig devConfig) { + // 将项目中模块依赖置为空 + return Collections.emptyMap(); + } + + @Override + protected String getPluginPath() { + return CLASSES_NAME + SEPARATOR; + } + + @Override + protected String getLibIndex(Artifact artifact){ + return PROD_LIB_PATH + artifact.getFile().getName() + repackageMojo.resolveLoadToMain(artifact); + } + + @Override + protected boolean filterArtifact(Artifact artifact) { + return Constant.scopeFilter(artifact.getScope()); + } + + protected void resolveClasses() throws Exception { + String buildDir = repackageMojo.getProject().getBuild().getOutputDirectory(); + packageZip.copyDirToPackage(new File(buildDir), null); + } + + @Override + protected Manifest getManifest() throws Exception { + Manifest manifest = super.getManifest(); + Attributes attributes = manifest.getMainAttributes(); + attributes.putValue(ManifestKey.PLUGIN_META_PATH, PROD_PLUGIN_META_PATH); + attributes.putValue(ManifestKey.PLUGIN_PACKAGE_TYPE, PackageType.PLUGIN_PACKAGE_TYPE_ZIP); + return manifest; + } + + @Override + protected Properties createPluginMetaInfo() throws Exception { + Properties properties = super.createPluginMetaInfo(); + properties.put(PluginDescriptorKey.PLUGIN_RESOURCES_CONFIG, PROD_RESOURCES_DEFINE_PATH); + return properties; + } + + @Override + protected void writeManifest(Manifest manifest) throws Exception { + packageZip.writeManifest(manifest); + } + + @Override + protected String writePluginMetaInfo(Properties properties) throws Exception { + packageZip.write(PROD_PLUGIN_META_PATH, outputStream->{ + properties.store(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8), + Constant.PLUGIN_METE_COMMENTS); + }); + return PROD_PLUGIN_META_PATH; + } + + protected void resolveResourcesDefine() throws Exception{ + Set dependencyIndexNames = resolveDependencies(); + StringBuilder content = new StringBuilder(); + content.append(RESOURCES_DEFINE_DEPENDENCIES).append("\n"); + for (String dependencyIndexName : dependencyIndexNames) { + content.append(dependencyIndexName).append("\n"); + } + String loadMainResources = super.getLoadMainResources(); + if(!ObjectUtils.isEmpty(loadMainResources)){ + content.append(loadMainResources).append("\n"); + } + final byte[] bytes = content.toString().getBytes(StandardCharsets.UTF_8); + try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes)){ + packageZip.putInputStreamEntry(PROD_RESOURCES_DEFINE_PATH, byteArrayInputStream); + } + } + + protected Set resolveDependencies() throws Exception { + Set dependencies = repackageMojo.getFilterDependencies(); + String libDirEntryName = createLibEntry(); + Set dependencyIndexNames = new HashSet<>(dependencies.size()); + for (Artifact artifact : dependencies) { + if(filterArtifact(artifact)){ + continue; + } + String dependencyIndexName = packageZip.writeDependency(artifact.getFile(), libDirEntryName); + dependencyIndexNames.add(dependencyIndexName); + } + return dependencyIndexNames; + } + + protected String createLibEntry() throws Exception { + String libDirEntryName = PROD_LIB_PATH; + packageZip.putDirEntry(libDirEntryName); + return libDirEntryName; + } + +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/utils/CommonUtils.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/utils/CommonUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..f9d722906ba44e311dc1e7600c489cd1782e14c5 --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/utils/CommonUtils.java @@ -0,0 +1,51 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.utils; + +import com.gitee.starblues.plugin.pack.filter.Exclude; +import org.apache.maven.artifact.Artifact; + +import java.util.Objects; + +/** + * Object 工具类 + * @author starBlues + * @version 3.0.0 + */ +public class CommonUtils { + + public final static String PLUGIN_FRAMEWORK_GROUP_ID = "com.gitee.starblues"; + public final static String PLUGIN_FRAMEWORK_ARTIFACT_ID = "springboot-plugin-framework"; + + public final static String PLUGIN_FRAMEWORK_LOADER_ARTIFACT_ID = "springboot-plugin-framework-loader"; + + private CommonUtils(){} + + public static Exclude getPluginFrameworkExclude(){ + return Exclude.get(PLUGIN_FRAMEWORK_GROUP_ID, PLUGIN_FRAMEWORK_ARTIFACT_ID); + } + + public static boolean isPluginFramework(Artifact artifact){ + return Objects.equals(artifact.getGroupId(), PLUGIN_FRAMEWORK_GROUP_ID) + && Objects.equals(artifact.getArtifactId(), PLUGIN_FRAMEWORK_ARTIFACT_ID); + } + + public static boolean isPluginFrameworkLoader(Artifact artifact){ + return Objects.equals(artifact.getGroupId(), PLUGIN_FRAMEWORK_GROUP_ID) + && Objects.equals(artifact.getArtifactId(), PLUGIN_FRAMEWORK_LOADER_ARTIFACT_ID); + } +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/utils/PackageJar.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/utils/PackageJar.java new file mode 100644 index 0000000000000000000000000000000000000000..d82a8f387ef9e87823e4518244dcd9b45e96a713 --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/utils/PackageJar.java @@ -0,0 +1,55 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.utils; + +import org.apache.commons.compress.archivers.ArchiveOutputStream; +import org.apache.commons.compress.archivers.jar.JarArchiveEntry; +import org.apache.commons.compress.archivers.jar.JarArchiveOutputStream; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; + +import java.io.File; +import java.io.FileOutputStream; + +/** + * jar 打包工具 + * @author starBlues + * @version 3.0.0 + */ +public class PackageJar extends PackageZip{ + public PackageJar(File file) throws Exception { + super(file); + } + + public PackageJar(String outputDirectory, String packageName) throws Exception { + super(outputDirectory, packageName); + } + + @Override + protected String getPackageFileSuffix() { + return "jar"; + } + + @Override + protected ArchiveOutputStream getOutputStream(File packFile) throws Exception { + return new JarArchiveOutputStream(new FileOutputStream(packFile)); + } + + @Override + protected ZipArchiveEntry getArchiveEntry(String name) { + return new JarArchiveEntry(name); + } +} diff --git a/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/utils/PackageZip.java b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/utils/PackageZip.java new file mode 100644 index 0000000000000000000000000000000000000000..20d3931ab04d140df7811bd2aef6a89ff157d465 --- /dev/null +++ b/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/utils/PackageZip.java @@ -0,0 +1,251 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.plugin.pack.utils; + +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.utils.FilesUtils; +import com.gitee.starblues.utils.ObjectUtils; +import org.apache.commons.compress.archivers.ArchiveOutputStream; +import org.apache.commons.compress.archivers.zip.UnixStat; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; +import org.apache.commons.compress.archivers.zip.ZipFile; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; + +import java.io.*; +import java.util.Enumeration; +import java.util.jar.Manifest; +import java.util.zip.CRC32; +import java.util.zip.ZipEntry; + +import static com.gitee.starblues.common.PackageStructure.*; + +/** + * zip 打包工具 + * @author starBlues + * @version 3.0.0 + */ +public class PackageZip implements Closeable{ + + + private static final int UNIX_FILE_MODE = UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM; + + private static final int UNIX_DIR_MODE = UnixStat.DIR_FLAG | UnixStat.DEFAULT_DIR_PERM; + + private final File file; + private final ArchiveOutputStream outputStream; + + + public PackageZip(File file) throws Exception { + this.file = file; + this.outputStream = getOutputStream(file); + } + + public PackageZip(String outputDirectory, String packageName) throws Exception{ + String rootPath = FilesUtils.joiningFilePath(outputDirectory, packageName); + this.file = getPackageFile(rootPath); + this.outputStream = getOutputStream(file); + } + + public File getFile(){ + return file; + } + + public String getFileName(){ + return file.getName(); + } + + protected File getPackageFile(String rootPath) throws Exception { + String fileSuffix = getPackageFileSuffix(); + File file = new File(rootPath + "." + fileSuffix); + + if(file.exists()){ + int i = 0; + while (true){ + file = new File(rootPath + "_" + i + "." + fileSuffix); + if(file.exists()){ + i = i + 1; + continue; + } + break; + } + } + if(file.createNewFile()){ + return file; + } + throw new IOException("Create file '" + file.getPath() + "' failure."); + } + + protected String getPackageFileSuffix(){ + return "zip"; + } + + protected ArchiveOutputStream getOutputStream(File packFile) throws Exception { + return new ZipArchiveOutputStream(new FileOutputStream(packFile)); + } + + public void copyDirToPackage(File rootDir, String packageDir) throws Exception { + if(packageDir == null){ + packageDir = rootDir.getName(); + } + if (rootDir.isDirectory()) { + File[] childFiles = rootDir.listFiles(); + if(ObjectUtils.isEmpty(packageDir)){ + packageDir = ""; + } else { + packageDir = packageDir + "/"; + putDirEntry(packageDir); + } + if(childFiles == null){ + return; + } + for (File childFile : childFiles) { + copyDirToPackage(childFile, packageDir + childFile.getName()); + } + } else { + putFileEntry(rootDir, packageDir); + } + } + + public void copyZipToPackage(File sourceZipFile) throws Exception { + if(sourceZipFile == null || !sourceZipFile.exists()){ + return; + } + try (ZipFile zipFile = new ZipFile(sourceZipFile)){ + Enumeration entries = zipFile.getEntries(); + while (entries.hasMoreElements()){ + ZipArchiveEntry zipArchiveEntry = entries.nextElement(); + String name = zipArchiveEntry.getName(); + if(name.contains(PackageStructure.META_INF_NAME)){ + // 不拷贝 mate-inf + continue; + } + if(zipArchiveEntry.isDirectory()){ + putDirEntry(name); + } else { + try (InputStream inputStream = zipFile.getInputStream(zipArchiveEntry)){ + putInputStreamEntry(name, inputStream); + } + } + } + } + } + + public String writeDependency(File dependencyFile, String libDirEntryName) throws Exception { + String indexName = libDirEntryName + dependencyFile.getName(); + ZipArchiveEntry entry = getArchiveEntry(indexName); + entry.setTime(System.currentTimeMillis()); + entry.setUnixMode(indexName.endsWith("/") ? UNIX_DIR_MODE : UNIX_FILE_MODE); + entry.getGeneralPurposeBit().useUTF8ForNames(true); + try(FileInputStream inputStream = new FileInputStream(dependencyFile)){ + new CrcAndSize(inputStream).setupStoredEntry(entry); + } + try (FileInputStream inputStream = new FileInputStream(dependencyFile)){ + outputStream.putArchiveEntry(entry); + IOUtils.copy(inputStream, outputStream); + outputStream.closeArchiveEntry(); + } + return indexName; + } + + public void putFileEntry(File destFile, String rootDir) throws Exception { + if(!destFile.exists()){ + throw new FileNotFoundException("Not found file : " + destFile.getPath()); + } + outputStream.putArchiveEntry(getArchiveEntry(rootDir)); + FileUtils.copyFile(destFile, outputStream); + outputStream.closeArchiveEntry(); + } + + public void putInputStreamEntry(String name, InputStream inputStream) throws Exception { + outputStream.putArchiveEntry(getArchiveEntry(name)); + IOUtils.copy(inputStream, outputStream); + outputStream.closeArchiveEntry(); + } + + public void write(String name, Writer writer) throws Exception { + outputStream.putArchiveEntry(getArchiveEntry(name)); + writer.write(outputStream); + outputStream.closeArchiveEntry(); + } + + public void write(String name, File file) throws Exception { + outputStream.putArchiveEntry(getArchiveEntry(name)); + try (FileInputStream fileInputStream = new FileInputStream(file)){ + IOUtils.copy(fileInputStream, outputStream); + outputStream.closeArchiveEntry(); + } + } + + public void writeManifest(Manifest manifest) throws Exception { + putDirEntry(META_INF_NAME + SEPARATOR); + write(PROD_MANIFEST_PATH, manifest::write); + } + + public void putDirEntry(String dir) throws IOException { + outputStream.putArchiveEntry(getArchiveEntry(dir)); + outputStream.closeArchiveEntry(); + } + + protected ZipArchiveEntry getArchiveEntry(String name){ + return new ZipArchiveEntry(name); + } + + + @Override + public void close() throws IOException { + outputStream.finish(); + outputStream.close(); + } + + private static class CrcAndSize { + + private static final int BUFFER_SIZE = 32 * 1024; + + private final CRC32 crc = new CRC32(); + + private long size; + + CrcAndSize(InputStream inputStream) throws IOException { + load(inputStream); + } + + private void load(InputStream inputStream) throws IOException { + byte[] buffer = new byte[BUFFER_SIZE]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + this.crc.update(buffer, 0, bytesRead); + this.size += bytesRead; + } + } + + void setupStoredEntry(ZipArchiveEntry entry) { + entry.setSize(this.size); + entry.setCompressedSize(this.size); + entry.setCrc(this.crc.getValue()); + entry.setMethod(ZipEntry.STORED); + } + + } + + @FunctionalInterface + public interface Writer{ + void write(ArchiveOutputStream outputStream) throws Exception; + } + +} diff --git a/spring-brick-maven-packager/src/main/resources/META-INF/maven/com.gitee.starblues.springboot-plugin-maven-packager/plugin-help.xml b/spring-brick-maven-packager/src/main/resources/META-INF/maven/com.gitee.starblues.springboot-plugin-maven-packager/plugin-help.xml new file mode 100644 index 0000000000000000000000000000000000000000..b3fd6271d4fe96579d2303ea62cfdea7e0deda74 --- /dev/null +++ b/spring-brick-maven-packager/src/main/resources/META-INF/maven/com.gitee.starblues.springboot-plugin-maven-packager/plugin-help.xml @@ -0,0 +1,160 @@ + + + + + + Spring Boot Plugin Maven Packager + com.gitee.starblues + spring-brick-maven-packager + 3.0.0 + spring-brick-packager + false + true + + + repackage + Repackage existing JAR and WAR archives so that they can be executed from the command + line using {@literal java -jar}. With <code>layout=NONE</code> can also be used simply + to package a JAR with nested dependencies (and no main class, so not executable). + compile+runtime + false + true + false + false + false + true + package + com.gitee.starblues.plugin.pack.RepackageMojo + java + per-lookup + once-per-session + 1.0.0 + compile+runtime + true + + + project + org.apache.maven.project.MavenProject + 1.0.0 + true + false + The Maven project. + + + outputDirectory + java.io.File + 1.0.0 + true + true + Directory containing the generated archive. + + + includes + java.util.List + 1.2.0 + false + true + Collection of artifact definitions to include. The {@link Include} element defines + mandatory {@code groupId} and {@code artifactId} properties and an optional + mandatory {@code groupId} and {@code artifactId} properties and an optional + {@code classifier} property. + + + excludes + java.util.List + 1.1.0 + false + true + Collection of artifact definitions to exclude. The {@link Exclude} element defines + mandatory {@code groupId} and {@code artifactId} properties and an optional + {@code classifier} property. + + + skip + boolean + 1.2.0 + false + true + Skip the execution. + + + mode + string + 3.0.0 + true + true + 打包模式: dev/prod ,默认为dev + + + pluginInfo + com.gitee.starblues.plugin.pack.PluginInfo + 3.0.0 + false + true + 插件信息 + + + loadMainResourcePattern + com.gitee.starblues.plugin.pack.LoadMainResourcePattern + 3.0.0 + false + true + 从主程序加载资源的定义 + + + devConfig + com.gitee.starblues.plugin.pack.dev.DevConfig + 3.0.0 + false + true + dev打包模式配置 + + + prodConfig + com.gitee.starblues.plugin.pack.prod.ProdConfig + 3.0.0 + false + true + prod打包模式配置 + + + mainConfig + com.gitee.starblues.plugin.pack.main.MainConfig + 3.0.0 + false + true + main打包模式配置 + + + loadToMain + com.gitee.starblues.plugin.pack.LoadToMain + 3.0.0 + false + true + 加载到主程序的依赖 + + + + + + + + + + ${springboot-plugin.includes} + ${springboot-plugin.excludes} + + + + + + + + org.apache.maven.project.MavenProjectHelper + projectHelper + + + + + + \ No newline at end of file diff --git a/spring-brick-maven-packager/src/main/resources/META-INF/maven/plugin.xml b/spring-brick-maven-packager/src/main/resources/META-INF/maven/plugin.xml new file mode 100644 index 0000000000000000000000000000000000000000..b3fd6271d4fe96579d2303ea62cfdea7e0deda74 --- /dev/null +++ b/spring-brick-maven-packager/src/main/resources/META-INF/maven/plugin.xml @@ -0,0 +1,160 @@ + + + + + + Spring Boot Plugin Maven Packager + com.gitee.starblues + spring-brick-maven-packager + 3.0.0 + spring-brick-packager + false + true + + + repackage + Repackage existing JAR and WAR archives so that they can be executed from the command + line using {@literal java -jar}. With <code>layout=NONE</code> can also be used simply + to package a JAR with nested dependencies (and no main class, so not executable). + compile+runtime + false + true + false + false + false + true + package + com.gitee.starblues.plugin.pack.RepackageMojo + java + per-lookup + once-per-session + 1.0.0 + compile+runtime + true + + + project + org.apache.maven.project.MavenProject + 1.0.0 + true + false + The Maven project. + + + outputDirectory + java.io.File + 1.0.0 + true + true + Directory containing the generated archive. + + + includes + java.util.List + 1.2.0 + false + true + Collection of artifact definitions to include. The {@link Include} element defines + mandatory {@code groupId} and {@code artifactId} properties and an optional + mandatory {@code groupId} and {@code artifactId} properties and an optional + {@code classifier} property. + + + excludes + java.util.List + 1.1.0 + false + true + Collection of artifact definitions to exclude. The {@link Exclude} element defines + mandatory {@code groupId} and {@code artifactId} properties and an optional + {@code classifier} property. + + + skip + boolean + 1.2.0 + false + true + Skip the execution. + + + mode + string + 3.0.0 + true + true + 打包模式: dev/prod ,默认为dev + + + pluginInfo + com.gitee.starblues.plugin.pack.PluginInfo + 3.0.0 + false + true + 插件信息 + + + loadMainResourcePattern + com.gitee.starblues.plugin.pack.LoadMainResourcePattern + 3.0.0 + false + true + 从主程序加载资源的定义 + + + devConfig + com.gitee.starblues.plugin.pack.dev.DevConfig + 3.0.0 + false + true + dev打包模式配置 + + + prodConfig + com.gitee.starblues.plugin.pack.prod.ProdConfig + 3.0.0 + false + true + prod打包模式配置 + + + mainConfig + com.gitee.starblues.plugin.pack.main.MainConfig + 3.0.0 + false + true + main打包模式配置 + + + loadToMain + com.gitee.starblues.plugin.pack.LoadToMain + 3.0.0 + false + true + 加载到主程序的依赖 + + + + + + + + + + ${springboot-plugin.includes} + ${springboot-plugin.excludes} + + + + + + + + org.apache.maven.project.MavenProjectHelper + projectHelper + + + + + + \ No newline at end of file diff --git a/spring-brick/pom.xml b/spring-brick/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..e32cc26352fabe264d35cac840b23bc2f085fef3 --- /dev/null +++ b/spring-brick/pom.xml @@ -0,0 +1,117 @@ + + + 4.0.0 + + + spring-brick-parent + com.gitee.starblues + 3.0.0 + + + spring-brick + jar + + spring boot 插件式开发集成包 + + + 2.10.1 + 1.7.7 + 2.11.0 + + 5.3.12 + 2.5.6 + 3.0.12.RELEASE + 2.10.5 + 1.5.2 + 4.0.1 + 0.9.0 + 1.18.20 + 4.11 + + + + + com.gitee.starblues + spring-brick-common + ${project.version} + + + com.gitee.starblues + spring-brick-loader + ${project.version} + + + org.slf4j + slf4j-api + ${slf4j.version} + + + commons-io + commons-io + ${commons-io.version} + + + com.github.zafarkhaja + java-semver + ${java-semver.version} + + + org.springframework.boot + spring-boot + ${spring-boot.version} + provided + true + + + org.springframework + spring-webmvc + ${spring.version} + provided + true + + + org.thymeleaf + thymeleaf-spring5 + ${thymeleaf-spring5.version} + provided + true + + + javax.servlet + javax.servlet-api + ${javax.servlet-api.version} + provided + true + + + io.springfox + springfox-spring-web + ${swagger-spring-web.version} + provided + true + + + org.springdoc + springdoc-openapi-ui + ${springdoc.version} + provided + true + + + org.projectlombok + lombok + ${lombok.version} + provided + true + + + junit + junit + ${junit.version} + test + + + + \ No newline at end of file diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/annotation/Caller.java b/spring-brick/src/main/java/com/gitee/starblues/annotation/Caller.java similarity index 60% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/annotation/Caller.java rename to spring-brick/src/main/java/com/gitee/starblues/annotation/Caller.java index 6fed31b854d6bbf0be9540476e43ac2a0dcd35a1..49badbfa85c246a7c6a08e87c8780fe6627a3845 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/annotation/Caller.java +++ b/spring-brick/src/main/java/com/gitee/starblues/annotation/Caller.java @@ -1,14 +1,30 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.gitee.starblues.annotation; + +import java.lang.annotation.*; + /** * 调用者的注解。配合 @Supplier 注解使用, 两者结合实现插件中的方法调用。 * * @author starBlues * @version 2.4.0 */ - -import java.lang.annotation.*; - @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/annotation/Extract.java b/spring-brick/src/main/java/com/gitee/starblues/annotation/Extract.java similarity index 43% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/annotation/Extract.java rename to spring-brick/src/main/java/com/gitee/starblues/annotation/Extract.java index 05c57eb22b9edac0767651619799de32096fbce0..2e465b1ffc0ff386687379d20e697ba470f4aee7 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/annotation/Extract.java +++ b/spring-brick/src/main/java/com/gitee/starblues/annotation/Extract.java @@ -1,6 +1,23 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.gitee.starblues.annotation; -import com.gitee.starblues.utils.OrderPriority; +import org.springframework.core.annotation.AliasFor; +import org.springframework.stereotype.Component; import java.lang.annotation.*; @@ -12,8 +29,16 @@ import java.lang.annotation.*; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented +@Component public @interface Extract { + /** + * 指定 Component Bean 名称 + * @return component name + */ + @AliasFor(annotation = Component.class) + String value() default ""; + /** * 业务 * @return 业务标志 diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/annotation/Supplier.java b/spring-brick/src/main/java/com/gitee/starblues/annotation/Supplier.java similarity index 49% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/annotation/Supplier.java rename to spring-brick/src/main/java/com/gitee/starblues/annotation/Supplier.java index 04858f72af99d2b5dc4e3f2656a35f7af7abfe00..44959b2ea05bcc0a7668866f5bbaae65d148e3ab 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/annotation/Supplier.java +++ b/spring-brick/src/main/java/com/gitee/starblues/annotation/Supplier.java @@ -1,22 +1,43 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.gitee.starblues.annotation; +import org.springframework.core.annotation.AliasFor; +import org.springframework.stereotype.Component; + import java.lang.annotation.*; /** * 被调用类的提供者。配合 @Caller 注解使用, 两者结合实现插件中的方法调用。 * * @author starBlues - * @version 1.0 + * @version 2.4.0 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented +@Component public @interface Supplier { /** * 全局唯一key.全局不能重复 * @return String */ + @AliasFor(annotation = Component.class) String value(); /** diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/DefaultPluginInsideInfo.java b/spring-brick/src/main/java/com/gitee/starblues/core/DefaultPluginInsideInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..1f562254ce1aa67294c213104c5fff0755653256 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/DefaultPluginInsideInfo.java @@ -0,0 +1,108 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core; + +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.utils.Assert; + +import java.util.Date; + +/** + * 默认的内部PluginWrapperInside实现 + * @author starBlues + * @version 3.0.0 + */ +public class DefaultPluginInsideInfo implements PluginInsideInfo { + + private final String pluginId; + private final InsidePluginDescriptor pluginDescriptor; + private PluginState pluginState; + private boolean isFollowInitial = false; + + private Date startTime; + private Date stopTime; + + public DefaultPluginInsideInfo(InsidePluginDescriptor pluginDescriptor) { + this.pluginId = pluginDescriptor.getPluginId(); + this.pluginDescriptor = pluginDescriptor; + } + + @Override + public void setPluginState(PluginState pluginState) { + this.pluginState = Assert.isNotNull(pluginState, "pluginState 不能为空"); + resolveTime(pluginState); + } + + @Override + public void setFollowSystem() { + isFollowInitial = true; + } + + @Override + public String getPluginId() { + return pluginId; + } + + @Override + public InsidePluginDescriptor getPluginDescriptor() { + return pluginDescriptor; + } + + @Override + public PluginInfo toPluginInfo() { + return new PluginInfoFace(this); + } + + @Override + public String getPluginPath() { + return pluginDescriptor.getPluginPath(); + } + + @Override + public PluginState getPluginState() { + return pluginState; + } + + @Override + public Date getStartTime() { + return startTime; + } + + @Override + public Date getStopTime() { + return stopTime; + } + + @Override + public boolean isFollowSystem() { + return isFollowInitial; + } + + private void resolveTime(PluginState pluginState){ + if(pluginState == PluginState.STARTED || pluginState == PluginState.STARTED_FAILURE){ + startTime = new Date(); + stopTime = null; + } else if(pluginState == PluginState.STOPPED || pluginState == PluginState.STOPPED_FAILURE){ + stopTime = new Date(); + startTime = null; + } else { + startTime = null; + stopTime = null; + } + } + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/DefaultPluginManager.java b/spring-brick/src/main/java/com/gitee/starblues/core/DefaultPluginManager.java new file mode 100644 index 0000000000000000000000000000000000000000..50b3e555289776fdd2ffbb45608fa352a85e0b6e --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/DefaultPluginManager.java @@ -0,0 +1,611 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core; + +import com.gitee.starblues.core.checker.ComposePluginLauncherChecker; +import com.gitee.starblues.core.checker.DefaultPluginLauncherChecker; +import com.gitee.starblues.core.checker.DependencyPluginLauncherChecker; +import com.gitee.starblues.core.checker.PluginBasicChecker; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.core.descriptor.PluginDescriptorLoader; +import com.gitee.starblues.core.exception.PluginDisabledException; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.core.scanner.ComposePathResolve; +import com.gitee.starblues.core.scanner.DevPathResolve; +import com.gitee.starblues.core.scanner.PathResolve; +import com.gitee.starblues.core.scanner.ProdPathResolve; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.integration.listener.DefaultPluginListenerFactory; +import com.gitee.starblues.integration.listener.PluginListenerFactory; +import com.gitee.starblues.utils.*; +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; + +/** + * 抽象的插件管理者 + * @author starBlues + * @version 3.0.0 + */ +public class DefaultPluginManager implements PluginManager{ + + private final Logger log = LoggerFactory.getLogger(this.getClass()); + + private final RealizeProvider provider; + private final IntegrationConfiguration configuration; + private final List pluginRootDirs; + + private final PathResolve pathResolve; + private final PluginBasicChecker basicChecker; + + protected final ComposePluginLauncherChecker launcherChecker; + + private final AtomicBoolean loaded = new AtomicBoolean(false); + + private final Map startedPlugins = new ConcurrentHashMap<>(); + private final Map resolvedPlugins = new ConcurrentHashMap<>(); + + protected PluginListenerFactory pluginListenerFactory; + + + private List sortedPluginIds; + + public DefaultPluginManager(RealizeProvider realizeProvider, IntegrationConfiguration configuration) { + this.provider = Assert.isNotNull(realizeProvider, "参数 realizeProvider 不能为空"); + this.configuration = Assert.isNotNull(configuration, "参数 configuration 不能为空"); + this.pluginRootDirs = resolvePath(configuration.pluginPath()); + this.pathResolve = getComposePathResolve(); + this.basicChecker = realizeProvider.getPluginBasicChecker(); + this.launcherChecker = getComposeLauncherChecker(realizeProvider); + setSortedPluginIds(configuration.sortInitPluginIds()); + } + + protected ComposePluginLauncherChecker getComposeLauncherChecker(RealizeProvider realizeProvider){ + ComposePluginLauncherChecker checker = new ComposePluginLauncherChecker(); + checker.add(new DefaultPluginLauncherChecker(realizeProvider, configuration)); + checker.add(new DependencyPluginLauncherChecker(this)); + return checker; + } + + protected ComposePathResolve getComposePathResolve(){ + return new ComposePathResolve(new DevPathResolve(), new ProdPathResolve()); + } + + public void setSortedPluginIds(List sortedPluginIds) { + this.sortedPluginIds = sortedPluginIds; + } + + @Override + public List getPluginsRoots() { + return new ArrayList<>(pluginRootDirs); + } + + @Override + public String getDefaultPluginRoot() { + if(pluginRootDirs == null){ + return null; + } + return pluginRootDirs.stream().findFirst().orElseThrow(()->{ + return new PluginException("插件根路径未配置"); + }); + } + + @Override + public synchronized List loadPlugins() { + if(loaded.get()){ + throw new RuntimeException("已经加载过了插件, 不能在重复调用: loadPlugins"); + } + try { + if(ObjectUtils.isEmpty(pluginRootDirs)){ + log.warn("插件根目录为空, 无法加载插件."); + return Collections.emptyList(); + } + List scanPluginPaths = provider.getPluginScanner().scan(pluginRootDirs); + if(ObjectUtils.isEmpty(scanPluginPaths)){ + StringBuilder warn = new StringBuilder("以下路径未发现插件: \n"); + for (int i = 0; i < pluginRootDirs.size(); i++) { + warn.append(i + 1).append(". ").append(pluginRootDirs.get(i)).append("\n"); + } + warn.append("请检查路径是否合适.\n"); + warn.append("请检查配置[plugin.runMode]是否合适.\n"); + if(provider.getRuntimeMode() == RuntimeMode.DEV){ + warn.append("请检查插件包是否编译.\n"); + } else { + warn.append("请检查插件是否合法.\n"); + } + log.warn(warn.toString()); + return Collections.emptyList(); + } + pluginListenerFactory = createPluginListenerFactory(); + Map pluginInfoMap = new LinkedHashMap<>(scanPluginPaths.size()); + for (Path path : scanPluginPaths) { + try { + PluginInsideInfo pluginInfo = loadPlugin(path, false); + if(pluginInfo != null){ + pluginInfo.setFollowSystem(); + PluginInfo pluginInfoFace = pluginInfo.toPluginInfo(); + pluginListenerFactory.loadSuccess(pluginInfoFace); + pluginInfoMap.put(pluginInfo.getPluginId(), pluginInfoFace); + } + } catch (PluginException e) { + pluginListenerFactory.loadFailure(path, e); + log.error("加载插件包失败: {}. {}", path, e.getMessage(), e); + } + } + return getSortPlugin(pluginInfoMap); + } finally { + loaded.set(true); + } + } + + protected PluginListenerFactory createPluginListenerFactory(){ + return new DefaultPluginListenerFactory(); + } + + @Override + public boolean verify(Path pluginPath) { + Assert.isNotNull(pluginPath, "参数pluginPath不能为空"); + try (PluginDescriptorLoader pluginDescriptorLoader = provider.getPluginDescriptorLoader()){ + basicChecker.checkPath(pluginPath); + PluginDescriptor pluginDescriptor = pluginDescriptorLoader.load(pluginPath); + return pluginDescriptor != null; + } catch (Exception e) { + log.error("插件jar包校验失败: {}" , pluginPath, e); + return false; + } + } + + @Override + public PluginInfo parse(Path pluginPath) throws PluginException { + PluginInsideInfo pluginInsideInfo = loadFromPath(pluginPath); + if(pluginInsideInfo == null){ + throw new PluginException("非法插件包: " + pluginPath); + } + return pluginInsideInfo.toPluginInfo(); + } + + @Override + public synchronized PluginInfo load(Path pluginPath, boolean unpackPlugin) throws PluginException { + Assert.isNotNull(pluginPath, "参数pluginPath不能为空"); + String sourcePluginPath = pluginPath.toString(); + try { + // 解析插件 + PluginInfo pluginInfo = parse(pluginPath); + // 检查是否存在当前插件 + PluginInsideInfo plugin = getPlugin(pluginInfo.getPluginId()); + if(plugin != null){ + // 已经存在该插件 + throw new PluginException("加载插件包[" + pluginPath + "]失败. 已经存在该插件: " + + MsgUtils.getPluginUnique(plugin.getPluginDescriptor())); + } + if(configuration.isProd()){ + // 如果为生产环境, 则拷贝插件 + pluginPath = copyPlugin(pluginPath, unpackPlugin); + } + // 加载插件 + PluginInsideInfo pluginInsideInfo = loadPlugin(pluginPath, true); + if(pluginInsideInfo != null){ + PluginInfo pluginInfoFace = pluginInsideInfo.toPluginInfo(); + pluginListenerFactory.loadSuccess(pluginInfoFace); + return pluginInfoFace; + } else { + pluginListenerFactory.loadFailure(pluginPath, new PluginException("Not found PluginInsideInfo")); + return null; + } + } catch (Exception e) { + PluginException pluginException = PluginException.getPluginException(e, () -> { + throw new PluginException("插件包加载失败: " + sourcePluginPath, e); + }); + pluginListenerFactory.loadFailure(pluginPath, pluginException); + throw pluginException; + } + } + + @Override + public synchronized void unLoad(String pluginId) { + Assert.isNotNull(pluginId, "参数pluginId不能为空"); + PluginInsideInfo pluginInsideInfo = resolvedPlugins.remove(pluginId); + pluginListenerFactory.unLoadSuccess(pluginInsideInfo.toPluginInfo()); + } + + @Override + public synchronized PluginInfo install(Path pluginPath, boolean unpackPlugin) throws PluginException { + Assert.isNotNull(pluginPath, "参数pluginPath不能为空"); + PluginInfo loadPluginInfo = load(pluginPath, unpackPlugin); + if(loadPluginInfo == null){ + throw new PluginException("插件包安装失败: " + pluginPath); + } + PluginInsideInfo pluginInsideInfo = resolvedPlugins.get(loadPluginInfo.getPluginId()); + PluginInfo pluginInfo = pluginInsideInfo.toPluginInfo(); + try { + start(pluginInsideInfo); + pluginListenerFactory.startSuccess(pluginInfo); + log.info("插件[{}]安装成功", MsgUtils.getPluginUnique(pluginInsideInfo.getPluginDescriptor())); + return pluginInsideInfo.toPluginInfo(); + } catch (Exception e){ + if(e instanceof PluginDisabledException){ + throw (PluginDisabledException)e; + } + PluginException pluginException = PluginException.getPluginException(e, ()-> { + unLoad(loadPluginInfo.getPluginId()); + throw new PluginException("插件包[ " + pluginPath + " ]安装失败: " + e.getMessage(), e); + }); + pluginListenerFactory.startFailure(pluginInfo, pluginException); + throw pluginException; + } + } + + @Override + public synchronized void uninstall(String pluginId) throws PluginException { + Assert.isNotNull(pluginId, "参数pluginId不能为空"); + PluginInsideInfo wrapperInside = getPlugin(pluginId); + if(wrapperInside == null){ + throw new PluginException("没有发现插件: " + pluginId); + } + PluginInfo pluginInfo = wrapperInside.toPluginInfo(); + if(wrapperInside.getPluginState() == PluginState.STARTED){ + try { + stop(wrapperInside); + pluginListenerFactory.stopSuccess(pluginInfo); + } catch (Exception e) { + PluginException pluginException = PluginException.getPluginException(e, + ()-> new PluginException("停止", pluginId, e)); + pluginListenerFactory.stopFailure(pluginInfo, pluginException); + throw pluginException; + } + } + startedPlugins.remove(pluginId); + unLoad(pluginId); + LogUtils.info(log, wrapperInside.getPluginDescriptor(), "卸载成功"); + } + + @Override + public synchronized PluginInfo upgrade(Path pluginPath, boolean unpackPlugin) throws PluginException { + Assert.isNotNull(pluginPath, "参数pluginPath不能为空"); + // 解析插件包 + PluginInfo upgradePlugin = parse(pluginPath); + if(upgradePlugin == null){ + throw new PluginException("非法插件包: " + pluginPath); + } + // 检查插件是否被禁用 + PluginDisabledException.checkDisabled(upgradePlugin, configuration, "更新"); + String pluginId = upgradePlugin.getPluginId(); + // 得到旧插件 + PluginInsideInfo oldPlugin = getPlugin(pluginId); + if(oldPlugin == null){ + // 旧插件为空, 则直接安装新插件 + return install(pluginPath, unpackPlugin); + } + // 检查插件版本 + PluginDescriptor upgradePluginDescriptor = upgradePlugin.getPluginDescriptor(); + checkVersion(oldPlugin.getPluginDescriptor().getPluginVersion(), upgradePluginDescriptor.getPluginVersion()); + if(oldPlugin.getPluginState() == PluginState.STARTED){ + // 如果插件被启动, 则卸载旧的插件 + uninstall(pluginId); + } else if(oldPlugin.getPluginState() == PluginState.LOADED){ + // 如果插件被load + unLoad(pluginId); + } + try { + // 安装新插件 + install(pluginPath, unpackPlugin); + log.info("更新插件[{}]成功", MsgUtils.getPluginUnique(upgradePluginDescriptor)); + return upgradePlugin; + } catch (Exception e){ + throw PluginException.getPluginException(e, ()-> new PluginException(upgradePluginDescriptor, "更新失败", e)); + } + } + + @Override + public synchronized PluginInfo start(String pluginId) throws PluginException { + if(ObjectUtils.isEmpty(pluginId)){ + return null; + } + PluginInsideInfo pluginInsideInfo = getPlugin(pluginId); + if(pluginInsideInfo == null){ + throw new PluginException("没有发现插件: " + pluginId); + } + PluginInfo pluginInfo = pluginInsideInfo.toPluginInfo(); + try { + start(pluginInsideInfo); + log.info("插件[{}]启动成功", MsgUtils.getPluginUnique(pluginInsideInfo.getPluginDescriptor())); + pluginListenerFactory.startSuccess(pluginInfo); + return pluginInfo; + } catch (Exception e){ + PluginException pluginException = PluginException.getPluginException(e, + ()-> new PluginException(pluginInsideInfo.getPluginDescriptor(), "启动失败", e)); + pluginListenerFactory.startFailure(pluginInfo, pluginException); + throw pluginException; + } + } + + @Override + public synchronized PluginInfo stop(String pluginId) throws PluginException { + if(ObjectUtils.isEmpty(pluginId)){ + return null; + } + PluginInsideInfo pluginInsideInfo = startedPlugins.get(pluginId); + if(pluginInsideInfo == null){ + throw new PluginException("没有发现插件: " + pluginId); + } + PluginInfo pluginInfo = pluginInsideInfo.toPluginInfo(); + try { + stop(pluginInsideInfo); + log.info("停止插件[{}]成功", MsgUtils.getPluginUnique(pluginInsideInfo.getPluginDescriptor())); + pluginListenerFactory.stopSuccess(pluginInfo); + return pluginInfo; + } catch (Exception e) { + PluginException pluginException = PluginException.getPluginException(e, + () -> new PluginException(pluginInsideInfo.getPluginDescriptor(), "停止失败", e)); + pluginListenerFactory.stopFailure(pluginInfo, pluginException); + throw pluginException; + } + } + + @Override + public synchronized PluginInfo getPluginInfo(String pluginId) { + if(ObjectUtils.isEmpty(pluginId)){ + return null; + } + PluginInsideInfo wrapperInside = startedPlugins.get(pluginId); + if(wrapperInside == null){ + wrapperInside = resolvedPlugins.get(pluginId); + } + if(wrapperInside != null){ + return wrapperInside.toPluginInfo(); + } else { + return null; + } + } + + @Override + public synchronized List getPluginInfos() { + List pluginDescriptors = new ArrayList<>( + resolvedPlugins.size() + startedPlugins.size()); + for (PluginInsideInfo wrapperInside : startedPlugins.values()) { + pluginDescriptors.add(wrapperInside.toPluginInfo()); + } + for (PluginInsideInfo wrapperInside : resolvedPlugins.values()) { + pluginDescriptors.add(wrapperInside.toPluginInfo()); + } + return pluginDescriptors; + } + + protected PluginInsideInfo loadPlugin(Path pluginPath, boolean resolvePath) { + if(resolvePath){ + Path sourcePluginPath = pluginPath; + pluginPath = pathResolve.resolve(pluginPath); + if(pluginPath == null){ + throw new PluginException("未发现插件: " + sourcePluginPath); + } + } + PluginInsideInfo pluginInsideInfo = loadFromPath(pluginPath); + if(pluginInsideInfo == null){ + return null; + } + String pluginId = pluginInsideInfo.getPluginId(); + if(resolvedPlugins.containsKey(pluginId)){ + throw new PluginException(pluginInsideInfo.getPluginDescriptor(), "已经被加载"); + } + resolvedPlugins.put(pluginId, pluginInsideInfo); + LogUtils.info(log, pluginInsideInfo.getPluginDescriptor(), "加载成功"); + return pluginInsideInfo; + } + + protected PluginInsideInfo loadFromPath(Path pluginPath) { + try { + basicChecker.checkPath(pluginPath); + } catch (Exception e) { + throw PluginException.getPluginException(e, ()-> { + return new PluginException("非法插件包. " + e.getMessage(), e); + }); + } + + try (PluginDescriptorLoader pluginDescriptorLoader = provider.getPluginDescriptorLoader()){ + InsidePluginDescriptor pluginDescriptor = pluginDescriptorLoader.load(pluginPath); + if(pluginDescriptor == null){ + return null; + } + String pluginId = pluginDescriptor.getPluginId(); + PluginInsideInfo pluginInsideInfo = new DefaultPluginInsideInfo(pluginDescriptor); + if(configuration.isDisabled(pluginId)){ + pluginInsideInfo.setPluginState(PluginState.DISABLED); + } else { + pluginInsideInfo.setPluginState(PluginState.LOADED); + } + return pluginInsideInfo; + } catch (Exception e){ + throw PluginException.getPluginException(e, ()-> new PluginException("加载插件失败")); + } + } + + /** + * 拷贝插件文件到插件根目录 + * @param pluginPath 源插件文件路径 + * @param unpackPlugin 是否解压插件包. 只有为压缩类型包才可解压 + * @return 拷贝后的插件路径 + * @throws IOException IO 异常 + */ + protected Path copyPlugin(Path pluginPath, boolean unpackPlugin) throws IOException { + if(configuration.isDev()){ + return pluginPath; + } + File targetFile = pluginPath.toFile(); + if(!targetFile.exists()) { + throw new PluginException("不存在插件文件: " + pluginPath); + } + String targetFileName = targetFile.getName(); + // 先判断当前插件文件是否在插件目录中 + File pluginRootDir = null; + for (String dir : pluginRootDirs) { + File rootDir = new File(dir); + if(targetFile.getParentFile().compareTo(rootDir) == 0){ + pluginRootDir = rootDir; + break; + } + } + String resolvePluginFileName = unpackPlugin ? PluginFileUtils.getFileName(targetFile) : targetFileName; + Path resultPath = null; + if(pluginRootDir != null){ + // 在根目录中存在 + if(targetFile.isFile() && unpackPlugin){ + // 需要解压, 检查解压后的文件名称是否存在同名文件 + checkExistFile(pluginRootDir, resolvePluginFileName); + String unpackPluginPath = FilesUtils.joiningFilePath(pluginRootDir.getPath(), resolvePluginFileName); + PluginFileUtils.decompressZip(targetFile.getPath(), unpackPluginPath); + resultPath = Paths.get(unpackPluginPath); + PluginFileUtils.deleteFile(targetFile); + } else { + resultPath = targetFile.toPath(); + } + } else { + File pluginFile = pluginPath.toFile(); + pluginRootDir = new File(getDefaultPluginRoot()); + File pluginRootDirFile = new File(getDefaultPluginRoot()); + // 检查是否存在同名文件 + checkExistFile(pluginRootDirFile, resolvePluginFileName); + targetFile = Paths.get(FilesUtils.joiningFilePath(pluginRootDir.getPath(), resolvePluginFileName)).toFile(); + if(pluginFile.isFile()){ + if(unpackPlugin){ + // 需要解压 + String unpackPluginPath = FilesUtils.joiningFilePath(pluginRootDir.getPath(), resolvePluginFileName); + PluginFileUtils.decompressZip(pluginFile.getPath(), unpackPluginPath); + resultPath = Paths.get(unpackPluginPath); + } else { + FileUtils.copyFile(pluginFile, targetFile); + resultPath = targetFile.toPath(); + } + } else { + FileUtils.copyDirectory(pluginFile, targetFile); + resultPath = targetFile.toPath(); + } + } + return resultPath; + } + + + /** + * 统一启动插件操作 + * @param pluginInsideInfo PluginInsideInfo + * @throws Exception 启动异常 + */ + protected void start(PluginInsideInfo pluginInsideInfo) throws Exception{ + Assert.isNotNull(pluginInsideInfo, "pluginInsideInfo 参数不能为空"); + String pluginId = pluginInsideInfo.getPluginId(); + launcherChecker.checkCanStart(pluginInsideInfo); + pluginInsideInfo.setPluginState(PluginState.STARTED); + startedPlugins.put(pluginId, pluginInsideInfo); + resolvedPlugins.remove(pluginId); + } + + /** + * 统一停止插件操作 + * @param pluginInsideInfo PluginInsideInfo + * @throws Exception 启动异常 + */ + protected void stop(PluginInsideInfo pluginInsideInfo) throws Exception{ + launcherChecker.checkCanStop(pluginInsideInfo); + String pluginId = pluginInsideInfo.getPluginId(); + pluginInsideInfo.setPluginState(PluginState.STOPPED); + resolvedPlugins.put(pluginId, pluginInsideInfo); + startedPlugins.remove(pluginId); + } + + /** + * 根据配置重新排序插件 + * @param pluginInfos 未排序的查询信息 + * @return 排序的插件信息 + */ + protected List getSortPlugin(Map pluginInfos){ + if (ObjectUtils.isEmpty(sortedPluginIds)) { + return new ArrayList<>(pluginInfos.values()); + } + List sortPluginInfos = new ArrayList<>(); + for (String sortedPluginId : sortedPluginIds) { + PluginInfo pluginInfo = pluginInfos.get(sortedPluginId); + if(pluginInfo != null){ + sortPluginInfos.add(pluginInfo); + pluginInfos.remove(sortedPluginId); + } + } + sortPluginInfos.addAll(pluginInfos.values()); + return sortPluginInfos; + } + + + protected PluginInsideInfo getPlugin(String pluginId){ + PluginInsideInfo wrapperInside = startedPlugins.get(pluginId); + if(wrapperInside == null){ + wrapperInside = resolvedPlugins.get(pluginId); + } + return wrapperInside; + } + + /** + * 检查是否目录中是否存在同名插件 + * @param dirFile 目录文件 + * @param pluginFileName 检查的插件名 + */ + private void checkExistFile(File dirFile, String pluginFileName) { + if(ResourceUtils.existFile(dirFile, pluginFileName)){ + // 插件根目录存在同名插件文件 + throw getExistFileException(dirFile.getPath(), pluginFileName); + } + } + + private PluginException getExistFileException(String rootPath, String pluginFileName){ + return new PluginException("插件目录[" + rootPath + "]存在同名文件: " + pluginFileName); + } + + /** + * 检查比较插件版本 + * @param oldPluginVersion 旧插件版本 + * @param newPluginVersion 新插件版本 + */ + protected void checkVersion(String oldPluginVersion, String newPluginVersion){ + int compareVersion = provider.getVersionInspector().compareTo(oldPluginVersion, newPluginVersion); + if(compareVersion <= 0){ + throw new PluginException("插件包版本[" + newPluginVersion + "]必须大于:" + + oldPluginVersion); + } + } + + private List resolvePath(List path){ + if(ObjectUtils.isEmpty(path)){ + return Collections.emptyList(); + } else { + File file = new File(""); + String absolutePath = file.getAbsolutePath(); + return path.stream() + .filter(p->!ObjectUtils.isEmpty(p)) + .map(p->FilesUtils.resolveRelativePath(absolutePath, p)) + .collect(Collectors.toList()); + } + } + + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/DefaultRealizeProvider.java b/spring-brick/src/main/java/com/gitee/starblues/core/DefaultRealizeProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..758d531012a2f564b8822e4cede173f99ca02c2a --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/DefaultRealizeProvider.java @@ -0,0 +1,112 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core; + +import com.gitee.starblues.core.checker.ComposePluginBasicChecker; +import com.gitee.starblues.core.checker.DefaultPluginBasicChecker; +import com.gitee.starblues.core.checker.PluginBasicChecker; +import com.gitee.starblues.core.descriptor.ComposeDescriptorLoader; +import com.gitee.starblues.core.descriptor.DevPluginDescriptorLoader; +import com.gitee.starblues.core.descriptor.PluginDescriptorLoader; +import com.gitee.starblues.core.descriptor.ProdPluginDescriptorLoader; +import com.gitee.starblues.core.scanner.BasePluginScanner; +import com.gitee.starblues.core.scanner.DevPathResolve; +import com.gitee.starblues.core.scanner.PluginScanner; +import com.gitee.starblues.core.scanner.ProdPathResolve; +import com.gitee.starblues.core.version.SemverVersionInspector; +import com.gitee.starblues.core.version.VersionInspector; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.utils.Assert; +import org.springframework.context.ApplicationContext; + +/** + * @author starBlues + * @version 3.0.0 + */ +public class DefaultRealizeProvider implements RealizeProvider { + + private PluginScanner pluginScanner; + private PluginBasicChecker pluginBasicChecker; + private PluginDescriptorLoader pluginDescriptorLoader; + private VersionInspector versionInspector; + + protected final IntegrationConfiguration configuration; + protected final ApplicationContext applicationContext; + + public DefaultRealizeProvider(IntegrationConfiguration configuration, + ApplicationContext applicationContext){ + this.configuration = Assert.isNotNull(configuration, "参数 configuration 不能为空"); + this.applicationContext = Assert.isNotNull(applicationContext, "参数 configuration 不能为空"); + } + + @Override + public void init() { + BasePluginScanner basePluginScanner = new BasePluginScanner(); + if(configuration.environment() == RuntimeMode.DEV){ + basePluginScanner.setPathResolve(new DevPathResolve()); + } else { + basePluginScanner.setPathResolve(new ProdPathResolve()); + } + setPluginScanner(basePluginScanner); + setPluginBasicChecker(new ComposePluginBasicChecker(applicationContext)); + setPluginDescriptorLoader(new ComposeDescriptorLoader(pluginBasicChecker)); + setVersionInspector(new SemverVersionInspector()); + } + + public void setPluginScanner(PluginScanner pluginScanner) { + this.pluginScanner = Assert.isNotNull(pluginScanner, "pluginScanner 不能为空"); + } + + public void setPluginBasicChecker(PluginBasicChecker pluginBasicChecker) { + this.pluginBasicChecker = Assert.isNotNull(pluginBasicChecker, + "pluginBasicChecker 不能为空"); + } + + public void setPluginDescriptorLoader(PluginDescriptorLoader pluginDescriptorLoader) { + this.pluginDescriptorLoader = Assert.isNotNull(pluginDescriptorLoader, + "pluginDescriptorLoader 不能为空"); + } + + public void setVersionInspector(VersionInspector versionInspector) { + this.versionInspector = Assert.isNotNull(versionInspector, "versionInspector 不能为空"); + } + + @Override + public RuntimeMode getRuntimeMode() { + return configuration.environment(); + } + + @Override + public PluginScanner getPluginScanner() { + return Assert.isNotNull(pluginScanner, "PluginScanner 实现不能为空"); + } + + @Override + public PluginBasicChecker getPluginBasicChecker() { + return Assert.isNotNull(pluginBasicChecker, "pluginBasicChecker 实现不能为空"); + } + + @Override + public PluginDescriptorLoader getPluginDescriptorLoader() { + return Assert.isNotNull(pluginDescriptorLoader, "PluginDescriptorLoader 实现不能为空"); + } + + @Override + public VersionInspector getVersionInspector() { + return Assert.isNotNull(versionInspector, "VersionInspector 实现不能为空"); + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/PluginInfo.java b/spring-brick/src/main/java/com/gitee/starblues/core/PluginInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..851d1f810f2dc0be811bdeba3b458e9fcd74887a --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/PluginInfo.java @@ -0,0 +1,73 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core; + +import com.gitee.starblues.core.descriptor.PluginDescriptor; + +import java.util.Date; + +/** + * 插件信息 + * @author starBlues + * @version 3.0.0 + */ +public interface PluginInfo { + + /** + * 得到插件id + * @return String + */ + String getPluginId(); + + /** + * 得到插件描述 + * @return PluginDescriptor + */ + PluginDescriptor getPluginDescriptor(); + + /** + * 得到插件路径 + * @return Path + */ + String getPluginPath(); + + /** + * 得到插件状态 + * @return PluginState + */ + PluginState getPluginState(); + + /** + * 启动时间. 只有启动状态 {@link PluginState#STARTED} 才有值。 + * @return Date + */ + Date getStartTime(); + + /** + * 停止时间. 只有停止状态 {@link PluginState#STOPPED} 才有值。 + * @return Date + */ + Date getStopTime(); + + + /** + * 是否跟随系统启动而加载的插件 + * @return true: 是, false: 否 + */ + boolean isFollowSystem(); + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/PluginInfoFace.java b/spring-brick/src/main/java/com/gitee/starblues/core/PluginInfoFace.java new file mode 100644 index 0000000000000000000000000000000000000000..0822f8418c5d4d683c4d4de79c41a2aefafc6ebc --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/PluginInfoFace.java @@ -0,0 +1,81 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core; + +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.utils.Assert; + +import java.util.Date; + +/** + * 外部 PluginWrapperFace + * @author starBlues + * @version 3.0.0 + */ +public class PluginInfoFace implements PluginInfo { + + private final PluginDescriptor pluginDescriptor; + private final PluginState pluginState; + private final boolean followSystem; + + private final Date startTime; + private final Date stopTime; + + public PluginInfoFace(PluginInsideInfo pluginInsideInfo) { + Assert.isNotNull(pluginInsideInfo, "参数 pluginWrapperInside 不能为空"); + this.pluginDescriptor = pluginInsideInfo.getPluginDescriptor().toPluginDescriptor(); + this.pluginState = pluginInsideInfo.getPluginState(); + this.followSystem = pluginInsideInfo.isFollowSystem(); + this.startTime = pluginInsideInfo.getStartTime(); + this.stopTime = pluginInsideInfo.getStopTime(); + } + + @Override + public String getPluginId() { + return pluginDescriptor.getPluginId(); + } + + @Override + public PluginDescriptor getPluginDescriptor() { + return pluginDescriptor; + } + + @Override + public String getPluginPath() { + return pluginDescriptor.getPluginPath(); + } + + @Override + public PluginState getPluginState() { + return pluginState; + } + + @Override + public Date getStartTime() { + return startTime; + } + + @Override + public Date getStopTime() { + return stopTime; + } + + @Override + public boolean isFollowSystem() { + return followSystem; + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/PluginInsideInfo.java b/spring-brick/src/main/java/com/gitee/starblues/core/PluginInsideInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..1c2a39c6cc6fbc3f8a3942a8e46317e119164dc4 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/PluginInsideInfo.java @@ -0,0 +1,52 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core; + +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; + +/** + * 内部的 PluginInfo + * @version 3.0.0 + * @author starBlues + */ +public interface PluginInsideInfo extends PluginInfo { + + /** + * 设置插件状态 + * @param pluginState 插件状态 + */ + void setPluginState(PluginState pluginState); + + /** + * 设置是跟随系统启动而启动的插件 + */ + void setFollowSystem(); + + /** + * 得到插件描述 + * @return PluginDescriptor + */ + @Override + InsidePluginDescriptor getPluginDescriptor(); + + /** + * 转换为外部插件信息 + * @return PluginInfo + */ + PluginInfo toPluginInfo(); + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/PluginLauncherManager.java b/spring-brick/src/main/java/com/gitee/starblues/core/PluginLauncherManager.java new file mode 100644 index 0000000000000000000000000000000000000000..432fcf62d4c2491517c7f42f5ffe67eea0441b3d --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/PluginLauncherManager.java @@ -0,0 +1,146 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core; + +import com.gitee.starblues.core.checker.PluginLauncherChecker; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.core.launcher.plugin.DefaultPluginInteractive; +import com.gitee.starblues.core.launcher.plugin.PluginInteractive; +import com.gitee.starblues.core.launcher.plugin.PluginLauncher; +import com.gitee.starblues.core.launcher.plugin.involved.PluginLaunchInvolved; +import com.gitee.starblues.core.launcher.plugin.involved.PluginLaunchInvolvedFactory; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.integration.listener.DefaultPluginListenerFactory; +import com.gitee.starblues.integration.listener.PluginListenerFactory; +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.spring.MainApplicationContextProxy; +import com.gitee.starblues.spring.SpringPluginHook; +import com.gitee.starblues.spring.invoke.DefaultInvokeSupperCache; +import com.gitee.starblues.spring.invoke.InvokeSupperCache; +import com.gitee.starblues.utils.SpringBeanUtils; +import org.springframework.context.support.GenericApplicationContext; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 可引导启动的插件管理者 + * @author starBlues + * @version 3.0.0 + */ +public class PluginLauncherManager extends DefaultPluginManager{ + + private final Map registryInfo = new ConcurrentHashMap<>(); + + + private final MainApplicationContext mainApplicationContext; + private final GenericApplicationContext mainGenericApplicationContext; + private final IntegrationConfiguration configuration; + private final InvokeSupperCache invokeSupperCache; + private final PluginLaunchInvolved pluginLaunchInvolved; + + public PluginLauncherManager(RealizeProvider realizeProvider, + GenericApplicationContext applicationContext, + IntegrationConfiguration configuration) { + super(realizeProvider, configuration); + this.mainApplicationContext = new MainApplicationContextProxy(applicationContext, applicationContext); + this.mainGenericApplicationContext = applicationContext; + this.configuration = configuration; + this.invokeSupperCache = new DefaultInvokeSupperCache(); + this.pluginLaunchInvolved = new PluginLaunchInvolvedFactory(); + addCustomPluginChecker(); + } + + private void addCustomPluginChecker(){ + List pluginCheckers = SpringBeanUtils.getBeans(mainGenericApplicationContext, + PluginLauncherChecker.class); + for (PluginLauncherChecker pluginChecker : pluginCheckers) { + super.launcherChecker.add(pluginChecker); + } + } + + @Override + protected PluginListenerFactory createPluginListenerFactory() { + return new DefaultPluginListenerFactory(mainGenericApplicationContext); + } + + @Override + public synchronized List loadPlugins() { + this.pluginLaunchInvolved.initialize(mainGenericApplicationContext, configuration); + return super.loadPlugins(); + } + + @Override + protected void start(PluginInsideInfo pluginInsideInfo) throws Exception { + super.start(pluginInsideInfo); + try { + InsidePluginDescriptor pluginDescriptor = pluginInsideInfo.getPluginDescriptor(); + PluginInteractive pluginInteractive = new DefaultPluginInteractive(pluginDescriptor, + mainApplicationContext, configuration, invokeSupperCache); + PluginLauncher pluginLauncher = new PluginLauncher(pluginInteractive, pluginLaunchInvolved); + SpringPluginHook springPluginHook = pluginLauncher.run(); + RegistryPluginInfo registryPluginInfo = new RegistryPluginInfo(pluginDescriptor, springPluginHook); + registryInfo.put(pluginDescriptor.getPluginId(), registryPluginInfo); + } catch (Exception e){ + // 启动失败, 进行停止 + pluginInsideInfo.setPluginState(PluginState.STARTED_FAILURE); + throw e; + } + } + + + @Override + protected void stop(PluginInsideInfo pluginInsideInfo) throws Exception { + String pluginId = pluginInsideInfo.getPluginId(); + RegistryPluginInfo registryPluginInfo = registryInfo.get(pluginId); + if(registryPluginInfo == null){ + throw new PluginException("没有发现插件 '" + pluginId + "' 信息"); + } + try { + SpringPluginHook springPluginHook = registryPluginInfo.getSpringPluginHook(); + springPluginHook.stopVerify(); + springPluginHook.close(); + invokeSupperCache.remove(pluginId); + registryInfo.remove(pluginId); + super.stop(pluginInsideInfo); + } catch (Exception e){ + pluginInsideInfo.setPluginState(PluginState.STOPPED_FAILURE); + throw e; + } + } + + static class RegistryPluginInfo{ + private final PluginDescriptor descriptor; + private final SpringPluginHook springPluginHook; + + private RegistryPluginInfo(PluginDescriptor descriptor, SpringPluginHook springPluginHook) { + this.descriptor = descriptor; + this.springPluginHook = springPluginHook; + } + + public PluginDescriptor getDescriptor() { + return descriptor; + } + + public SpringPluginHook getSpringPluginHook() { + return springPluginHook; + } + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/PluginManager.java b/spring-brick/src/main/java/com/gitee/starblues/core/PluginManager.java new file mode 100644 index 0000000000000000000000000000000000000000..b1da15f3be369153970253903ba6394d09ee12c2 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/PluginManager.java @@ -0,0 +1,135 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core; + +import com.gitee.starblues.core.exception.PluginException; + +import java.nio.file.Path; +import java.util.List; + +/** + * 插件管理者 + * @author starBlues + * @version 3.0.0 + */ +public interface PluginManager { + + /** + * 得到插件root目录 + * @return List + */ + List getPluginsRoots(); + + /** + * 得到插件默认的根路径 + * @return String + */ + String getDefaultPluginRoot(); + + /** + * 加载配置目录中全部插件 + * @return 加载的插件信息 + */ + List loadPlugins(); + + /** + * 校验插件jar包 + * @param pluginPath 插件包的路径 + * @return 成功: 返回true; 失败: 抛出异常或者返回false + * @throws PluginException 校验异常 + */ + boolean verify(Path pluginPath); + + /** + * 解析插件包 + * @param pluginPath 插件包路基 + * @return 解析的插件信息 + * @throws PluginException 插件异常 + */ + PluginInfo parse(Path pluginPath) throws PluginException; + + /** + * 根据具体插件路径来加载插件. + * @param pluginPath 插件路径 + * @param unpackPlugin 是否解压插件文件 + * @return 加载的插件信息 + * @throws PluginException 插件异常 + */ + PluginInfo load(Path pluginPath, boolean unpackPlugin) throws PluginException; + + /** + * 卸载加载插件 + * @param pluginId 插件id + */ + void unLoad(String pluginId); + + /** + * 通过路径安装插件(会启用), 该插件文件必须存在于服务器 [适用于生产环境] + * 如果在插件目录存在同名的插件包, 系统会自动备份该插件包。备份文件命名规则为;[install-backup][时间]_原jar名.jar + * @param pluginPath 插件路径 + * @param unpackPlugin 是否解压插件包. (如果插件包为压缩包时生效) + * @return 成功: 返回插件信息PluginInfo; 失败: 抛出异常或者返回null + * @throws PluginException 异常信息 + */ + PluginInfo install(Path pluginPath, boolean unpackPlugin) throws PluginException; + + /** + * 卸载插件 + * @param pluginId 插件id + * @throws PluginException 插件异常 + */ + void uninstall(String pluginId) throws PluginException; + + /** + * 更新已经安装的插件 + * @param pluginPath 新版本插件路径 + * @param unpackPlugin 是否解压要更新的插件文件 + * @throws PluginException 插件异常 + * @return PluginInfo 更新的插件信息 + */ + PluginInfo upgrade(Path pluginPath, boolean unpackPlugin) throws PluginException; + + /** + * 启动处于 RESOLVED 状态的插件 + * @param pluginId 插件id + * @return PluginDescriptor + * @throws PluginException 插件异常 + */ + PluginInfo start(String pluginId) throws PluginException; + + /** + * 停止启动的插件 + * @param pluginId 插件id + * @return PluginDescriptor + * @throws PluginException 插件异常 + */ + PluginInfo stop(String pluginId) throws PluginException; + + /** + * 根据插件id获取插件描述信息 + * @param pluginId 插件id + * @return PluginDescriptor + */ + PluginInfo getPluginInfo(String pluginId); + + /** + * 得到全部的插件信息 + * @return List PluginWrapper + */ + List getPluginInfos(); + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/PluginState.java b/spring-brick/src/main/java/com/gitee/starblues/core/PluginState.java new file mode 100644 index 0000000000000000000000000000000000000000..ff124587b2945e241a6f7209359130f954746090 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/PluginState.java @@ -0,0 +1,81 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core; + +/** + * 插件状态枚举 + * @author starBlues + * @version 3.0.0 + */ +public enum PluginState { + + /** + * 被禁用状态 + */ + DISABLED("DISABLED"), + + /** + * 被加载了 + */ + LOADED("LOADED"), + + /** + * 启动状态 + */ + STARTED("STARTED"), + + /** + * 启动失败状态 + */ + STARTED_FAILURE("STARTED_FAILURE"), + + /** + * 停止状态 + */ + STOPPED("STOPPED"), + + /** + * 停止失败状态 + */ + STOPPED_FAILURE("STOPPED_FAILURE"); + + + private final String status; + + PluginState(String status) { + this.status = status; + } + + public boolean equals(String status) { + return (this.status.equalsIgnoreCase(status)); + } + + @Override + public String toString() { + return status; + } + + public static PluginState parse(String string) { + for (PluginState status : values()) { + if (status.equals(string)) { + return status; + } + } + return null; + } + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/RealizeProvider.java b/spring-brick/src/main/java/com/gitee/starblues/core/RealizeProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..59417d2de124b2e5e820bdd9f3e64e09ddc4c499 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/RealizeProvider.java @@ -0,0 +1,65 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core; + +import com.gitee.starblues.core.checker.PluginBasicChecker; +import com.gitee.starblues.core.descriptor.PluginDescriptorLoader; +import com.gitee.starblues.core.scanner.PluginScanner; +import com.gitee.starblues.core.version.VersionInspector; +/** + * 插件扩展配置 + * @author starBlues + * @version 3.0.0 + */ +public interface RealizeProvider { + + /** + * 初始化 + */ + void init(); + + /** + * 当前运行环境 + * @return RuntimeMode + */ + RuntimeMode getRuntimeMode(); + + /** + * 得到 PluginScanner 实现 + * @return PluginScanner + */ + PluginScanner getPluginScanner(); + + /** + * 得到插件基本的检查者 + * @return PluginBasicChecker + */ + PluginBasicChecker getPluginBasicChecker(); + + /** + * 得到 PluginDescriptorLoader 实现 + * @return PluginDescriptorLoader + */ + PluginDescriptorLoader getPluginDescriptorLoader(); + + /** + * 得到 VersionInspector 实现 + * @return VersionInspector + */ + VersionInspector getVersionInspector(); + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/RuntimeMode.java b/spring-brick/src/main/java/com/gitee/starblues/core/RuntimeMode.java new file mode 100644 index 0000000000000000000000000000000000000000..ebd126abd240a6a9325e4277f2feaae94b1b3348 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/RuntimeMode.java @@ -0,0 +1,59 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core; + +/** + * 插件运行环境 + * @author starBlues + * @version 3.0.0 + */ +public enum RuntimeMode { + + /** + * 开发环境 + */ + DEV("dev"), + + /** + * 生产环境 + */ + PROD("prod"); + + private final String mode; + + RuntimeMode(String mode) { + this.mode = mode; + } + + public String getMode() { + return mode; + } + + public static RuntimeMode byName(String model){ + if(DEV.name().equalsIgnoreCase(model)){ + return RuntimeMode.DEV; + } else { + return RuntimeMode.PROD; + } + } + + @Override + public String toString() { + return mode; + } + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/checker/ComposePluginBasicChecker.java b/spring-brick/src/main/java/com/gitee/starblues/core/checker/ComposePluginBasicChecker.java new file mode 100644 index 0000000000000000000000000000000000000000..de0bd8e5c2c41ae5a5fea64ff596c91609a86f3c --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/checker/ComposePluginBasicChecker.java @@ -0,0 +1,73 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.checker; + +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.utils.SpringBeanUtils; +import org.springframework.context.ApplicationContext; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +/** + * 组合插件检查者 + * @author starBlues + * @version 3.0.0 + */ +public class ComposePluginBasicChecker implements PluginBasicChecker { + + private final List pluginCheckers; + + public ComposePluginBasicChecker(ApplicationContext applicationContext) { + this.pluginCheckers = new ArrayList<>(); + addDefaultChecker(); + addCustomChecker(applicationContext); + } + + protected void addDefaultChecker(){ + pluginCheckers.add(new DefaultPluginBasicChecker()); + } + + protected void addCustomChecker(ApplicationContext applicationContext){ + List pluginCheckers = SpringBeanUtils.getBeans(applicationContext, + PluginBasicChecker.class); + this.pluginCheckers.addAll(pluginCheckers); + } + + public void add(PluginBasicChecker pluginChecker){ + if(pluginChecker != null){ + this.pluginCheckers.add(pluginChecker); + } + } + + + @Override + public void checkPath(Path path) throws Exception { + for (PluginBasicChecker pluginChecker : pluginCheckers) { + pluginChecker.checkPath(path); + } + } + + @Override + public void checkDescriptor(PluginDescriptor descriptor) throws PluginException { + for (PluginBasicChecker pluginChecker : pluginCheckers) { + pluginChecker.checkDescriptor(descriptor); + } + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/checker/ComposePluginLauncherChecker.java b/spring-brick/src/main/java/com/gitee/starblues/core/checker/ComposePluginLauncherChecker.java new file mode 100644 index 0000000000000000000000000000000000000000..716f788874c4c8196ffc606ef803540441e34f5e --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/checker/ComposePluginLauncherChecker.java @@ -0,0 +1,63 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.checker; + +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.core.exception.PluginException; + +import java.util.ArrayList; +import java.util.List; + +/** + * 组合插件检查者 + * @author starBlues + * @version 3.0.0 + */ +public class ComposePluginLauncherChecker implements PluginLauncherChecker { + + private final List pluginCheckers; + + public ComposePluginLauncherChecker() { + this(new ArrayList<>()); + } + + public ComposePluginLauncherChecker(List pluginCheckers) { + this.pluginCheckers = pluginCheckers; + } + + public void add(PluginLauncherChecker pluginChecker){ + if(pluginChecker != null){ + this.pluginCheckers.add(pluginChecker); + } + } + + + @Override + public void checkCanStart(PluginInfo pluginInfo) throws PluginException { + for (PluginLauncherChecker pluginChecker : pluginCheckers) { + pluginChecker.checkCanStart(pluginInfo); + } + } + + @Override + public void checkCanStop(PluginInfo pluginInfo) throws PluginException { + for (PluginLauncherChecker pluginChecker : pluginCheckers) { + pluginChecker.checkCanStop(pluginInfo); + } + } + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/checker/DefaultPluginBasicChecker.java b/spring-brick/src/main/java/com/gitee/starblues/core/checker/DefaultPluginBasicChecker.java new file mode 100644 index 0000000000000000000000000000000000000000..846ab7e30017f8074448d8d621e8c324702bb2a8 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/checker/DefaultPluginBasicChecker.java @@ -0,0 +1,73 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.checker; + +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.common.PluginDescriptorKey; +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.utils.Assert; + +import java.io.FileNotFoundException; +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * 默认的基本检查者 + * @author starBlues + * @version 3.0.0 + */ +public class DefaultPluginBasicChecker implements PluginBasicChecker { + + + @Override + public void checkPath(Path path) throws Exception { + if(path == null){ + throw new FileNotFoundException("path 文件路径不能为空"); + } + if(Files.notExists(path)){ + throw new FileNotFoundException("不存在文件: " + path.toString()); + } + } + + @Override + public void checkDescriptor(PluginDescriptor descriptor) throws PluginException { + Assert.isNotNull(descriptor, "PluginDescriptor 不能为空"); + + Assert.isNotEmpty(descriptor.getPluginPath(), "pluginPath 不能为空"); + + Assert.isNotNull(descriptor.getPluginId(), + PluginDescriptorKey.PLUGIN_ID + "不能为空"); + + Assert.isNotNull(descriptor.getPluginBootstrapClass(), + PluginDescriptorKey.PLUGIN_BOOTSTRAP_CLASS + "不能为空"); + + Assert.isNotNull(descriptor.getPluginVersion(), + PluginDescriptorKey.PLUGIN_VERSION + "不能为空"); + + String illegal = PackageStructure.getIllegal(descriptor.getPluginId()); + if(illegal != null){ + throw new PluginException(descriptor, "插件id不能包含:" + illegal); + } + illegal = PackageStructure.getIllegal(descriptor.getPluginVersion()); + if(illegal != null){ + throw new PluginException(descriptor, "插件版本号不能包含:" + illegal); + } + } + + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/checker/DefaultPluginLauncherChecker.java b/spring-brick/src/main/java/com/gitee/starblues/core/checker/DefaultPluginLauncherChecker.java new file mode 100644 index 0000000000000000000000000000000000000000..d93de10c0a3a245f3d1fd50419b2cfebc16441b6 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/checker/DefaultPluginLauncherChecker.java @@ -0,0 +1,92 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.checker; + +import com.gitee.starblues.common.Constants; +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.core.PluginState; +import com.gitee.starblues.core.RealizeProvider; +import com.gitee.starblues.core.exception.PluginDisabledException; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.utils.ObjectUtils; + +/** + * @author starBlues + * @version 3.0.0 + */ +public class DefaultPluginLauncherChecker implements PluginLauncherChecker { + + + protected final RealizeProvider realizeProvider; + protected final IntegrationConfiguration configuration; + + + public DefaultPluginLauncherChecker(RealizeProvider realizeProvider, + IntegrationConfiguration configuration) { + this.realizeProvider = realizeProvider; + this.configuration = configuration; + } + + + @Override + public void checkCanStart(PluginInfo pluginInfo) throws PluginException { + PluginDisabledException.checkDisabled(pluginInfo, configuration, "启动"); + PluginState pluginState = pluginInfo.getPluginState(); + if(pluginState == PluginState.STARTED){ + throw new PluginException(pluginInfo.getPluginDescriptor(), "已经启动, 不能再启动"); + } + checkRequiresVersion(pluginInfo); + } + + + @Override + public void checkCanStop(PluginInfo pluginInfo) throws PluginException { + PluginDisabledException.checkDisabled(pluginInfo, configuration, "停止"); + PluginState pluginState = pluginInfo.getPluginState(); + if(pluginState != PluginState.STARTED){ + throw new PluginException(pluginInfo.getPluginDescriptor(), "没有启动, 不能停止"); + } + } + + /** + * 检查可安装到主程序的版本 + * @param pluginInfo pluginInfo + */ + private void checkRequiresVersion(PluginInfo pluginInfo){ + String version = configuration.version(); + if(ObjectUtils.isEmpty(version) || Constants.ALLOW_VERSION.equals(version)){ + return; + } + String requires = pluginInfo.getPluginDescriptor().getRequires(); + boolean exactVersion = configuration.exactVersion(); + int compareVersion = realizeProvider.getVersionInspector().compareTo(requires, version); + if(exactVersion && compareVersion != 0){ + String error = "需要安装到[" + requires + "]版本的主程序, 但当前主程序版本为[" + version + "]"; + throw new PluginException(pluginInfo.getPluginDescriptor(), error); + } + if(compareVersion > 0){ + String error = "需要安装到小于等于[" + requires + "]版本的主程序, 但当前主程序版本为[" + version + "]"; + throw new PluginException(pluginInfo.getPluginDescriptor(), error); + } + } + + + + + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/checker/DependencyPluginLauncherChecker.java b/spring-brick/src/main/java/com/gitee/starblues/core/checker/DependencyPluginLauncherChecker.java new file mode 100644 index 0000000000000000000000000000000000000000..3691f2c0336925fdb68527043cc18fdf86a84807 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/checker/DependencyPluginLauncherChecker.java @@ -0,0 +1,124 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.checker; + +import com.gitee.starblues.common.Constants; +import com.gitee.starblues.common.DependencyPlugin; +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.core.PluginManager; +import com.gitee.starblues.core.PluginState; +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.core.exception.PluginDisabledException; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.utils.MsgUtils; +import com.gitee.starblues.utils.ObjectUtils; + +import java.util.List; +import java.util.Objects; + +/** + * 插件依赖的插件检查者 + * @author starBlues + * @version 3.0.0 + */ +public class DependencyPluginLauncherChecker implements PluginLauncherChecker { + + private final PluginManager pluginManager; + + public DependencyPluginLauncherChecker(PluginManager pluginManager) { + this.pluginManager = pluginManager; + } + + /** + * 检查插件依赖。如果依赖没有启动, 则自动启动 + * @param pluginInfo 插件信息 + * @throws PluginException PluginException + */ + @Override + public void checkCanStart(PluginInfo pluginInfo) throws PluginException { + List dependencyPlugins = pluginInfo.getPluginDescriptor().getDependencyPlugin(); + if(ObjectUtils.isEmpty(dependencyPlugins)){ + return; + } + PluginDescriptor descriptor = pluginInfo.getPluginDescriptor(); + resolveDependencyPlugin(pluginInfo, (dependencyPlugin, dependencyPluginInfo)->{ + String id = dependencyPlugin.getId(); + String version = dependencyPlugin.getVersion(); + boolean allowAllVersion = Constants.ALLOW_VERSION.equals(version); + + boolean findDependency = false; + if(dependencyPluginInfo != null){ + if(allowAllVersion){ + findDependency = true; + } else { + findDependency = Objects.equals(dependencyPluginInfo.getPluginDescriptor().getPluginVersion(), + descriptor.getPluginVersion()); + } + } + String dependencyPluginUnique = MsgUtils.getPluginUnique(id, allowAllVersion ? null : version); + if(!findDependency){ + throw new PluginException(descriptor, "需要依赖插件[" + dependencyPluginUnique + "]才能启动"); + } + if(dependencyPluginInfo.getPluginState() != PluginState.STARTED){ + // 没有启动的话, 手动启动 + try { + pluginManager.start(dependencyPluginInfo.getPluginId()); + } catch (Exception e){ + if(e instanceof PluginDisabledException){ + // 依赖被禁用, 不能启动 + throw new PluginDisabledException(descriptor, + "依赖的插件[" + dependencyPluginUnique + "]被禁用, 无法启动当前插件"); + } + throw new PluginException(descriptor, + "依赖的插件[" + dependencyPluginUnique + "]启动失败. 无法启动当前插件", e); + } + } + }); + } + + @Override + public void checkCanStop(PluginInfo pluginInfo) throws PluginException { + // 忽略 + } + + private void resolveDependencyPlugin(PluginInfo pluginInfo, ResolveDependencyPlugin resolveDependencyPlugin) + throws PluginException { + List dependencyPlugins = pluginInfo.getPluginDescriptor().getDependencyPlugin(); + if(ObjectUtils.isEmpty(dependencyPlugins)){ + return; + } + for (DependencyPlugin dependencyPlugin : dependencyPlugins) { + if (dependencyPlugin.getOptional()) { + continue; + } + PluginInfo plugin = pluginManager.getPluginInfo(dependencyPlugin.getId()); + resolveDependencyPlugin.process(dependencyPlugin, plugin); + } + } + + @FunctionalInterface + protected interface ResolveDependencyPlugin{ + /** + * 处理依赖的插件 + * @param dependencyPlugin 当前依赖的插件 + * @param dependencyPluginInfo 查询出当前系统安装的依赖的插件信息。可能为空 + * @throws PluginException 处理异常 + */ + void process(DependencyPlugin dependencyPlugin, PluginInfo dependencyPluginInfo) throws PluginException; + } + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/checker/PluginBasicChecker.java b/spring-brick/src/main/java/com/gitee/starblues/core/checker/PluginBasicChecker.java new file mode 100644 index 0000000000000000000000000000000000000000..100c878f4fbb56b71cdc8e0d44becbd038b72ff3 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/checker/PluginBasicChecker.java @@ -0,0 +1,45 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.checker; + +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.core.exception.PluginException; + +import java.nio.file.Path; + +/** + * 插件基本检查者 + * @author starBlues + * @version 3.0.0 + */ +public interface PluginBasicChecker { + + /** + * 根据路径检查 + * @param path path + * @throws Exception 检查异常 + */ + void checkPath(Path path) throws Exception; + + /** + * 检查插件描述是否合法 + * @param descriptor 插件信息 + * @throws PluginException 检查异常 + */ + void checkDescriptor(PluginDescriptor descriptor) throws PluginException; + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/checker/PluginLauncherChecker.java b/spring-brick/src/main/java/com/gitee/starblues/core/checker/PluginLauncherChecker.java new file mode 100644 index 0000000000000000000000000000000000000000..c44f9ae02c1fe14749b1fff2b2704d99ce97bc6a --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/checker/PluginLauncherChecker.java @@ -0,0 +1,44 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.checker; + +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.core.exception.PluginException; + +/** + * 插件启动检查者 + * @author starBlues + * @version 3.0.0 + */ +public interface PluginLauncherChecker { + + /** + * 检查是否能启动 + * @param pluginInfo 插件信息 + * @throws PluginException 不能启动, 抛出PluginException异常即可 + */ + void checkCanStart(PluginInfo pluginInfo) throws PluginException; + + /** + * 检查是否能停止 + * @param pluginInfo 插件信息 + * @throws PluginException 不能停止, 抛出PluginException异常即可 + */ + void checkCanStop(PluginInfo pluginInfo) throws PluginException; + + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/classloader/CacheMainResourceMatcher.java b/spring-brick/src/main/java/com/gitee/starblues/core/classloader/CacheMainResourceMatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..58ed42aadbfc044440a043c45130994db14816fa --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/classloader/CacheMainResourceMatcher.java @@ -0,0 +1,51 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.classloader; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 可缓存的 ResourceMatcher + * @author starBlues + * @version 3.0.0 + */ +public class CacheMainResourceMatcher extends DefaultMainResourceMatcher implements AutoCloseable { + + private final Map resourceUrlMatchCache = new ConcurrentHashMap<>(); + + public CacheMainResourceMatcher(MainResourcePatternDefiner mainResourcePatternDefiner) { + super(mainResourcePatternDefiner); + } + + @Override + public boolean match(String resourceUrl) { + Boolean match = resourceUrlMatchCache.get(resourceUrl); + if(match == null){ + match = super.match(resourceUrl); + resourceUrlMatchCache.put(resourceUrl, match); + } + return match; + } + + + @Override + public void close() throws Exception { + resourceUrlMatchCache.clear(); + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/classloader/DefaultMainResourceMatcher.java b/spring-brick/src/main/java/com/gitee/starblues/core/classloader/DefaultMainResourceMatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..29c95de46d55e68278f66866cde19b895916789d --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/classloader/DefaultMainResourceMatcher.java @@ -0,0 +1,85 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.classloader; + +import com.gitee.starblues.utils.ObjectUtils; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; + +import java.util.Collection; +import java.util.Set; + +/** + * 默认的主程序资源匹配者 + * @author starBlues + * @version 3.0.0 + */ +public class DefaultMainResourceMatcher implements MainResourceMatcher{ + + private final Set includePatterns; + private final Set excludePatterns; + + private final PathMatcher pathMatcher; + + public DefaultMainResourceMatcher(MainResourcePatternDefiner mainResourcePatternDefiner) { + this.includePatterns = mainResourcePatternDefiner.getIncludePatterns(); + this.excludePatterns = mainResourcePatternDefiner.getExcludePatterns(); + this.pathMatcher = new AntPathMatcher(); + } + + @Override + public boolean match(String resourceUrl) { + return match(includePatterns, resourceUrl); + } + + private boolean match(Collection patterns, String url){ + if(ObjectUtils.isEmpty(patterns) || ObjectUtils.isEmpty(url)){ + return false; + } + url = formatUrl(url); + for (String pattern : patterns) { + boolean match = pathMatcher.match(pattern, url); + if(match){ + return !excludeMatch(excludePatterns, url); + } + } + return false; + } + + private boolean excludeMatch(Collection patterns, String url){ + if(ObjectUtils.isEmpty(patterns) || ObjectUtils.isEmpty(url)){ + return false; + } + url = formatUrl(url); + for (String pattern : patterns) { + boolean match = pathMatcher.match(pattern, url); + if(match){ + return true; + } + } + return false; + } + + + private String formatUrl(String url){ + url = url.replace("\\", AntPathMatcher.DEFAULT_PATH_SEPARATOR); + if(url.startsWith(AntPathMatcher.DEFAULT_PATH_SEPARATOR)){ + url = url.substring(url.indexOf(AntPathMatcher.DEFAULT_PATH_SEPARATOR) + 1); + } + return url; + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/classloader/EmptyMainResourcePatternDefiner.java b/spring-brick/src/main/java/com/gitee/starblues/core/classloader/EmptyMainResourcePatternDefiner.java new file mode 100644 index 0000000000000000000000000000000000000000..57ca80a10ac5c4951cfee8c96e70ef309f970bd8 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/classloader/EmptyMainResourcePatternDefiner.java @@ -0,0 +1,37 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.classloader; + +import java.util.Set; + +/** + * 空的 MainResourceDefiner + * @author starBlues + * @version 3.0.0 + */ +public class EmptyMainResourcePatternDefiner implements MainResourcePatternDefiner { + + @Override + public Set getIncludePatterns() { + return null; + } + + @Override + public Set getExcludePatterns() { + return null; + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/classloader/MainResourceMatcher.java b/spring-brick/src/main/java/com/gitee/starblues/core/classloader/MainResourceMatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..5977f35f51c2f3daf933c2a20ee6d46ab8d1fd7f --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/classloader/MainResourceMatcher.java @@ -0,0 +1,33 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.classloader; + +/** + * 主程序资源匹配者 + * @author starBlues + * @version 3.0.0 + */ +public interface MainResourceMatcher { + + /** + * 匹配主程序资源 + * @param resourceUrl 主程序资源url + * @return true 匹配成功, false 匹配失败 + */ + boolean match(String resourceUrl); + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/classloader/MainResourcePatternDefiner.java b/spring-brick/src/main/java/com/gitee/starblues/core/classloader/MainResourcePatternDefiner.java new file mode 100644 index 0000000000000000000000000000000000000000..5759573f88aaf28dd30cc5d5ff4bbea8d61892b0 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/classloader/MainResourcePatternDefiner.java @@ -0,0 +1,42 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.classloader; + +import java.util.Set; + +/** + * 主程序定义者, 从主程序加载资源的定义者 + * @author starBlues + * @version 3.0.0 + */ +public interface MainResourcePatternDefiner { + + /** + * 包含资源名称. + * @return 资源名称集合 + */ + Set getIncludePatterns(); + + /** + * 排除资源 + * @return String + */ + Set getExcludePatterns(); + + + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/classloader/NestedPluginJarResourceLoader.java b/spring-brick/src/main/java/com/gitee/starblues/core/classloader/NestedPluginJarResourceLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..8bc4af1257ee2a395b9b11f3ede57efabf7cc2b3 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/classloader/NestedPluginJarResourceLoader.java @@ -0,0 +1,101 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.classloader; + + +import com.gitee.starblues.core.descriptor.PluginLibInfo; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.loader.classloader.*; +import com.gitee.starblues.loader.classloader.resource.loader.*; +import com.gitee.starblues.loader.classloader.resource.storage.ResourceStorage; +import com.gitee.starblues.loader.launcher.ResourceLoaderFactoryGetter; + +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarInputStream; +import java.util.zip.ZipEntry; + +/** + * 嵌套插件jar加载者 + * @author starBlues + * @version 3.0.0 + */ +public class NestedPluginJarResourceLoader extends AbstractResourceLoader { + + private final InsidePluginDescriptor pluginDescriptor; + private final GenericClassLoader parentClassLoader; + private final ResourceLoaderFactory resourceLoaderFactory; + + + public NestedPluginJarResourceLoader(InsidePluginDescriptor pluginDescriptor, + GenericClassLoader parentClassLoader, + ResourceLoaderFactory resourceLoaderFactory) throws Exception { + super(new URL("jar:" + pluginDescriptor.getInsidePluginPath().toUri().toURL() + "!/")); + this.pluginDescriptor = pluginDescriptor; + this.parentClassLoader = parentClassLoader; + this.resourceLoaderFactory = resourceLoaderFactory; + } + + @Override + protected void loadOfChild(ResourceStorage resourceStorage) throws Exception { + try (JarFile jarFile = new JarFile(pluginDescriptor.getInsidePluginPath().toFile())) { + addClassPath(resourceStorage, jarFile); + addLib(jarFile); + } + } + + private void addClassPath(ResourceStorage resourceStorage, JarFile jarFile) throws Exception{ + String classesPath = pluginDescriptor.getPluginClassPath(); + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()){ + JarEntry jarEntry = entries.nextElement(); + if(!jarEntry.getName().startsWith(classesPath)){ + continue; + } + String realName = jarEntry.getName().replace(classesPath, ""); + URL url = new URL(baseUrl.toString() + jarEntry.getName()); + resourceStorage.add(realName, url, ()->{ + return getClassBytes(realName, jarFile.getInputStream(jarEntry), true); + }); + } + } + + private void addLib(JarFile jarFile) throws Exception { + JarEntry jarEntry = null; + Set pluginLibInfos = pluginDescriptor.getPluginLibInfo(); + for (PluginLibInfo pluginLibInfo : pluginLibInfos) { + jarEntry = jarFile.getJarEntry(pluginLibInfo.getPath()); + if (jarEntry.getMethod() != ZipEntry.STORED) { + throw new PluginException("插件依赖压缩方式错误, 必须是: 存储(stored)压缩方式"); + } + InputStream jarFileInputStream = jarFile.getInputStream(jarEntry); + URL url = new URL(baseUrl.toString() + pluginLibInfo.getPath() + "!/"); + if(pluginLibInfo.isLoadToMain()){ + parentClassLoader.addResource(new JarResourceLoader(url, new JarInputStream(jarFileInputStream))); + } else { + JarResourceLoader jarResourceLoader = new JarResourceLoader(url, new JarInputStream(jarFileInputStream)); + resourceLoaderFactory.addResource(jarResourceLoader); + } + } + } + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/classloader/PluginClassLoader.java b/spring-brick/src/main/java/com/gitee/starblues/core/classloader/PluginClassLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..a76be2cb28b39ebdf4a0520d4bfb8b0bf381ecf8 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/classloader/PluginClassLoader.java @@ -0,0 +1,151 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.classloader; + +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.core.descriptor.PluginLibInfo; +import com.gitee.starblues.core.descriptor.PluginType; +import com.gitee.starblues.loader.classloader.*; +import com.gitee.starblues.loader.classloader.resource.loader.ResourceLoaderFactory; +import com.gitee.starblues.utils.Assert; +import com.gitee.starblues.utils.FilesUtils; +import com.gitee.starblues.utils.ObjectUtils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import java.util.Set; + +/** + * 插件 classLoader + * @author starBlues + * @version 3.0.0 + */ +public class PluginClassLoader extends GenericClassLoader { + + private final GenericClassLoader parentClassLoader; + private MainResourceMatcher mainResourceMatcher; + + public PluginClassLoader(String name, GenericClassLoader parentClassLoader, MainResourcePatternDefiner patternDefiner, + ResourceLoaderFactory resourceLoaderFactory) { + super(name, parentClassLoader, resourceLoaderFactory); + this.parentClassLoader = parentClassLoader; + if(patternDefiner != null){ + setMainResourceMatcher(new CacheMainResourceMatcher(patternDefiner)); + } else { + setMainResourceMatcher(new ProhibitMainResourceMatcher()); + } + } + + public void setMainResourceMatcher(MainResourceMatcher mainResourceMatcher){ + this.mainResourceMatcher = Assert.isNotNull(mainResourceMatcher, "参数 mainResourceMatcher 不能为空"); + } + + public void addResource(InsidePluginDescriptor descriptor) throws Exception { + PluginType pluginType = descriptor.getType(); + if(pluginType == PluginType.JAR || pluginType == PluginType.ZIP){ + NestedPluginJarResourceLoader resourceLoader = + new NestedPluginJarResourceLoader(descriptor, parentClassLoader, resourceLoaderFactory); + resourceLoaderFactory.addResource(resourceLoader); + } else { + addClasspath(descriptor); + addLibFile(descriptor); + } + } + + private void addClasspath(InsidePluginDescriptor pluginDescriptor) throws Exception { + String pluginClassPath = pluginDescriptor.getPluginClassPath(); + File existFile = FilesUtils.getExistFile(pluginClassPath); + if(existFile != null){ + addResource(existFile); + } + } + + private void addLibFile(InsidePluginDescriptor pluginDescriptor) throws Exception { + Set pluginLibInfos = pluginDescriptor.getPluginLibInfo(); + if(ObjectUtils.isEmpty(pluginLibInfos)){ + return; + } + for (PluginLibInfo pluginLibInfo : pluginLibInfos) { + File existFile = FilesUtils.getExistFile(pluginLibInfo.getPath()); + if(existFile != null){ + if(pluginLibInfo.isLoadToMain()){ + // 加载到主程序中 + parentClassLoader.addResource(existFile); + } else { + addResource(existFile); + } + } + } + } + + + @Override + protected Class findClassFromParent(String className) throws ClassNotFoundException { + if(mainResourceMatcher.match(className.replace(".", "/"))){ + try { + return super.findClassFromParent(className); + } catch (Exception e){ + // 忽略 + } + } + return null; + } + + @Override + protected InputStream findInputStreamFromParent(String name) { + if(mainResourceMatcher.match(name)){ + try { + return super.findInputStreamFromParent(name); + } catch (Exception e){ + // 忽略 + } + } + return null; + } + + @Override + protected URL findResourceFromParent(String name) { + if(mainResourceMatcher.match(name)){ + return super.findResourceFromParent(name); + } + return null; + } + + @Override + protected Enumeration findResourcesFromParent(String name) throws IOException { + if(mainResourceMatcher.match(name)){ + return super.findResourcesFromParent(name); + } + return null; + } + + @Override + public void close() throws IOException { + super.close(); + if(mainResourceMatcher instanceof AutoCloseable){ + try { + ((AutoCloseable) mainResourceMatcher).close(); + } catch (Exception e){ + throw new IOException(e); + } + } + } + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/classloader/ProhibitMainResourceMatcher.java b/spring-brick/src/main/java/com/gitee/starblues/core/classloader/ProhibitMainResourceMatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..296dacf2d0a58c9cdbdd903f12d4f32eafdde26d --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/classloader/ProhibitMainResourceMatcher.java @@ -0,0 +1,31 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.classloader; + +/** + * 禁止匹配所有主程序资源 + * @author starBlues + * @version 3.0.0 + */ +public class ProhibitMainResourceMatcher implements MainResourceMatcher{ + + @Override + public boolean match(String resourceUrl) { + return false; + } + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/AbstractPluginDescriptorLoader.java b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/AbstractPluginDescriptorLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..14b11d814e552c2e2672e35bf0be60a04bc5fd39 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/AbstractPluginDescriptorLoader.java @@ -0,0 +1,189 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.descriptor; + + +import com.gitee.starblues.common.*; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.utils.FilesUtils; +import com.gitee.starblues.utils.PropertiesUtils; +import com.gitee.starblues.utils.ObjectUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import static com.gitee.starblues.common.PackageStructure.MANIFEST; +import static com.gitee.starblues.common.PluginDescriptorKey.*; +import static com.gitee.starblues.utils.PropertiesUtils.getValue; + +/** + * 抽象的 PluginDescriptorLoader + * @author starBlues + * @version 3.0.0 + */ +public abstract class AbstractPluginDescriptorLoader implements PluginDescriptorLoader{ + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + + @Override + public InsidePluginDescriptor load(Path location) throws PluginException { + PluginMeta pluginMeta = null; + try { + pluginMeta = getPluginMetaInfo(location); + if(pluginMeta == null || pluginMeta.getPluginMetaInfo() == null){ + logger.debug("路径[{}]没有发现插件配置信息", location); + return null; + } + return create(pluginMeta, location); + } catch (Exception e) { + return null; + } + } + + @Override + public void close() throws Exception { + + } + + /** + * 子类获取插件信息 + * @param location 路径 + * @return Properties + * @throws Exception 异常 + */ + protected abstract PluginMeta getPluginMetaInfo(Path location) throws Exception; + + protected DefaultInsidePluginDescriptor create(PluginMeta pluginMeta, Path path) throws Exception{ + Properties properties = pluginMeta.getPluginMetaInfo(); + DefaultInsidePluginDescriptor descriptor = new DefaultInsidePluginDescriptor( + getValue(properties, PLUGIN_ID), + getValue(properties, PLUGIN_VERSION), + getValue(properties, PLUGIN_BOOTSTRAP_CLASS), + path + ); + descriptor.setType(PluginType.byName(pluginMeta.getPackageType())); + + PluginResourcesConfig pluginResourcesConfig = getPluginResourcesConfig(path, properties); + + descriptor.setPluginLibInfo(getPluginLibInfo(pluginResourcesConfig.getDependenciesIndex())); + descriptor.setIncludeMainResourcePatterns(pluginResourcesConfig.getLoadMainResourceIncludes()); + descriptor.setExcludeMainResourcePatterns(pluginResourcesConfig.getLoadMainResourceExcludes()); + + descriptor.setProperties(properties); + descriptor.setPluginClassPath(getValue(properties, PLUGIN_PATH, false)); + descriptor.setDescription(getValue(properties, PLUGIN_DESCRIPTION, false)); + descriptor.setRequires(getValue(properties, PLUGIN_REQUIRES, false)); + descriptor.setProvider(getValue(properties, PLUGIN_PROVIDER, false)); + descriptor.setLicense(getValue(properties, PLUGIN_LICENSE, false)); + descriptor.setConfigFileName(getValue(properties, PLUGIN_CONFIG_FILE_NAME, false)); + descriptor.setConfigFileLocation(getValue(properties, PLUGIN_CONFIG_FILE_LOCATION, false)); + descriptor.setArgs(getValue(properties, PLUGIN_ARGS, false)); + + descriptor.setDependencyPlugins(getPluginDependency(properties)); + return descriptor; + } + + + protected List getPluginDependency(Properties properties){ + return AbstractDependencyPlugin.toList(getValue(properties, PLUGIN_DEPENDENCIES, false), + DefaultDependencyPlugin::new); + } + + protected PluginResourcesConfig getPluginResourcesConfig(Path path, Properties properties) throws Exception{ + String libIndex = getValue(properties, PLUGIN_RESOURCES_CONFIG); + if(ObjectUtils.isEmpty(libIndex)){ + return new PluginResourcesConfig(); + } + File file = new File(libIndex); + if(!file.exists()){ + // 如果绝对路径不存在, 则判断相对路径 + String pluginPath = getValue(properties, PLUGIN_PATH); + file = new File(FilesUtils.joiningFilePath(pluginPath, libIndex)); + } + if(!file.exists()){ + // 都不存在, 则返回为空 + return new PluginResourcesConfig(); + } + try { + List lines = Files.readAllLines(file.toPath()); + return PluginResourcesConfig.parse(lines); + } catch (IOException e) { + throw new Exception("Load plugin lib index path failure. " + libIndex, e); + } + } + + protected Set getPluginLibInfo(Set dependenciesIndex){ + if(ObjectUtils.isEmpty(dependenciesIndex)){ + return Collections.emptySet(); + } + Set pluginLibInfos = new HashSet<>(dependenciesIndex.size()); + File file = new File(""); + String absolutePath = file.getAbsolutePath(); + for (String index : dependenciesIndex) { + String libPath; + boolean loadToMain; + if(index.endsWith(Constants.LOAD_TO_MAIN_SIGN)){ + libPath = index.substring(0, index.lastIndexOf(Constants.LOAD_TO_MAIN_SIGN)); + loadToMain = true; + } else { + libPath = index; + loadToMain = false; + } + pluginLibInfos.add(new PluginLibInfo(FilesUtils.resolveRelativePath(absolutePath, libPath), loadToMain)); + } + return pluginLibInfos; + } + + protected Manifest getManifest(InputStream inputStream) throws Exception{ + Manifest manifest = new Manifest(); + try { + manifest.read(inputStream); + return manifest; + } finally { + inputStream.close(); + } + } + + protected Properties getProperties(InputStream inputStream) throws Exception{ + Properties properties = new Properties(); + try (InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);){ + properties.load(reader); + return properties; + } + } + + @AllArgsConstructor + @Getter + public static class PluginMeta{ + private final String packageType; + private final Properties pluginMetaInfo; + } + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ComposeDescriptorLoader.java b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ComposeDescriptorLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..475815e0df8d3615ab9ffcca814e6bd0c49ee9a6 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ComposeDescriptorLoader.java @@ -0,0 +1,75 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.descriptor; + +import com.gitee.starblues.core.checker.PluginBasicChecker; +import com.gitee.starblues.core.exception.PluginException; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +/** + * 组合插件描述加载者 + * @author starBlues + * @version 3.0.0 + */ +public class ComposeDescriptorLoader implements PluginDescriptorLoader{ + + private final List pluginDescriptorLoaders = new ArrayList<>(); + + private final PluginBasicChecker pluginChecker; + + public ComposeDescriptorLoader(PluginBasicChecker pluginChecker) { + this.pluginChecker = pluginChecker; + addDefaultLoader(); + } + + protected void addDefaultLoader(){ + addLoader(new DevPluginDescriptorLoader()); + addLoader(new ProdPluginDescriptorLoader()); + } + + + public void addLoader(PluginDescriptorLoader descriptorLoader){ + if(descriptorLoader != null){ + pluginDescriptorLoaders.add(descriptorLoader); + } + } + + + @Override + public InsidePluginDescriptor load(Path location) throws PluginException { + for (PluginDescriptorLoader pluginDescriptorLoader : pluginDescriptorLoaders) { + try { + InsidePluginDescriptor pluginDescriptor = pluginDescriptorLoader.load(location); + if(pluginDescriptor != null){ + pluginChecker.checkDescriptor(pluginDescriptor); + return pluginDescriptor; + } + } catch (Exception e){ + // 忽略异常 + } + } + return null; + } + + @Override + public void close() throws Exception { + + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DefaultDependencyPlugin.java b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DefaultDependencyPlugin.java new file mode 100644 index 0000000000000000000000000000000000000000..bc5fdee4cfa4397bcc053c80ff51043ac72ea230 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DefaultDependencyPlugin.java @@ -0,0 +1,66 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.descriptor; + +import com.gitee.starblues.common.AbstractDependencyPlugin; + +/** + * 依赖的插件信息 + * @author starBlues + * @version 3.0.0 + */ +public class DefaultDependencyPlugin extends AbstractDependencyPlugin { + + private String id; + private String version; + private Boolean optional = false; + + @Override + public String getId() { + return id; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public Boolean getOptional() { + if(optional == null){ + return false; + } + return optional; + } + @Override + public void setId(String id) { + this.id = id; + } + + @Override + public void setVersion(String version) { + this.version = version; + } + + @Override + public void setOptional(Boolean optional) { + if(optional == null){ + optional = false; + } + this.optional = optional; + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DefaultInsidePluginDescriptor.java b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DefaultInsidePluginDescriptor.java new file mode 100644 index 0000000000000000000000000000000000000000..ced611f24890c5333391ec308aa878af06c0bd55 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DefaultInsidePluginDescriptor.java @@ -0,0 +1,128 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.descriptor; + +import lombok.Setter; + +import java.nio.file.Path; +import java.util.Properties; +import java.util.Set; +import java.util.jar.Manifest; + +/** + * 内部的默认插件描述者 + * @author starBlues + * @version 3.0.0 + */ +public class DefaultInsidePluginDescriptor extends DefaultPluginDescriptor implements InsidePluginDescriptor { + + private final Path pluginPath; + private final String pluginFileName; + + @Setter + private String pluginClassPath; + @Setter + private Properties properties; + @Setter + private String configFileName; + @Setter + private String configFileLocation; + @Setter + private String args; + @Setter + private Set pluginLibInfo; + @Setter + private Set includeMainResourcePatterns; + @Setter + private Set excludeMainResourcePatterns; + + public DefaultInsidePluginDescriptor(String pluginId, String pluginVersion, String pluginClass, Path pluginPath) { + super(pluginId, pluginVersion, pluginClass, pluginPath.toAbsolutePath().toString()); + this.pluginPath = pluginPath; + this.pluginFileName = pluginPath.toFile().getName(); + } + + + @Override + public String getPluginClassPath() { + return pluginClassPath; + } + + @Override + public Set getPluginLibInfo() { + return pluginLibInfo; + } + + @Override + public Set getIncludeMainResourcePatterns() { + return includeMainResourcePatterns; + } + + @Override + public Set getExcludeMainResourcePatterns() { + return excludeMainResourcePatterns; + } + + @Override + public String getConfigFileName() { + return configFileName; + } + + @Override + public String getConfigFileLocation() { + return configFileLocation; + } + + @Override + public String getArgs() { + return args; + } + + @Override + public Path getInsidePluginPath() { + return pluginPath; + } + + @Override + public String getPluginFileName() { + return pluginFileName; + } + + @Override + public Properties getProperties() { + return properties; + } + + @Override + public PluginDescriptor toPluginDescriptor() { + Path pluginPath = getInsidePluginPath(); + if(getType() == PluginType.DEV) { + // dev模式 插件路径展示项目目录 + pluginPath = pluginPath.getParent().getParent(); + } + DefaultPluginDescriptor descriptor = new DefaultPluginDescriptor( + getPluginId(), getPluginVersion(), getPluginBootstrapClass(), pluginPath.toAbsolutePath().toString() + ); + descriptor.setType(getType()); + descriptor.setDescription(getDescription()); + descriptor.setProvider(getProvider()); + descriptor.setRequires(getRequires()); + descriptor.setLicense(getLicense()); + return descriptor; + } + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DefaultPluginDescriptor.java b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DefaultPluginDescriptor.java new file mode 100644 index 0000000000000000000000000000000000000000..d642dbd707f12f7029e87d19d8981edc2f9fdcc4 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DefaultPluginDescriptor.java @@ -0,0 +1,145 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.descriptor; + +import com.gitee.starblues.common.DependencyPlugin; +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.utils.Assert; + +import java.util.List; + +import static com.gitee.starblues.common.PluginDescriptorKey.*; + +/** + * 默认插件描述者 + * + * @author starBlues + * @version 3.0.0 + */ +public class DefaultPluginDescriptor implements PluginDescriptor { + + private final String pluginId; + private final String pluginVersion; + private final String pluginBootstrapClass; + private final String pluginPath; + + + private PluginType type; + private String description; + private String requires; + private String provider; + private String license; + + private List dependencyPlugins; + + public DefaultPluginDescriptor(String pluginId, String pluginVersion, + String pluginClass, String pluginPath) { + this.pluginId = Assert.isNotEmpty(pluginId, PLUGIN_ID + "不能为空"); + this.pluginVersion = Assert.isNotEmpty(pluginVersion, PLUGIN_VERSION + "不能为空"); + this.pluginBootstrapClass = Assert.isNotEmpty(pluginClass, PLUGIN_BOOTSTRAP_CLASS + "不能为空"); + this.pluginPath = Assert.isNotNull(pluginPath, "插件路径[pluginPath]不能为空"); + check(); + } + + void setDescription(String description) { + this.description = description; + } + + void setRequires(String requires) { + this.requires = requires; + } + + void setProvider(String provider) { + this.provider = provider; + } + + void setLicense(String license) { + this.license = license; + } + + void setType(PluginType type) { + this.type = type; + } + + void setDependencyPlugins(List dependencyPlugins) { + this.dependencyPlugins = dependencyPlugins; + } + + @Override + public String getPluginId() { + return pluginId; + } + + @Override + public String getPluginVersion() { + return pluginVersion; + } + + @Override + public String getPluginBootstrapClass() { + return pluginBootstrapClass; + } + + @Override + public String getPluginPath() { + return pluginPath; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public String getRequires() { + return requires; + } + + @Override + public String getProvider() { + return provider; + } + + @Override + public String getLicense() { + return license; + } + + @Override + public List getDependencyPlugin() { + return dependencyPlugins; + } + + + @Override + public PluginType getType() { + return type; + } + + private void check(){ + String illegal = PackageStructure.getIllegal(pluginId); + if(illegal != null){ + throw new PluginException(this, "插件id不能包含:" + illegal); + } + illegal = PackageStructure.getIllegal(pluginVersion); + if(illegal != null){ + throw new PluginException(this, "插件版本号不能包含:" + illegal); + } + } + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DevPluginDescriptorLoader.java b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DevPluginDescriptorLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..3b94a9fbab69ab0814e09b8fb4d3b668d61b9fef --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DevPluginDescriptorLoader.java @@ -0,0 +1,58 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.descriptor; + +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.common.PackageType; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Properties; +import java.util.jar.Manifest; + +/** + * 开发环境 PluginDescriptorLoader 加载者 + * @author starBlues + * @version 3.0.0 + */ +public class DevPluginDescriptorLoader extends AbstractPluginDescriptorLoader{ + + @Override + protected PluginMeta getPluginMetaInfo(Path location) throws Exception { + String pluginMetaPath = location.toString() + File.separator + PackageStructure.PLUGIN_META_NAME; + File file = new File(pluginMetaPath); + if(!file.exists()){ + return null; + } + Path path = Paths.get(pluginMetaPath); + Properties properties = super.getProperties(Files.newInputStream(path)); + if(properties.isEmpty()){ + return null; + } + return new PluginMeta(PackageType.PLUGIN_PACKAGE_TYPE_DEV, properties); + } + + @Override + protected DefaultInsidePluginDescriptor create(PluginMeta pluginMeta, Path path) throws Exception { + final DefaultInsidePluginDescriptor descriptor = super.create(pluginMeta, path); + descriptor.setType(PluginType.DEV); + return descriptor; + } + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/InsidePluginDescriptor.java b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/InsidePluginDescriptor.java new file mode 100644 index 0000000000000000000000000000000000000000..5064060fd7a6cfbcd65de7311bffd667d2c1349b --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/InsidePluginDescriptor.java @@ -0,0 +1,100 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.descriptor; + +import java.nio.file.Path; +import java.util.Properties; +import java.util.Set; +import java.util.jar.Manifest; + +/** + * 内部的PluginDescriptor + * @author starBlues + * @version 3.0.0 + */ +public interface InsidePluginDescriptor extends PluginDescriptor{ + + /** + * 得到插件的 Properties 配置 + * @return Properties + */ + Properties getProperties(); + + /** + * 获取插件配置文件名称。 + * 和 getConfigFileLocation 配置二选一, 如果都有值则默认使用 getConfigFileName + * @return String + */ + String getConfigFileName(); + + /** + * 获取插件配置文件路径。 + * 和 getConfigFileName 配置二选一, 如果都有值则默认使用 getConfigFileName + * @return String + */ + String getConfigFileLocation(); + + /** + * 得到插件启动时参数 + * @return String + */ + String getArgs(); + + /** + * 得到内部的插件路径 + * @return Path + */ + Path getInsidePluginPath(); + + /** + * 获取插件文件名称 + * @return String + */ + String getPluginFileName(); + + + /** + * 获取插件classes path路径 + * @return Path + */ + String getPluginClassPath(); + + /** + * 获取插件依赖的路径 + * @return String + */ + Set getPluginLibInfo(); + + /** + * 设置当前插件包含主程序加载资源的匹配 + * @return Set + */ + Set getIncludeMainResourcePatterns(); + + /** + * 设置当前插件排除从主程序加载资源的匹配 + * @return Set + */ + Set getExcludeMainResourcePatterns(); + + /** + * 转换为 PluginDescriptor + * @return PluginDescriptor + */ + PluginDescriptor toPluginDescriptor(); + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginDescriptor.java b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginDescriptor.java new file mode 100644 index 0000000000000000000000000000000000000000..4e125bbf2c913526cc3f44f66bae7b419f0ffa40 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginDescriptor.java @@ -0,0 +1,91 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.descriptor; + +import com.gitee.starblues.common.DependencyPlugin; + +import java.util.List; + +/** + * 插件信息 + * @author starBlues + * @version 3.0.0 + */ +public interface PluginDescriptor { + + /** + * 获取插件id + * @return String + */ + String getPluginId(); + + /** + * 获取插件版本 + * @return String + */ + String getPluginVersion(); + + /** + * 获取插件引导类 + * @return String + */ + String getPluginBootstrapClass(); + + /** + * 获取插件路径 + * @return Path + */ + String getPluginPath(); + + /** + * 获取插件描述 + * @return String + */ + String getDescription(); + + /** + * 获取插件所能安装到主程序的版本 + * @return String + */ + String getRequires(); + + /** + * 获取插件提供开发者 + * @return String + */ + String getProvider(); + + /** + * 获取插件 license + * @return String + */ + String getLicense(); + + /** + * 获取当前插件依赖 + * @return List + */ + List getDependencyPlugin(); + + /** + * 得到插件类型 + * @return 插件类型 + */ + PluginType getType(); + + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginDescriptorLoader.java b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginDescriptorLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..e69c34ea7a871500af7140b9b08ae40f4e086cc1 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginDescriptorLoader.java @@ -0,0 +1,41 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.descriptor; + +import com.gitee.starblues.core.exception.PluginException; + +import java.nio.file.Path; + +/** + * 插件描述加载者 + * @author starBlues + * @version 3.0.0 + */ +public interface PluginDescriptorLoader extends AutoCloseable{ + + + + /** + * 加载 PluginDescriptor + * @param location 引导配置文件路径 + * @return PluginDescriptor + * @throws PluginException 加载异常 + */ + InsidePluginDescriptor load(Path location) throws PluginException; + + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginLibInfo.java b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginLibInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..dbfa29e9a42a760a5af4b2074dbae6f932702338 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginLibInfo.java @@ -0,0 +1,45 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.descriptor; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +/** + * 插件依赖包信息 + * @author starBlues + * @version 3.0.0 + */ +@AllArgsConstructor +@Getter +@EqualsAndHashCode +@ToString +public class PluginLibInfo { + + /** + * 路径 + */ + private final String path; + + /** + * 是否加载到主程序中 + */ + private final boolean loadToMain; + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginResourcesConfig.java b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginResourcesConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..5dc88a6ab62739ac6a464a6f7653974b3f36a0a4 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginResourcesConfig.java @@ -0,0 +1,105 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.descriptor; + +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.utils.ObjectUtils; + +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +/** + * 插件 ResourcesDefine 文件定义 + * @author starBlues + * @version 3.0.0 + */ +public class PluginResourcesConfig { + + private Set dependenciesIndex; + private Set loadMainResourceIncludes; + private Set loadMainResourceExcludes; + + public static PluginResourcesConfig parse(List fileLines){ + final PluginResourcesConfig pluginResourcesConfig = new PluginResourcesConfig(); + if(ObjectUtils.isEmpty(fileLines)){ + return pluginResourcesConfig; + } + + Set dependenciesIndex = new HashSet<>(); + Set loadMainResourceIncludes = new HashSet<>(); + Set loadMainResourceExcludes = new HashSet<>(); + + int i = 0; + + for (String fileLine : fileLines) { + if(ObjectUtils.isEmpty(fileLine)){ + continue; + } + if(Objects.equals(fileLine, PackageStructure.RESOURCES_DEFINE_DEPENDENCIES)){ + i = 1; + continue; + } else if(Objects.equals(fileLine, PackageStructure.RESOURCES_DEFINE_LOAD_MAIN_INCLUDES)){ + i = 2; + continue; + } else if(Objects.equals(fileLine, PackageStructure.RESOURCES_DEFINE_LOAD_MAIN_EXCLUDES)){ + i = 3; + continue; + } + if(i == 1){ + dependenciesIndex.add(fileLine); + } else if(i == 2){ + loadMainResourceIncludes.add(fileLine); + } else if(i == 3){ + loadMainResourceExcludes.add(fileLine); + } + } + pluginResourcesConfig.setDependenciesIndex(dependenciesIndex); + pluginResourcesConfig.setLoadMainResourceIncludes(loadMainResourceIncludes); + pluginResourcesConfig.setLoadMainResourceExcludes(loadMainResourceExcludes); + return pluginResourcesConfig; + } + + + public Set getDependenciesIndex() { + return dependenciesIndex; + } + + public void setDependenciesIndex(Set dependenciesIndex) { + this.dependenciesIndex = dependenciesIndex; + } + + public Set getLoadMainResourceIncludes() { + return loadMainResourceIncludes; + } + + public void setLoadMainResourceIncludes(Set loadMainResourceIncludes) { + this.loadMainResourceIncludes = loadMainResourceIncludes; + } + + public Set getLoadMainResourceExcludes() { + return loadMainResourceExcludes; + } + + public void setLoadMainResourceExcludes(Set loadMainResourceExcludes) { + this.loadMainResourceExcludes = loadMainResourceExcludes; + } + + + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginType.java b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginType.java new file mode 100644 index 0000000000000000000000000000000000000000..5614e6c8d8008d06e8d5e1893a1370e197250936 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginType.java @@ -0,0 +1,87 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.descriptor; + +import com.gitee.starblues.common.PackageType; +import com.gitee.starblues.core.RuntimeMode; +import com.gitee.starblues.core.exception.PluginException; + +import java.util.Objects; + +/** + * @author starBlues + * @version 3.0.0 + */ +public enum PluginType { + + /** + * 开发模式目录 + */ + DEV(PackageType.PLUGIN_PACKAGE_TYPE_DEV), + + /** + * jar文件 + */ + JAR(PackageType.MAIN_PACKAGE_TYPE_JAR), + + /** + * jar-outer 文件 + */ + JAR_OUTER(PackageType.MAIN_PACKAGE_TYPE_JAR_OUTER), + + /** + * zip 文件 + */ + ZIP(PackageType.PLUGIN_PACKAGE_TYPE_ZIP), + + /** + * zip-outer 文件 + */ + ZIP_OUTER(PackageType.PLUGIN_PACKAGE_TYPE_ZIP_OUTER), + + /** + * 生产模式目录 + */ + DIR(PackageType.PLUGIN_PACKAGE_TYPE_DIR); + + + public final String name; + + PluginType(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static PluginType byName(String packageType){ + if(Objects.equals(packageType, PluginType.DEV.getName())){ + return PluginType.DEV; + } else if(Objects.equals(packageType, PluginType.JAR.getName())){ + return PluginType.JAR; + } else if(Objects.equals(packageType, PluginType.JAR_OUTER.getName())){ + return PluginType.JAR_OUTER; + } else if(Objects.equals(packageType, PluginType.ZIP.getName())){ + return PluginType.ZIP; + } else if(Objects.equals(packageType, PluginType.ZIP_OUTER.getName())){ + return PluginType.ZIP_OUTER; + } else { + throw new PluginException("不能解析'" + packageType + "'打包类型的插件"); + } + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ProdDirPluginDescriptorLoader.java b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ProdDirPluginDescriptorLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..f4547abf777f270089e318813fdbb58229231cfd --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ProdDirPluginDescriptorLoader.java @@ -0,0 +1,142 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.descriptor; + +import com.gitee.starblues.common.ManifestKey; +import com.gitee.starblues.common.PackageType; +import com.gitee.starblues.common.PluginDescriptorKey; +import com.gitee.starblues.utils.PropertiesUtils; +import com.gitee.starblues.utils.FilesUtils; +import com.gitee.starblues.utils.ObjectUtils; +import org.apache.commons.io.FileUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashSet; +import java.util.List; +import java.util.Properties; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import static com.gitee.starblues.common.PackageStructure.*; + + +/** + * 生产环境目录式插件 PluginDescriptorLoader 加载者 + * 解析生产的dir + * @author starBlues + * @version 3.0.0 + */ +public class ProdDirPluginDescriptorLoader extends AbstractPluginDescriptorLoader{ + + @Override + protected PluginMeta getPluginMetaInfo(Path location) throws Exception { + File file = new File(FilesUtils.joiningFilePath(location.toString(), resolvePath(PROD_MANIFEST_PATH))); + if(!file.exists()){ + return null; + } + Manifest manifest = new Manifest(); + String packageType = null; + String pluginMetaPath = null; + try (FileInputStream fileInputStream = new FileInputStream(file)){ + manifest.read(fileInputStream); + Attributes attributes = manifest.getMainAttributes(); + packageType = ManifestKey.getValue(attributes, ManifestKey.PLUGIN_PACKAGE_TYPE); + pluginMetaPath = ManifestKey.getValue(attributes, ManifestKey.PLUGIN_META_PATH); + } + if(packageType == null || pluginMetaPath == null){ + return null; + } + + File pluginMetaFile = new File(FilesUtils.joiningFilePath(location.toString(), pluginMetaPath)); + if(!pluginMetaFile.exists()){ + return null; + } + Properties properties = super.getProperties(new FileInputStream(pluginMetaFile)); + if(properties.isEmpty()){ + return null; + } + return new PluginMeta(packageType, properties); + } + + @Override + protected DefaultInsidePluginDescriptor create(PluginMeta pluginMeta, Path path) throws Exception { + DefaultInsidePluginDescriptor descriptor = super.create(pluginMeta, path); + String pathStr = path.toFile().getPath(); + descriptor.setPluginClassPath(FilesUtils.joiningFilePath( + pathStr, descriptor.getPluginClassPath() + )); + return descriptor; + } + + @Override + protected PluginResourcesConfig getPluginResourcesConfig(Path path, Properties properties) throws Exception { + String pathStr = path.toFile().getPath(); + String libIndexFile = getExistResourcesConfFile( + pathStr, PropertiesUtils.getValue(properties, PluginDescriptorKey.PLUGIN_RESOURCES_CONFIG) + ); + + if(libIndexFile == null){ + return new PluginResourcesConfig(); + } + File libFile = new File(libIndexFile); + List lines = FileUtils.readLines(libFile, CHARSET_NAME); + PluginResourcesConfig pluginResourcesConfig = PluginResourcesConfig.parse(lines); + + Set dependenciesIndex = pluginResourcesConfig.getDependenciesIndex(); + Set pluginLibPaths = new HashSet<>(); + for (String index : dependenciesIndex) { + index = resolvePath(index); + File file = new File(index); + if(!file.exists()){ + // 如果直接读取的路径不存在, 则从相对路径读取 + file = new File(FilesUtils.joiningFilePath( + pathStr, index + )); + } + if(file.exists()){ + pluginLibPaths.add(file.getPath()); + } + } + pluginResourcesConfig.setDependenciesIndex(pluginLibPaths); + return pluginResourcesConfig; + } + + protected String getExistResourcesConfFile(String rootPath, String libIndexPath){ + libIndexPath = resolvePath(libIndexPath); + if(ObjectUtils.isEmpty(libIndexPath)){ + // 如果配置为空, 直接从默认路径读取 + libIndexPath = FilesUtils.joiningFilePath(rootPath, resolvePath(PROD_RESOURCES_DEFINE_PATH)); + } else { + if(Files.exists(Paths.get(libIndexPath))){ + return libIndexPath; + } + libIndexPath = FilesUtils.joiningFilePath(rootPath, libIndexPath); + } + if(Files.exists(Paths.get(libIndexPath))){ + return libIndexPath; + } + return null; + } + + + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ProdPackagePluginDescriptorLoader.java b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ProdPackagePluginDescriptorLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..15896fcc53ecf285036d9df826ff166a06cf5a19 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ProdPackagePluginDescriptorLoader.java @@ -0,0 +1,94 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.descriptor; + + +import com.gitee.starblues.common.ManifestKey; +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.utils.PropertiesUtils; +import com.gitee.starblues.utils.ObjectUtils; +import org.apache.commons.io.IOUtils; + +import java.io.InputStream; +import java.nio.file.Path; +import java.util.List; +import java.util.Properties; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +import static com.gitee.starblues.common.PluginDescriptorKey.PLUGIN_RESOURCES_CONFIG; + + +/** + * 生产环境打包好的插件 PluginDescriptorLoader 加载者 + * 解析 jar、zip + * @author starBlues + * @version 3.0.0 + */ +public class ProdPackagePluginDescriptorLoader extends AbstractPluginDescriptorLoader{ + + private PluginResourcesConfig pluginResourcesConfig; + + public ProdPackagePluginDescriptorLoader() { + } + + @Override + protected PluginMeta getPluginMetaInfo(Path location) throws Exception { + try (JarFile jarFile = new JarFile(location.toFile())){ + Manifest manifest = jarFile.getManifest(); + Attributes attributes = manifest.getMainAttributes(); + String packageType = ManifestKey.getValue(attributes, ManifestKey.PLUGIN_PACKAGE_TYPE); + String pluginMetaPath = ManifestKey.getValue(attributes, ManifestKey.PLUGIN_META_PATH); + if(packageType == null || pluginMetaPath == null){ + return null; + } + JarEntry jarEntry = jarFile.getJarEntry(pluginMetaPath); + if(jarEntry == null){ + return null; + } + Properties properties = super.getProperties(jarFile.getInputStream(jarEntry)); + if(properties.isEmpty()){ + return null; + } + pluginResourcesConfig = getPluginResourcesConfig(jarFile, properties); + return new PluginMeta(packageType, properties); + } + } + + @Override + protected PluginResourcesConfig getPluginResourcesConfig(Path path, Properties properties) throws Exception { + return pluginResourcesConfig; + } + + protected PluginResourcesConfig getPluginResourcesConfig(JarFile jarFile, Properties properties) throws Exception { + String pluginResourcesConf = PropertiesUtils.getValue(properties, PLUGIN_RESOURCES_CONFIG); + if(ObjectUtils.isEmpty(pluginResourcesConf)){ + return new PluginResourcesConfig(); + } + JarEntry jarEntry = jarFile.getJarEntry(pluginResourcesConf); + if(jarEntry == null){ + return new PluginResourcesConfig(); + } + InputStream jarFileInputStream = jarFile.getInputStream(jarEntry); + List lines = IOUtils.readLines(jarFileInputStream, PackageStructure.CHARSET_NAME); + return PluginResourcesConfig.parse(lines); + } + + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ProdPluginDescriptorLoader.java b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ProdPluginDescriptorLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..41a0583e5f18a223402c212d794450e24f2529d7 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ProdPluginDescriptorLoader.java @@ -0,0 +1,56 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.descriptor; + +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.utils.ResourceUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.file.Path; + +/** + * 生产环境插件描述加载者 + * @author starBlues + * @version 3.0.0 + */ +public class ProdPluginDescriptorLoader implements PluginDescriptorLoader{ + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private PluginDescriptorLoader target; + + @Override + public InsidePluginDescriptor load(Path location) throws PluginException { + if(ResourceUtils.isJarFile(location)){ + target = new ProdPackagePluginDescriptorLoader(); + } else if(ResourceUtils.isZipFile(location)){ + target = new ProdPackagePluginDescriptorLoader(); + } else if(ResourceUtils.isDirFile(location)){ + target = new ProdDirPluginDescriptorLoader(); + } else { + logger.warn("不能解析文件: {}", location); + return null; + } + return target.load(location); + } + + @Override + public void close() throws Exception { + target.close(); + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginDisabledException.java b/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginDisabledException.java new file mode 100644 index 0000000000000000000000000000000000000000..25a6bb52e5bb84984f9f1e2dca14d63162e75893 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginDisabledException.java @@ -0,0 +1,56 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.exception; + +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.core.PluginState; +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.utils.MsgUtils; +import com.gitee.starblues.utils.ObjectUtils; + +/** + * 插件被禁用异常 + * @author starBlues + * @version 3.0.0 + */ +public class PluginDisabledException extends PluginException { + + public PluginDisabledException(PluginDescriptor pluginDescriptor) { + this(pluginDescriptor, null); + } + + public PluginDisabledException(PluginDescriptor pluginDescriptor, String opType) { + super("插件[" + MsgUtils.getPluginUnique(pluginDescriptor) + "]已被禁用, 不能" + + (!ObjectUtils.isEmpty(opType) ? opType : "操作")); + } + + + /** + * 检查插件是否被禁用 + * @param pluginInsideInfo 插件信息 + * @param configuration 集成配置 + * @param opType 操作类型 + */ + public static void checkDisabled(PluginInfo pluginInsideInfo, IntegrationConfiguration configuration, String opType){ + if(pluginInsideInfo.getPluginState() == PluginState.DISABLED + || configuration.isDisabled(pluginInsideInfo.getPluginId()) + || !configuration.isEnable(pluginInsideInfo.getPluginId())){ + throw new PluginDisabledException(pluginInsideInfo.getPluginDescriptor(), opType); + } + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginException.java b/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginException.java new file mode 100644 index 0000000000000000000000000000000000000000..92b79a1d48f8093a892d984eaf850c3ab5b1daf5 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginException.java @@ -0,0 +1,70 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.exception; + +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.utils.MsgUtils; + +import java.util.function.Supplier; + + +/** + * 插件异常 + * @author starBlues + * @version 3.0.0 + */ +public class PluginException extends RuntimeException{ + + public PluginException() { + super(); + } + + public PluginException(String message) { + super(message); + } + + public PluginException(Throwable cause) { + super(cause); + } + + public PluginException(String message, Throwable cause) { + super(message, cause); + } + + public PluginException(PluginDescriptor pluginDescriptor, String opType, Throwable cause) { + this(MsgUtils.getPluginUnique(pluginDescriptor), opType, cause); + } + + public PluginException(PluginDescriptor pluginDescriptor, String message) { + this(MsgUtils.getPluginUnique(pluginDescriptor), message); + } + + public PluginException(String pluginId, String opType, Throwable cause) { + super("插件[" + pluginId + "]" + opType + "失败. " + MsgUtils.getThrowableMsg(cause), cause); + } + + public PluginException(String pluginId, String message) { + super("插件[" + pluginId + "]" + MsgUtils.getThrowableMsg(message)); + } + + public static PluginException getPluginException(Throwable throwable, Supplier getException){ + if(throwable instanceof PluginException){ + return (PluginException) throwable; + } + return getException.get(); + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginProhibitStopException.java b/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginProhibitStopException.java new file mode 100644 index 0000000000000000000000000000000000000000..a5095d52335a58374840b7a0c313d142cc3fbf5d --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginProhibitStopException.java @@ -0,0 +1,32 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.exception; + +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.utils.MsgUtils; + +/** + * 插件禁止停止异常 + * @author starBlues + * @version 3.0.0 + */ +public class PluginProhibitStopException extends PluginException { + + public PluginProhibitStopException(PluginDescriptor pluginDescriptor, String message) { + super(pluginDescriptor, "被禁止卸载. " + MsgUtils.getThrowableMsg(message)); + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/launcher/JavaMainResourcePatternDefiner.java b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/JavaMainResourcePatternDefiner.java new file mode 100644 index 0000000000000000000000000000000000000000..4e46c7efd223d356fe402803fb0258f2afcb2d3d --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/JavaMainResourcePatternDefiner.java @@ -0,0 +1,83 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.launcher; + +import com.gitee.starblues.core.classloader.MainResourcePatternDefiner; + +import java.util.HashSet; +import java.util.Set; + +/** + * java 内部包匹配定义 + * @author starBlues + * @version 3.0.0 + */ +public class JavaMainResourcePatternDefiner implements MainResourcePatternDefiner { + + protected final Set includes = new HashSet<>(); + + public JavaMainResourcePatternDefiner(){ + // java 内部的包匹配定义 + addJava(); + addJavax(); + addSun(); + addJdk(); + addJavaXml(); + } + + protected void addJava(){ + includes.add("java/**"); + } + + protected void addJavax(){ + includes.add("javax/**"); + includes.add("org/ietf/jgss/**"); + } + + protected void addJdk(){ + includes.add("jdk/**"); + // jdk.internal.vm.compiler + includes.add("org/graalvm/**"); + // jdk.internal.vm.compiler.management + includes.add("org/graalvm/compiler/hotspot/management/**"); + // jdk.hotspot.agent + includes.add("images/toolbarButtonGraphics/general/**"); + includes.add("toolbarButtonGraphics/**"); + } + + protected void addJavaXml(){ + // jdk.xml.dom + includes.add("org/w3c/dom/**"); + includes.add("org/xml/sax/**"); + includes.add("org/jcp/xml/dsig/internal**"); + } + + protected void addSun(){ + includes.add("com/sun/**"); + includes.add("sun/**"); + } + + @Override + public Set getIncludePatterns() { + return includes; + } + + @Override + public Set getExcludePatterns() { + return null; + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/BasicMainResourcePatternDefiner.java b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/BasicMainResourcePatternDefiner.java new file mode 100644 index 0000000000000000000000000000000000000000..461a5ca27482da906c19ab52eab3a29d0c6a3277 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/BasicMainResourcePatternDefiner.java @@ -0,0 +1,50 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.launcher.plugin; + +import com.gitee.starblues.core.classloader.MainResourcePatternDefiner; +import com.gitee.starblues.utils.Assert; +import com.gitee.starblues.utils.ObjectUtils; + +import java.util.HashSet; +import java.util.Set; + +/** + * 基本的主程序资源匹配定义 + * @author starBlues + * @version 3.0.0 + */ +public class BasicMainResourcePatternDefiner implements MainResourcePatternDefiner { + + private final String mainPackageName; + + public BasicMainResourcePatternDefiner(String mainPackageName) { + this.mainPackageName = ObjectUtils.changePackageToMatch(mainPackageName); + } + + @Override + public Set getIncludePatterns() { + Set includePatterns = new HashSet<>(); + includePatterns.add(mainPackageName); + return includePatterns; + } + + @Override + public Set getExcludePatterns() { + return null; + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/CacheRegistryInfo.java b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/CacheRegistryInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..341680b9352a9cb7aa9077986abf676acbb4a5c0 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/CacheRegistryInfo.java @@ -0,0 +1,67 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.launcher.plugin; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +/** + * 可缓存的注册信息 + * @author starBlues + * @version 3.0.0 + */ +public class CacheRegistryInfo implements RegistryInfo { + + private final Map registryInfo = new ConcurrentHashMap<>(); + + @Override + public void addRegistryInfo(String key, Object value) { + registryInfo.put(key, value); + } + + @SuppressWarnings("unchecked") + @Override + public T getRegistryInfo(String key) { + Object o = registryInfo.get(key); + if(o == null){ + return null; + } + return (T) o; + } + + @Override + public T getRegistryInfo(String key, Supplier notExistCreate) { + T t = getRegistryInfo(key); + if(t != null){ + return t; + } + t = notExistCreate.get(); + registryInfo.put(key, t); + return t; + } + + @Override + public void removeRegistryInfo(String key) { + registryInfo.remove(key); + } + + @Override + public void clearRegistryInfo() { + registryInfo.clear(); + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/DefaultPluginInteractive.java b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/DefaultPluginInteractive.java new file mode 100644 index 0000000000000000000000000000000000000000..c26810d10eef2fccf120bb40d96a55e59bd365b0 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/DefaultPluginInteractive.java @@ -0,0 +1,81 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.launcher.plugin; + +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.spring.extract.DefaultExtractFactory; +import com.gitee.starblues.spring.extract.ExtractFactory; +import com.gitee.starblues.spring.extract.OpExtractFactory; +import com.gitee.starblues.spring.invoke.InvokeSupperCache; + +/** + * 默认的插件交互实现 + * @author starBlues + * @version 3.0.0 + */ +public class DefaultPluginInteractive implements PluginInteractive{ + + private final InsidePluginDescriptor pluginDescriptor; + private final MainApplicationContext mainApplicationContext; + private final IntegrationConfiguration configuration; + private final InvokeSupperCache invokeSupperCache; + private final OpExtractFactory opExtractFactory; + + public DefaultPluginInteractive(InsidePluginDescriptor pluginDescriptor, + MainApplicationContext mainApplicationContext, + IntegrationConfiguration configuration, + InvokeSupperCache invokeSupperCache) { + this.pluginDescriptor = pluginDescriptor; + this.mainApplicationContext = mainApplicationContext; + this.configuration = configuration; + this.invokeSupperCache = invokeSupperCache; + this.opExtractFactory = createOpExtractFactory(); + } + + protected OpExtractFactory createOpExtractFactory(){ + DefaultExtractFactory defaultExtractFactory = (DefaultExtractFactory)ExtractFactory.getInstant(); + return (OpExtractFactory) defaultExtractFactory.getTarget(); + } + + + @Override + public InsidePluginDescriptor getPluginDescriptor() { + return pluginDescriptor; + } + + @Override + public MainApplicationContext getMainApplicationContext() { + return mainApplicationContext; + } + + @Override + public IntegrationConfiguration getConfiguration() { + return configuration; + } + + @Override + public InvokeSupperCache getInvokeSupperCache() { + return invokeSupperCache; + } + + @Override + public OpExtractFactory getOpExtractFactory() { + return opExtractFactory; + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginInteractive.java b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginInteractive.java new file mode 100644 index 0000000000000000000000000000000000000000..e9e87e1c86f82f87826d15728b317d059bbab5eb --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginInteractive.java @@ -0,0 +1,62 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.launcher.plugin; + +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.spring.extract.OpExtractFactory; +import com.gitee.starblues.spring.invoke.InvokeSupperCache; + +/** + * 插件交互接口 + * @author starBlues + * @version 3.0.0 + */ +public interface PluginInteractive { + + /** + * 获取插件信息 + * @return PluginDescriptor + */ + InsidePluginDescriptor getPluginDescriptor(); + + /** + * 获取主程序的 MainApplicationContext + * @return MainApplicationContext + */ + MainApplicationContext getMainApplicationContext(); + + /** + * 获取主程序对框架集成配置信息 + * @return IntegrationConfiguration + */ + IntegrationConfiguration getConfiguration(); + + /** + * 获取远程调用缓存 + * @return InvokeSupperCache + */ + InvokeSupperCache getInvokeSupperCache(); + + /** + * 获取业务扩展功能的工厂 + * @return OpExtractFactory + */ + OpExtractFactory getOpExtractFactory(); + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginLauncher.java b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginLauncher.java new file mode 100644 index 0000000000000000000000000000000000000000..4c5ba7882c61a19393266e00b1a4770cf7db5724 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginLauncher.java @@ -0,0 +1,104 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.launcher.plugin; + +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.core.classloader.PluginClassLoader; +import com.gitee.starblues.core.launcher.plugin.involved.PluginLaunchInvolved; +import com.gitee.starblues.loader.classloader.GenericClassLoader; +import com.gitee.starblues.loader.classloader.resource.loader.DefaultResourceLoaderFactory; +import com.gitee.starblues.loader.classloader.resource.loader.ResourceLoaderFactory; +import com.gitee.starblues.loader.launcher.AbstractLauncher; +import com.gitee.starblues.loader.launcher.ResourceLoaderFactoryGetter; +import com.gitee.starblues.spring.SpringPluginHook; + +import java.util.Map; +import java.util.WeakHashMap; + +/** + * 插件启动引导类 + * @author starBlues + * @version 3.0.0 + */ +public class PluginLauncher extends AbstractLauncher { + + private static final Map CLASS_LOADER_CACHE = new WeakHashMap<>(); + + protected final PluginInteractive pluginInteractive; + protected final InsidePluginDescriptor pluginDescriptor; + protected final PluginMainResourcePatternDefiner mainResourcePatternDefiner; + + protected final PluginLaunchInvolved pluginLaunchInvolved; + + public PluginLauncher(PluginInteractive pluginInteractive, + PluginLaunchInvolved pluginLaunchInvolved) { + this.pluginInteractive = pluginInteractive; + this.pluginDescriptor = pluginInteractive.getPluginDescriptor(); + this.mainResourcePatternDefiner = new PluginMainResourcePatternDefiner(pluginInteractive); + this.pluginLaunchInvolved = pluginLaunchInvolved; + } + + @Override + protected ClassLoader createClassLoader(String... args) throws Exception { + PluginClassLoader pluginClassLoader = getPluginClassLoader(); + pluginClassLoader.addResource(pluginDescriptor); + return pluginClassLoader; + } + + protected synchronized PluginClassLoader getPluginClassLoader() throws Exception { + String pluginId = pluginDescriptor.getPluginId(); + PluginClassLoader classLoader = CLASS_LOADER_CACHE.get(pluginId); + if(classLoader != null){ + return classLoader; + } + PluginClassLoader pluginClassLoader = new PluginClassLoader( + pluginId, getParentClassLoader(), mainResourcePatternDefiner, + getResourceLoaderFactory() + ); + CLASS_LOADER_CACHE.put(pluginId, pluginClassLoader); + return pluginClassLoader; + } + + protected ResourceLoaderFactory getResourceLoaderFactory(){ + return new DefaultResourceLoaderFactory(pluginDescriptor.getPluginId()); + } + + protected GenericClassLoader getParentClassLoader() throws Exception { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + if(contextClassLoader instanceof GenericClassLoader){ + return (GenericClassLoader) contextClassLoader; + } else { + throw new Exception("非法父类加载器: " + contextClassLoader.getClass().getName()); + } + } + + @Override + protected SpringPluginHook launch(ClassLoader classLoader, String... args) throws Exception { + pluginLaunchInvolved.before(pluginDescriptor, classLoader); + try { + SpringPluginHook springPluginHook = (SpringPluginHook) new PluginMethodRunner(pluginInteractive) + .run(classLoader); + pluginLaunchInvolved.after(pluginDescriptor, classLoader, springPluginHook); + return new SpringPluginHookWrapper(springPluginHook, pluginDescriptor, pluginLaunchInvolved, classLoader); + } catch (Throwable throwable){ + pluginLaunchInvolved.failure(pluginDescriptor,classLoader, throwable); + throw throwable; + } + } + + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginMainResourcePatternDefiner.java b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginMainResourcePatternDefiner.java new file mode 100644 index 0000000000000000000000000000000000000000..8772b542c3e8cd3d90184f6b84d07c1d4508e9f3 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginMainResourcePatternDefiner.java @@ -0,0 +1,157 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.launcher.plugin; + +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.core.launcher.JavaMainResourcePatternDefiner; +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.utils.Assert; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.SpringBeanCustomUtils; + +import java.util.HashSet; +import java.util.Set; + +/** + * 定义插件从主程序加载资源的匹配 + * @author starBlues + * @version 3.0.0 + */ +public class PluginMainResourcePatternDefiner extends JavaMainResourcePatternDefiner { + + private static final String FRAMEWORK = "com/gitee/starblues/**"; + + public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; + + private final String mainPackage; + private final InsidePluginDescriptor descriptor; + private final BasicMainResourcePatternDefiner basicPatternDefiner; + + public PluginMainResourcePatternDefiner(PluginInteractive pluginInteractive) { + mainPackage = pluginInteractive.getConfiguration().mainPackage(); + this.descriptor = pluginInteractive.getPluginDescriptor(); + basicPatternDefiner = getPatternDefiner(pluginInteractive); + } + + @Override + public Set getIncludePatterns() { + Set includeResourcePatterns = super.getIncludePatterns(); + Set includePatterns = basicPatternDefiner.getIncludePatterns(); + if(!ObjectUtils.isEmpty(includePatterns)){ + includeResourcePatterns.addAll(includePatterns); + } else { + includeResourcePatterns.add(ObjectUtils.changePackageToMatch(mainPackage)); + } + includeResourcePatterns.add(FRAMEWORK); + addWebIncludeResourcePatterns(includeResourcePatterns); + addApiDoc(includeResourcePatterns); + addDbDriver(includeResourcePatterns); + + // 配置插件自定义从主程序加载的资源匹配 + Set includeMainResourcePatterns = descriptor.getIncludeMainResourcePatterns(); + if(ObjectUtils.isEmpty(includeMainResourcePatterns)){ + return includeResourcePatterns; + } + + for (String includeMainResourcePattern : includeMainResourcePatterns) { + if(ObjectUtils.isEmpty(includeMainResourcePattern)){ + continue; + } + includeResourcePatterns.add(includeMainResourcePattern); + } + return includeResourcePatterns; + } + + + + @Override + public Set getExcludePatterns() { + Set excludeResourcePatterns = new HashSet<>(); + Set excludePatterns = basicPatternDefiner.getExcludePatterns(); + if(!ObjectUtils.isEmpty(excludePatterns)){ + excludeResourcePatterns.addAll(excludePatterns); + } + Set excludeMainResourcePatterns = descriptor.getExcludeMainResourcePatterns(); + if(!ObjectUtils.isEmpty(excludeMainResourcePatterns)){ + excludeResourcePatterns.addAll(excludeMainResourcePatterns); + } + excludeResourcePatterns.add(FACTORIES_RESOURCE_LOCATION); + return excludeResourcePatterns; + } + + protected void addWebIncludeResourcePatterns(Set patterns){ + patterns.add("org/springframework/web/**"); + patterns.add("org/springframework/http/**"); + patterns.add("org/springframework/remoting/**"); + patterns.add("org/springframework/ui/**"); + + patterns.add("com/fasterxml/jackson/**"); + + } + + protected void addApiDoc(Set patterns){ + patterns.add("springfox/documentation/**"); + patterns.add("io/swagger/**"); + patterns.add("org/springdoc/**"); + } + + protected void addDbDriver(Set patterns){ + // mysql + patterns.add("com/mysql/**"); + // oracle + patterns.add("oracle/jdbc/**"); + // sqlserver + patterns.add("com/microsoft/jdbc/sqlserver/**"); + // DB2 + patterns.add("com/ibm/db2/jdbc/**"); + // DB2/AS400 + patterns.add("com/ibm/as400/**"); + // Informix + patterns.add("com/informix/jdbc/**"); + // Hypersonic + patterns.add("org/hsql/**"); + // MS SQL + patterns.add("com/microsoft/jdbc/**"); + // Postgres + patterns.add("org/postgresql/**"); + // Sybase + patterns.add("com/sybase/jdbc2/**"); + // Weblogic + patterns.add("weblogic/jdbc/**"); + // h2 + patterns.add("jdbc/h2/**"); + } + + /** + * 获取基本的 MainResourcePatternDefiner + * @param pluginInteractive PluginInteractive + * @return BasicMainResourcePatternDefiner + */ + private BasicMainResourcePatternDefiner getPatternDefiner(PluginInteractive pluginInteractive){ + final MainApplicationContext mainApplicationContext = pluginInteractive.getMainApplicationContext(); + BasicMainResourcePatternDefiner definer = SpringBeanCustomUtils.getExistBean( + mainApplicationContext, BasicMainResourcePatternDefiner.class); + if(definer == null){ + return new BasicMainResourcePatternDefiner(mainPackage); + } else { + return definer; + } + } + + + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginMethodRunner.java b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginMethodRunner.java new file mode 100644 index 0000000000000000000000000000000000000000..c5d12d4add3ba48709865563d5d1712f5b81e9eb --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginMethodRunner.java @@ -0,0 +1,102 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.launcher.plugin; + +import com.gitee.starblues.core.RuntimeMode; +import com.gitee.starblues.loader.launcher.runner.MethodRunner; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.ReflectionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Method; + +/** + * 插件方法运行器。 + * @author starBlues + * @version 3.0.0 + */ +public class PluginMethodRunner extends MethodRunner { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private static final String PLUGIN_RUN_METHOD_NAME = "run"; + + private final PluginInteractive pluginInteractive; + + public PluginMethodRunner(PluginInteractive pluginInteractive) { + super(pluginInteractive.getPluginDescriptor().getPluginBootstrapClass(), PLUGIN_RUN_METHOD_NAME, new String[]{}); + this.pluginInteractive = pluginInteractive; + String args = pluginInteractive.getPluginDescriptor().getArgs(); + if(!ObjectUtils.isEmpty(args)){ + super.args = args.split(" "); + } + } + + @Override + protected Class loadRunClass(ClassLoader classLoader) throws Exception { + try { + return super.loadRunClass(classLoader); + } catch (Exception e){ + if(e instanceof ClassNotFoundException){ + String pluginId = pluginInteractive.getPluginDescriptor().getPluginId(); + String error = "插件[" + pluginId + "]没有发现" + "[" + className + "]引导类"; + if(pluginInteractive.getConfiguration().environment() == RuntimeMode.DEV){ + error = error + ", 请确保已经编译!"; + } + throw new ClassNotFoundException(error); + } + throw e; + } + } + + @Override + protected Object runMethod(Class runClass) throws Exception { + Method runMethod = ReflectionUtils.findMethod(runClass, runMethodName, Class.class, String[].class); + if(runMethod == null) { + throw new NoSuchMethodException(runClass.getName() + "." + runMethodName + + "(Class arg0, String[] arg1)"); + } + Object instance = getInstance(runClass); + setPluginInteractive(instance); + runMethod.setAccessible(true); + try { + return runMethod.invoke(instance, runClass, this.args); + } catch (Exception e){ + String error = "Invoke failure: " + + ReflectionUtils.methodToString(runClass, runMethodName, runMethod.getParameterTypes()) + + ". "; + String message = e.getMessage(); + if(message != null){ + error = error + message; + logger.error(error, e); + } else { + logger.error(error); + } + throw new Exception(error); + } + } + + private void setPluginInteractive(Object launchObject) throws Exception { + if(launchObject == null){ + return; + } + ReflectionUtils.setAttribute(launchObject, "setPluginInteractive", pluginInteractive); + } + + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/RegistryInfo.java b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/RegistryInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..b604517822509eaa6335d5c05bea3ed97efb0c1d --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/RegistryInfo.java @@ -0,0 +1,64 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.launcher.plugin; +import java.util.function.Supplier; + +/** + * 注册的信息接口 + * @author starBlues + * @version 3.0.0 + */ +public interface RegistryInfo { + + + /** + * 添加注册的信息 + * @param key 注册信息key + * @param value 注册信息值 + */ + void addRegistryInfo(String key, Object value); + + /** + * 得到注册信息 + * @param key 注册信息key + * @param 返回类型泛型 + * @return 注册信息的值 + */ + T getRegistryInfo(String key); + + /** + * 得到注册信息 + * @param key 注册信息key + * @param notExistCreate 不存在的话, 进行创建操作 + * @param 返回类型泛型 + * @return 注册信息的值 + */ + T getRegistryInfo(String key, Supplier notExistCreate); + + + /** + * 移除注册信息 + * @param key 注册信息key + */ + void removeRegistryInfo(String key); + + /** + * 清除全部的注册信息 + */ + void clearRegistryInfo(); + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/SpringPluginHookWrapper.java b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/SpringPluginHookWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..e25ac5fa8f31549cea02a6c26fa25175d2b0827c --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/SpringPluginHookWrapper.java @@ -0,0 +1,75 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.launcher.plugin; + +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.core.exception.PluginProhibitStopException; +import com.gitee.starblues.core.launcher.plugin.involved.PluginLaunchInvolved; +import com.gitee.starblues.spring.ApplicationContext; +import com.gitee.starblues.spring.SpringPluginHook; +import com.gitee.starblues.spring.WebConfig; +import com.gitee.starblues.spring.web.thymeleaf.ThymeleafConfig; +import com.gitee.starblues.utils.ResourceUtils; + +/** + * SpringPluginHook-Wrapper + * @author starBlues + * @version 3.0.0 + */ +public class SpringPluginHookWrapper implements SpringPluginHook { + + private final SpringPluginHook target; + private final InsidePluginDescriptor descriptor; + private final PluginLaunchInvolved pluginLaunchInvolved; + private final ClassLoader classLoader; + + public SpringPluginHookWrapper(SpringPluginHook target, InsidePluginDescriptor descriptor, + PluginLaunchInvolved pluginLaunchInvolved, + ClassLoader classLoader) { + this.target = target; + this.descriptor = descriptor; + this.pluginLaunchInvolved = pluginLaunchInvolved; + this.classLoader = classLoader; + } + + @Override + public void stopVerify() throws PluginProhibitStopException { + target.stopVerify(); + } + + @Override + public ApplicationContext getApplicationContext() { + return target.getApplicationContext(); + } + + @Override + public WebConfig getWebConfig() { + return target.getWebConfig(); + } + + @Override + public ThymeleafConfig getThymeleafConfig() { + return null; + } + + @Override + public void close() throws Exception { + pluginLaunchInvolved.close(descriptor, classLoader); + ResourceUtils.closeQuietly(target); + ResourceUtils.closeQuietly(classLoader); + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/DefaultPluginLaunchInvolved.java b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/DefaultPluginLaunchInvolved.java new file mode 100644 index 0000000000000000000000000000000000000000..33fee5b7d1789116f691e7fb02ff849969cac8d1 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/DefaultPluginLaunchInvolved.java @@ -0,0 +1,47 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.launcher.plugin.involved; + +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.loader.PluginResourceStorage; +import com.gitee.starblues.spring.SpringPluginHook; +import com.gitee.starblues.spring.web.PluginStaticResourceResolver; + +/** + * 默认的插件启动介入者 + * @author starBlues + * @version 3.0.0 + */ +public class DefaultPluginLaunchInvolved implements PluginLaunchInvolved{ + + @Override + public void before(InsidePluginDescriptor descriptor, ClassLoader classLoader) throws Exception { + PluginResourceStorage.addPlugin(descriptor.getPluginId(), descriptor.getPluginFileName()); + } + + @Override + public void after(InsidePluginDescriptor descriptor, ClassLoader classLoader, SpringPluginHook pluginHook) throws Exception { + PluginStaticResourceResolver.parse(descriptor, classLoader, pluginHook.getWebConfig()); + } + + @Override + public void close(InsidePluginDescriptor descriptor, ClassLoader classLoader) throws Exception { + String pluginId = descriptor.getPluginId(); + PluginResourceStorage.removePlugin(pluginId); + PluginStaticResourceResolver.remove(pluginId); + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/PluginApplicationContextGetter.java b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/PluginApplicationContextGetter.java new file mode 100644 index 0000000000000000000000000000000000000000..8a8754ebf554d05543217d2a1d576b82e01ce992 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/PluginApplicationContextGetter.java @@ -0,0 +1,52 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.launcher.plugin.involved; + +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.spring.ApplicationContext; +import com.gitee.starblues.spring.SpringPluginHook; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author starBlues + * @version 3.0.0 + */ +public class PluginApplicationContextGetter implements PluginLaunchInvolved{ + + private static final Map PLUGIN_CONTEXTS = new ConcurrentHashMap<>(); + + @Override + public void after(InsidePluginDescriptor descriptor, ClassLoader classLoader, SpringPluginHook pluginHook) throws Exception { + PLUGIN_CONTEXTS.put(descriptor.getPluginId(), pluginHook.getApplicationContext()); + } + + @Override + public void close(InsidePluginDescriptor descriptor, ClassLoader classLoader) throws Exception { + PLUGIN_CONTEXTS.remove(descriptor.getPluginId()); + } + + public static ApplicationContext get(String pluginId){ + return PLUGIN_CONTEXTS.get(pluginId); + } + + public static Map get(){ + return Collections.unmodifiableMap(PLUGIN_CONTEXTS); + } + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/PluginLaunchInvolved.java b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/PluginLaunchInvolved.java new file mode 100644 index 0000000000000000000000000000000000000000..93fc5a1752e807b8e7111d808d841c95a1cb0dd0 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/PluginLaunchInvolved.java @@ -0,0 +1,84 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.launcher.plugin.involved; + +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.spring.SpringPluginHook; +import com.gitee.starblues.utils.OrderPriority; +import org.springframework.context.support.GenericApplicationContext; + +/** + * 插件启动前后介入 + * @author starBlues + * @version 3.0.0 + */ +public interface PluginLaunchInvolved { + + /** + * 初始化。仅调用一次 + * @param applicationContext 主程序GenericApplicationContext + * @param configuration 集成配置 + */ + default void initialize(GenericApplicationContext applicationContext, IntegrationConfiguration configuration){} + + /** + * 启动之前 + * @param descriptor 插件信息 + * @param classLoader 插件classloader + * @throws Exception 执行异常 + */ + default void before(InsidePluginDescriptor descriptor, ClassLoader classLoader) throws Exception{} + + /** + * 启动之后 + * @param descriptor 插件信息 + * @param classLoader 插件classloader + * @param pluginHook 启动成功后插件返回的钩子 + * @throws Exception 执行异常 + */ + default void after(InsidePluginDescriptor descriptor, ClassLoader classLoader, + SpringPluginHook pluginHook) throws Exception{} + + /** + * 启动失败 + * @param descriptor 插件信息 + * @param classLoader 插件classloader + * @param throwable 异常信息 + * @throws Exception 执行异常 + */ + default void failure(InsidePluginDescriptor descriptor, ClassLoader classLoader, Throwable throwable) throws Exception{} + + /** + * 关闭的时候 + * @param descriptor 插件信息 + * @param classLoader 插件classloader + * @throws Exception 执行异常 + */ + default void close(InsidePluginDescriptor descriptor, ClassLoader classLoader) throws Exception{} + + /** + * 执行顺序 + * @return OrderPriority + */ + default OrderPriority order(){ + return OrderPriority.getMiddlePriority(); + } + + + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/PluginLaunchInvolvedFactory.java b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/PluginLaunchInvolvedFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..8330ce2c49130a40b942ed45e978c4fc0901e613 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/PluginLaunchInvolvedFactory.java @@ -0,0 +1,114 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.launcher.plugin.involved; + +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.spring.SpringPluginHook; +import com.gitee.starblues.utils.OrderUtils; +import com.gitee.starblues.utils.ObjectUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.support.GenericApplicationContext; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * 插件启动介入工厂 + * @author starBlues + * @version 3.0.0 + */ +public class PluginLaunchInvolvedFactory implements PluginLaunchInvolved{ + + private static final Logger logger = LoggerFactory.getLogger(PluginLaunchInvolvedFactory.class); + + private List pluginLaunchInvolvedList; + + + @Override + public void initialize(GenericApplicationContext applicationContext, IntegrationConfiguration configuration) { + this.pluginLaunchInvolvedList = getPluginLaunchInvolvedList(applicationContext); + for (PluginLaunchInvolved pluginLaunchInvolved : pluginLaunchInvolvedList) { + try { + pluginLaunchInvolved.initialize(applicationContext, configuration); + } catch (Exception e){ + logger.error("[{}] execute initialize exception : {}", + pluginLaunchInvolved.getClass().getName(), e.getMessage(), e); + } + } + } + + protected List getPluginLaunchInvolvedList(GenericApplicationContext applicationContext){ + List pluginLaunchInvolvedList = getDefaultPluginLaunchInvolved(); + if(pluginLaunchInvolvedList == null){ + pluginLaunchInvolvedList = new ArrayList<>(); + } + Map pluginLaunchInvolvedMap = applicationContext.getBeansOfType(PluginLaunchInvolved.class); + if(!ObjectUtils.isEmpty(pluginLaunchInvolvedMap)){ + pluginLaunchInvolvedList.addAll(pluginLaunchInvolvedMap.values()); + } + pluginLaunchInvolvedList.sort(OrderUtils.orderPriority(PluginLaunchInvolved::order)); + return pluginLaunchInvolvedList; + } + + protected List getDefaultPluginLaunchInvolved(){ + List defaultPluginLaunchInvolved = new ArrayList<>(); + defaultPluginLaunchInvolved.add(new DefaultPluginLaunchInvolved()); + defaultPluginLaunchInvolved.add(new PluginApplicationContextGetter()); + return defaultPluginLaunchInvolved; + } + + @Override + public void before(InsidePluginDescriptor descriptor, ClassLoader classLoader) throws Exception { + for (PluginLaunchInvolved pluginLaunchInvolved : pluginLaunchInvolvedList) { + pluginLaunchInvolved.before(descriptor, classLoader); + } + } + + @Override + public void after(InsidePluginDescriptor descriptor, ClassLoader classLoader, SpringPluginHook pluginHook) throws Exception { + for (PluginLaunchInvolved pluginLaunchInvolved : pluginLaunchInvolvedList) { + pluginLaunchInvolved.after(descriptor, classLoader, pluginHook); + } + } + + @Override + public void failure(InsidePluginDescriptor descriptor, ClassLoader classLoader, Throwable throwable) throws Exception { + for (PluginLaunchInvolved pluginLaunchInvolved : pluginLaunchInvolvedList) { + try { + pluginLaunchInvolved.failure(descriptor, classLoader, throwable); + } catch (Exception e){ + logger.error("[{}] execute failure exception : {}", + pluginLaunchInvolved.getClass().getName(), e.getMessage(), e); + } + } + } + + @Override + public void close(InsidePluginDescriptor descriptor, ClassLoader classLoader) throws Exception { + for (PluginLaunchInvolved pluginLaunchInvolved : pluginLaunchInvolvedList) { + try { + pluginLaunchInvolved.close(descriptor, classLoader); + } catch (Exception e){ + logger.error("[{}] execute close exception : {}", + pluginLaunchInvolved.getClass().getName(), e.getMessage(), e); + } + } + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/scanner/BasePluginScanner.java b/spring-brick/src/main/java/com/gitee/starblues/core/scanner/BasePluginScanner.java new file mode 100644 index 0000000000000000000000000000000000000000..a17c7563e106c0c918a20bee8c86845c5c67721b --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/scanner/BasePluginScanner.java @@ -0,0 +1,80 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.scanner; + +import com.gitee.starblues.utils.ObjectUtils; + +import java.io.File; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * 基本的插件扫描者 + * @author starBlues + * @version 3.0.0 + */ +public class BasePluginScanner implements PluginScanner{ + + private PathResolve pathResolve; + + public void setPathResolve(PathResolve pathResolve) { + this.pathResolve = pathResolve; + } + + @Override + public List scan(List rootDir) { + if(ObjectUtils.isEmpty(rootDir)){ + return Collections.emptyList(); + } + List pluginPaths = new ArrayList<>(); + if(pathResolve == null){ + return pluginPaths; + } + for (String dir : rootDir) { + if(ObjectUtils.isEmpty(dir)){ + continue; + } + File file = new File(dir); + if(!file.exists()){ + continue; + } + resolve(file, pluginPaths); + } + return pluginPaths; + } + + protected void resolve(File currentFile, List pluginPaths){ + if(currentFile == null || !currentFile.exists()){ + return; + } + Path currentPath = currentFile.toPath(); + currentPath = pathResolve.resolve(currentPath); + if(currentPath != null){ + pluginPaths.add(currentPath); + } else { + File[] files = currentFile.listFiles(); + if(files == null || files.length == 0){ + return; + } + for (File file : files) { + resolve(file, pluginPaths); + } + } + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/scanner/ComposePathResolve.java b/spring-brick/src/main/java/com/gitee/starblues/core/scanner/ComposePathResolve.java new file mode 100644 index 0000000000000000000000000000000000000000..56cee67bc727ed49ea371d56d61f237efa5df9a0 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/scanner/ComposePathResolve.java @@ -0,0 +1,63 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.scanner; + +import com.gitee.starblues.utils.ObjectUtils; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 组合的PathResolve + * @author starBlues + * @version 3.0.0 + */ +public class ComposePathResolve implements PathResolve{ + + private final List pathResolves; + + public ComposePathResolve(PathResolve ...pathResolves) { + this(Arrays.asList(pathResolves)); + } + + public ComposePathResolve(List pathResolves) { + if(ObjectUtils.isEmpty(pathResolves)){ + this.pathResolves = new ArrayList<>(); + } else { + this.pathResolves = pathResolves; + } + } + + public void addPathResolve(PathResolve pathResolve){ + if(pathResolve != null){ + pathResolves.add(pathResolve); + } + } + + @Override + public Path resolve(Path path) { + for (PathResolve pathResolve : pathResolves) { + Path resolvePath = pathResolve.resolve(path); + if(resolvePath != null){ + return resolvePath; + } + } + return null; + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/scanner/DevPathResolve.java b/spring-brick/src/main/java/com/gitee/starblues/core/scanner/DevPathResolve.java new file mode 100644 index 0000000000000000000000000000000000000000..6c1e9444b0f679846ee2c8312212b6dcdac5a0df --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/scanner/DevPathResolve.java @@ -0,0 +1,57 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.scanner; + +import com.gitee.starblues.common.PackageStructure; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +/** + * 开发环境路径解决器 + * @author starBlues + * @version 3.0.0 + */ +public class DevPathResolve implements PathResolve{ + + private final List devCompilePackageNames = new ArrayList<>(); + + public DevPathResolve() { + addCompilePackageName(); + } + + protected void addCompilePackageName(){ + // 添加插件信息查询目录 + devCompilePackageNames.add("target".concat(File.separator).concat(PackageStructure.META_INF_NAME)); + } + + @Override + public Path resolve(Path path) { + for (String devCompilePackageName : devCompilePackageNames) { + String compilePackagePathStr = path.toString() + File.separator + devCompilePackageName; + Path compilePackagePath = Paths.get(compilePackagePathStr); + if(Files.exists(compilePackagePath)){ + return compilePackagePath; + } + } + return null; + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/scanner/PathResolve.java b/spring-brick/src/main/java/com/gitee/starblues/core/scanner/PathResolve.java new file mode 100644 index 0000000000000000000000000000000000000000..e6be248da5150300ac25cdb41f44124ea9127aad --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/scanner/PathResolve.java @@ -0,0 +1,36 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.scanner; + +import java.nio.file.Path; + +/** + * 从路径中发现合适的插件 + * @author starBlues + * @version 3.0.0 + */ +public interface PathResolve { + + /** + * 过滤并返回正确的路径 + * @param path 待过滤路径 + * @return path 处理后的路径, 返回null 表示不可用 + */ + Path resolve(Path path); + + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/scanner/PluginScanner.java b/spring-brick/src/main/java/com/gitee/starblues/core/scanner/PluginScanner.java new file mode 100644 index 0000000000000000000000000000000000000000..32b4069cf62a068e0603350c1de342cf647d7987 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/scanner/PluginScanner.java @@ -0,0 +1,37 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.scanner; + +import java.nio.file.Path; +import java.util.List; + +/** + * 插件扫描者 + * @author starBlues + * @version 3.0.0 + */ +public interface PluginScanner { + + /** + * 从 rootDir 集合中扫描出插件路径 + * @param rootDir 根目录 + * @return 扫描出的目录 + */ + List scan(List rootDir); + + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/scanner/ProdPathResolve.java b/spring-brick/src/main/java/com/gitee/starblues/core/scanner/ProdPathResolve.java new file mode 100644 index 0000000000000000000000000000000000000000..9a8f7a73f3891bb66beec839327116b7ce850927 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/scanner/ProdPathResolve.java @@ -0,0 +1,78 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.scanner; + +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.utils.FilesUtils; +import com.gitee.starblues.utils.OrderUtils; +import com.gitee.starblues.utils.ObjectUtils; + +import java.io.File; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +/** + * 生产环境目录解决器 + * @author starBlues + * @version 3.0.0 + */ +public class ProdPathResolve implements PathResolve{ + + private final List packageSuffix = new ArrayList<>(); + + public ProdPathResolve(){ + // jar包 + addPackageSuffix(".jar"); + // zip包 + addPackageSuffix(".zip"); + } + + protected void addPackageSuffix(String name){ + if(ObjectUtils.isEmpty(name)){ + return; + } + packageSuffix.add(name); + } + + @Override + public Path resolve(Path path) { + if(isDirPlugin(path)){ + return path; + } + String fileName = path.getFileName().toString().toLowerCase(); + for (String suffixName : packageSuffix) { + boolean exist = fileName.endsWith(suffixName.toLowerCase()); + if(exist){ + return path; + } + } + return null; + } + + protected boolean isDirPlugin(Path path){ + File file = path.toFile(); + if(file.isFile()){ + return false; + } + + file = new File(FilesUtils.joiningFilePath(path.toString(), PackageStructure.resolvePath( + PackageStructure.PROD_MANIFEST_PATH + ))); + return file.exists() && file.isFile(); + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/version/SemverVersionInspector.java b/spring-brick/src/main/java/com/gitee/starblues/core/version/SemverVersionInspector.java new file mode 100644 index 0000000000000000000000000000000000000000..22c1e5c663903b2c26426a76e896b12a2215f865 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/version/SemverVersionInspector.java @@ -0,0 +1,35 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.version; + +import com.github.zafarkhaja.semver.Version; + +/** + * Semver标准版本检查 + * @author starBlues + * @version 3.0.0 + */ +public class SemverVersionInspector implements VersionInspector{ + + @Override + public int compareTo(String version1, String version2) { + Version v1 = Version.valueOf(version1); + Version v2 = Version.valueOf(version2); + return v1.compareTo(v2); + } + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/core/version/VersionInspector.java b/spring-brick/src/main/java/com/gitee/starblues/core/version/VersionInspector.java new file mode 100644 index 0000000000000000000000000000000000000000..be6a573a5394c19c9080a297112a0330e5289133 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/core/version/VersionInspector.java @@ -0,0 +1,35 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.core.version; + +/** + * 版本检查 + * @author starBlues + * @version 3.0.0 + */ +public interface VersionInspector { + + + /** + * 比较 v1 和 v2版本. + * @param v1 版本号码1 + * @param v2 版本号码2 + * @return 如果 v1大于等于v2, 则返回大于等于0的数字, 否则返回小于0的数字 + */ + int compareTo(String v1, String v2); + +} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/AutoIntegrationConfiguration.java b/spring-brick/src/main/java/com/gitee/starblues/integration/AutoIntegrationConfiguration.java similarity index 40% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/AutoIntegrationConfiguration.java rename to spring-brick/src/main/java/com/gitee/starblues/integration/AutoIntegrationConfiguration.java index 641f5c859a69a063aa4bbd377f61a3854a3e7d0a..2b5f0ea3c7c1f2de866e34a28fac796d5b01478d 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/AutoIntegrationConfiguration.java +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/AutoIntegrationConfiguration.java @@ -1,7 +1,25 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.gitee.starblues.integration; -import org.pf4j.RuntimeMode; -import org.pf4j.util.StringUtils; +import com.gitee.starblues.core.RuntimeMode; +import com.gitee.starblues.utils.ResourceUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @@ -13,12 +31,23 @@ import java.util.Set; /** * 自动集成的配置 * @author starBlues - * @version 2.4.4 + * @version 3.0.0 */ +@EqualsAndHashCode(callSuper = true) @Component @ConfigurationProperties(prefix = "plugin") +@Data public class AutoIntegrationConfiguration extends DefaultIntegrationConfiguration{ + public static final String ENABLE_KEY = "plugin.enable"; + public static final String ENABLE_STARTER_KEY = "plugin.enableStarter"; + + /** + * 是否启用插件功能 + */ + @Value("${enable:true}") + private Boolean enable; + /** * 运行模式 * 开发环境: development、dev @@ -28,10 +57,10 @@ public class AutoIntegrationConfiguration extends DefaultIntegrationConfiguratio private String runMode; /** - * 是否启用插件功能 + * 主程序包名 */ - @Value("${enable:true}") - private Boolean enable; + @Value("${mainPackage:}") + private String mainPackage; /** * 插件的路径 @@ -39,10 +68,16 @@ public class AutoIntegrationConfiguration extends DefaultIntegrationConfiguratio private List pluginPath; /** - * 插件文件的路径 + * 上传的插件所存储的临时目录 + */ + @Value("${uploadTempPath:temp}") + private String uploadTempPath; + + /** + * 在卸载插件后, 备份插件的目录 */ - @Value("${pluginConfigFilePath:plugin-configs}") - private String pluginConfigFilePath; + @Value("${backupPath:backupPlugin}") + private String backupPath; /** * 插件rest接口前缀. 默认: /plugins @@ -60,22 +95,20 @@ public class AutoIntegrationConfiguration extends DefaultIntegrationConfiguratio private Boolean enablePluginIdRestPathPrefix; /** - * 是否启用Swagger刷新机制. 默认启用 + * 启用的插件id */ - @Value("${enableSwaggerRefresh:true}") - private Boolean enableSwaggerRefresh; + private Set enablePluginIds; /** - * 在卸载插件后, 备份插件的目录 + * 禁用的插件id, 禁用后系统不会启动该插件 + * 如果禁用所有插件, 则Set集合中返回一个字符: * */ - @Value("${backupPath:}") - private String backupPath; + private Set disablePluginIds; /** - * 上传的插件所存储的临时目录 + * 设置初始化时插件启动的顺序 */ - @Value("${uploadTempPath:}") - private String uploadTempPath; + private List sortInitPluginIds; /** * 当前主程序的版本号, 用于校验插件是否可安装. @@ -90,64 +123,35 @@ public class AutoIntegrationConfiguration extends DefaultIntegrationConfiguratio * 设置为false表示插件设置的requires的版本号小于等于version值, 插件就可安装, 即requires<=x.y.z * 默认为false */ - @Value("${exactVersionAllowed:false}") - private Boolean exactVersionAllowed; - - /** - * 停止插件时, 是否停止当前插件依赖的插件 - * 默认不停止 - **/ - @Value("${exactVersionAllowed:false}") - private Boolean stopDependents; - - /** - * 启用的插件id - */ - private Set enablePluginIds; - - /** - * 禁用的插件id, 禁用后系统不会启动该插件 - * 如果禁用所有插件, 则Set集合中返回一个字符: * - */ - private Set disablePluginIds; - - /** - * 设置初始化时插件启动的顺序 - */ - private List sortInitPluginIds; - - /** - * 是否启用webSocket的功能. 如需启用, 则需要引入springboot支持的WebSocket依赖 - */ - @Value("${enableWebSocket:false}") - private Boolean enableWebSocket; + @Value("${exactVersion:false}") + private Boolean exactVersion; @Override - public RuntimeMode environment() { - return RuntimeMode.byName(runMode); + public boolean enable() { + if(enable == null){ + return true; + } + return enable; } @Override - public List pluginPath() { - return pluginPath; + public RuntimeMode environment() { + return RuntimeMode.byName(runMode); } @Override - public String pluginConfigFilePath() { - return pluginConfigFilePath; + public String mainPackage() { + return ResourceUtils.replacePackage(mainPackage); } @Override - public boolean enable() { - if(enable == null){ - return true; - } - return enable; + public List pluginPath() { + return pluginPath; } @Override public String uploadTempPath() { - if(StringUtils.isNullOrEmpty(uploadTempPath)){ + if(ObjectUtils.isEmpty(uploadTempPath)){ return super.uploadTempPath(); } return uploadTempPath; @@ -155,7 +159,7 @@ public class AutoIntegrationConfiguration extends DefaultIntegrationConfiguratio @Override public String backupPath() { - if(StringUtils.isNullOrEmpty(backupPath)){ + if(ObjectUtils.isEmpty(backupPath)){ return super.backupPath(); } return backupPath; @@ -163,7 +167,7 @@ public class AutoIntegrationConfiguration extends DefaultIntegrationConfiguratio @Override public String pluginRestPathPrefix() { - if(StringUtils.isNullOrEmpty(pluginRestPathPrefix)){ + if(pluginRestPathPrefix == null){ return super.pluginRestPathPrefix(); } else { return pluginRestPathPrefix; @@ -179,183 +183,4 @@ public class AutoIntegrationConfiguration extends DefaultIntegrationConfiguratio } } - @Override - public Set enablePluginIds() { - return enablePluginIds; - } - - @Override - public Set disablePluginIds() { - return disablePluginIds; - } - - @Override - public boolean enableSwaggerRefresh() { - return enableSwaggerRefresh; - } - - @Override - public List sortInitPluginIds() { - return sortInitPluginIds; - } - - @Override - public String version() { - return version; - } - - @Override - public boolean exactVersionAllowed() { - if(exactVersionAllowed == null){ - return false; - } - return exactVersionAllowed; - } - - @Override - public boolean enableWebSocket() { - if(enableWebSocket == null){ - return false; - } - return enableWebSocket; - } - - @Override - public boolean stopDependents() { - if(stopDependents == null){ - return super.stopDependents(); - } - return stopDependents; - } - - public String getRunMode() { - return runMode; - } - - public void setRunMode(String runMode) { - this.runMode = runMode; - } - - public Boolean getEnable() { - return enable; - } - - public void setEnable(Boolean enable) { - this.enable = enable; - } - - public List getPluginPath() { - if(ObjectUtils.isEmpty(pluginPath)){ - return super.pluginPath(); - } - return pluginPath; - } - - public void setPluginPath(List pluginPath) { - this.pluginPath = pluginPath; - } - - public String getPluginConfigFilePath() { - return pluginConfigFilePath; - } - - public void setPluginConfigFilePath(String pluginConfigFilePath) { - this.pluginConfigFilePath = pluginConfigFilePath; - } - - public String getPluginRestPathPrefix() { - return pluginRestPathPrefix; - } - - public void setPluginRestPathPrefix(String pluginRestPathPrefix) { - this.pluginRestPathPrefix = pluginRestPathPrefix; - } - - public Boolean getEnablePluginIdRestPathPrefix() { - return enablePluginIdRestPathPrefix; - } - - public void setEnablePluginIdRestPathPrefix(Boolean enablePluginIdRestPathPrefix) { - this.enablePluginIdRestPathPrefix = enablePluginIdRestPathPrefix; - } - - public Boolean getEnableSwaggerRefresh() { - return enableSwaggerRefresh; - } - - public void setEnableSwaggerRefresh(Boolean enableSwaggerRefresh) { - this.enableSwaggerRefresh = enableSwaggerRefresh; - } - - public String getBackupPath() { - return backupPath; - } - - public void setBackupPath(String backupPath) { - this.backupPath = backupPath; - } - - public String getUploadTempPath() { - return uploadTempPath; - } - - public void setUploadTempPath(String uploadTempPath) { - this.uploadTempPath = uploadTempPath; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - public Boolean getExactVersionAllowed() { - return exactVersionAllowed; - } - - public void setExactVersionAllowed(Boolean exactVersionAllowed) { - this.exactVersionAllowed = exactVersionAllowed; - } - - public Set getEnablePluginIds() { - return enablePluginIds; - } - - public void setEnablePluginIds(Set enablePluginIds) { - this.enablePluginIds = enablePluginIds; - } - - public Set getDisablePluginIds() { - return disablePluginIds; - } - - public void setDisablePluginIds(Set disablePluginIds) { - this.disablePluginIds = disablePluginIds; - } - - public List getSortInitPluginIds() { - return sortInitPluginIds; - } - - public void setSortInitPluginIds(List sortInitPluginIds) { - this.sortInitPluginIds = sortInitPluginIds; - } - - public Boolean getEnableWebSocket() { - return enableWebSocket; - } - - public void setEnableWebSocket(Boolean enableWebSocket) { - this.enableWebSocket = enableWebSocket; - } - - public Boolean getStopDependents() { - return stopDependents; - } - - public void setStopDependents(Boolean stopDependents) { - this.stopDependents = stopDependents; - } } diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/DefaultIntegrationConfiguration.java b/spring-brick/src/main/java/com/gitee/starblues/integration/DefaultIntegrationConfiguration.java similarity index 50% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/DefaultIntegrationConfiguration.java rename to spring-brick/src/main/java/com/gitee/starblues/integration/DefaultIntegrationConfiguration.java index c1da6835fd8b8295d6960299246e9ac50ecd0dca..c80f1bfc68eed38423cf9690557b9f8ef2c1c155 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/DefaultIntegrationConfiguration.java +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/DefaultIntegrationConfiguration.java @@ -1,6 +1,22 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.gitee.starblues.integration; -import org.pf4j.RuntimeMode; +import com.gitee.starblues.utils.Assert; import java.util.ArrayList; import java.util.List; @@ -10,26 +26,25 @@ import java.util.Set; * 默认的插件集成配置。给非必须配置设置了默认值 * * @author starBlues - * @version 2.4.4 + * @version 3.0.0 */ public abstract class DefaultIntegrationConfiguration implements IntegrationConfiguration{ - @Override - public List pluginPath() { - List pluginPath = new ArrayList<>(1); - if(environment() == RuntimeMode.DEPLOYMENT){ - pluginPath.add("plugins"); - } else if(environment() == RuntimeMode.DEVELOPMENT){ - pluginPath.add("./plugins/"); - } - return pluginPath; - } + public static final String DEFAULT_PLUGIN_REST_PATH_PREFIX = "plugins"; + public static final boolean DEFAULT_ENABLE_PLUGIN_ID_REST_PATH_PREFIX = true; @Override public boolean enable() { return true; } + @Override + public List pluginPath() { + List pluginPath = new ArrayList<>(1); + pluginPath.add("~/plugins/"); + return pluginPath; + } + @Override public String uploadTempPath(){ return "temp"; @@ -42,12 +57,12 @@ public abstract class DefaultIntegrationConfiguration implements IntegrationConf @Override public String pluginRestPathPrefix(){ - return "/plugins"; + return DEFAULT_PLUGIN_REST_PATH_PREFIX; } @Override public boolean enablePluginIdRestPathPrefix() { - return true; + return DEFAULT_ENABLE_PLUGIN_ID_REST_PATH_PREFIX; } @Override @@ -60,11 +75,6 @@ public abstract class DefaultIntegrationConfiguration implements IntegrationConf return null; } - @Override - public boolean enableSwaggerRefresh() { - return true; - } - @Override public List sortInitPluginIds() { return null; @@ -76,17 +86,16 @@ public abstract class DefaultIntegrationConfiguration implements IntegrationConf } @Override - public boolean exactVersionAllowed() { + public boolean exactVersion() { return false; } + /** + * 检查配置 + */ @Override - public boolean enableWebSocket() { - return false; + public void checkConfig(){ + Assert.isNotEmpty(mainPackage(), "插件配置: mainPackage 不能为空"); } - @Override - public boolean stopDependents() { - return false; - } } diff --git a/spring-brick/src/main/java/com/gitee/starblues/integration/ExtendPointConfiguration.java b/spring-brick/src/main/java/com/gitee/starblues/integration/ExtendPointConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..46bb11a84f46c70aa12efdbd01e8057f103b861f --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/ExtendPointConfiguration.java @@ -0,0 +1,84 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.integration; + +import com.gitee.starblues.core.DefaultRealizeProvider; +import com.gitee.starblues.core.RealizeProvider;; +import com.gitee.starblues.core.launcher.plugin.BasicMainResourcePatternDefiner; +import com.gitee.starblues.integration.operator.DefaultPluginOperator; +import com.gitee.starblues.integration.operator.PluginOperator; +import com.gitee.starblues.integration.operator.PluginOperatorWrapper; +import com.gitee.starblues.integration.user.DefaultPluginUser; +import com.gitee.starblues.integration.user.PluginUser; +import com.gitee.starblues.spring.extract.ExtractFactory; +import org.springframework.boot.autoconfigure.condition.*; +import org.springframework.context.annotation.Bean; +import org.springframework.context.support.GenericApplicationContext; + +/** + * 系统Bean配置 + * @author starBlues + * @version 3.0.0 + */ +public class ExtendPointConfiguration { + + private final GenericApplicationContext applicationContext; + private final IntegrationConfiguration configuration; + + public ExtendPointConfiguration(GenericApplicationContext applicationContext, + IntegrationConfiguration configuration) { + this.applicationContext = applicationContext; + this.configuration = configuration; + } + + @Bean + @ConditionalOnMissingBean + public PluginUser createPluginUser() { + return new DefaultPluginUser(applicationContext); + } + + @Bean + @ConditionalOnMissingBean + public PluginOperator createPluginOperator(RealizeProvider realizeProvider) { + PluginOperator pluginOperator = new DefaultPluginOperator( + applicationContext, + realizeProvider, + configuration + ); + return new PluginOperatorWrapper(pluginOperator, configuration); + } + + @Bean + @ConditionalOnMissingBean + public RealizeProvider realizeProvider() { + DefaultRealizeProvider defaultRealizeProvider = new DefaultRealizeProvider(configuration, applicationContext); + defaultRealizeProvider.init(); + return defaultRealizeProvider; + } + + @Bean + public ExtractFactory extractFactory(){ + return ExtractFactory.getInstant(); + } + + @Bean + @ConditionalOnMissingBean + public BasicMainResourcePatternDefiner mainResourcePatternDefiner(){ + return new BasicMainResourcePatternDefiner(configuration.mainPackage()); + } + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/integration/ExtendPointWebConfiguration.java b/spring-brick/src/main/java/com/gitee/starblues/integration/ExtendPointWebConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..e1e197d57fcb14aba5bb0cbabf9f0607d70ab25c --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/ExtendPointWebConfiguration.java @@ -0,0 +1,93 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.integration; + +import com.gitee.starblues.integration.listener.SwaggerListener; +import com.gitee.starblues.spring.web.PluginStaticResourceConfig; +import com.gitee.starblues.spring.web.PluginStaticResourceWebMvcConfigurer; +import com.gitee.starblues.spring.web.thymeleaf.PluginThymeleafInvolved; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.web.servlet.resource.ResourceResolver; +import org.thymeleaf.spring5.SpringTemplateEngine; +import org.thymeleaf.templatemode.TemplateMode; +import springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper; + +/** + * 系统web环境配置点 + * @author starBlues + * @version 3.0.0 + */ +@ConditionalOnWebApplication +@Import({ + ExtendPointWebConfiguration.PluginStaticResourceConfiguration.class, + ExtendPointWebConfiguration.PluginThymeleafConfiguration.class, + ExtendPointWebConfiguration.SwaggerListenerConfiguration.class, +}) +public class ExtendPointWebConfiguration { + + + @ConditionalOnClass(ResourceResolver.class) + public static class PluginStaticResourceConfiguration{ + + @Bean + @ConditionalOnMissingBean + public PluginStaticResourceWebMvcConfigurer pluginWebResourceResolver(PluginStaticResourceConfig resourceConfig){ + return new PluginStaticResourceWebMvcConfigurer(resourceConfig); + } + + @Bean + @ConditionalOnMissingBean + public PluginStaticResourceConfig pluginStaticResourceConfig() { + return new PluginStaticResourceConfig(); + } + } + + @ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class }) + @ConditionalOnProperty(name = "spring.thymeleaf.enabled", havingValue = "true", matchIfMissing = true) + public static class PluginThymeleafConfiguration{ + + @Bean + @ConditionalOnMissingBean + public PluginThymeleafInvolved pluginThymeleafInvolved(){ + return new PluginThymeleafInvolved(); + } + } + + @ConditionalOnClass({ DocumentationPluginsBootstrapper.class }) + public static class SwaggerListenerConfiguration { + + private final GenericApplicationContext applicationContext; + + public SwaggerListenerConfiguration(GenericApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + @Bean + @ConditionalOnMissingBean + public SwaggerListener swaggerListener(){ + return new SwaggerListener(applicationContext); + } + + } + +} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/IntegrationConfiguration.java b/spring-brick/src/main/java/com/gitee/starblues/integration/IntegrationConfiguration.java similarity index 53% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/IntegrationConfiguration.java rename to spring-brick/src/main/java/com/gitee/starblues/integration/IntegrationConfiguration.java index 29c73d6052c310b6f58eb9d39b7b3127da761818..ace90166a30961c2ccff00468b72c83d6c49163c 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/IntegrationConfiguration.java +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/IntegrationConfiguration.java @@ -1,6 +1,26 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.gitee.starblues.integration; -import org.pf4j.RuntimeMode; + +import com.gitee.starblues.common.Constants; +import com.gitee.starblues.core.RuntimeMode; +import com.gitee.starblues.utils.ObjectUtils; +import org.springframework.http.CacheControl; import java.util.List; import java.util.Set; @@ -9,16 +29,28 @@ import java.util.Set; /** * 插件集成时的配置接口。插件集成的配置接口 * @author starBlues - * @version 2.4.4 + * @version 3.0.0 */ public interface IntegrationConfiguration { /** - * 运行环境。运行项目时的模式。分为开发环境(DEVELOPMENT)、生产环境(DEPLOYMENT) - * @return RuntimeMode.DEVELOPMENT、RuntimeMode.DEPLOYMENT + * 是否启用该插件框架 + * @return true 启用, false 禁用 + */ + boolean enable(); + + /** + * 运行环境。运行项目时的模式。分为开发环境(Dev)、生产环境(Prod) + * @return RuntimeMode.DEV、RuntimeMode.PROD */ RuntimeMode environment(); + /** + * 主程序包名 + * @return String + */ + String mainPackage(); + /** * 插件的路径。可设置多个插件路径 * 开发环境建议直接配置为插件模块的父级目录。例如: plugins。如果启动主程序时, 插件为加载, 请检查该配置是否正确。 @@ -26,14 +58,6 @@ public interface IntegrationConfiguration { */ List pluginPath(); - /** - * 插件文件的配置路径。在生产环境下, 插件的配置文件路径。 - * 在生产环境下, 请将所有插件使用到的配置文件统一放到该路径下管理。 - * 在开发环境下,配置为空串。程序会自动从 resources 获取配置文件, 所以请确保编译后的target 下存在该配置文件 - * @return 插件文件的配置路径 - */ - String pluginConfigFilePath(); - /** * 上传插件包存储的临时路径。默认 temp(相对于主程序jar路径)。 * @return 上传插件的临时保存路径。 @@ -60,13 +84,6 @@ public interface IntegrationConfiguration { */ boolean enablePluginIdRestPathPrefix(); - - /** - * 是否启用该插件框架 - * @return true 启用, false 禁用 - */ - boolean enable(); - /** * 启用的插件id * @return Set @@ -80,12 +97,6 @@ public interface IntegrationConfiguration { */ Set disablePluginIds(); - /** - * 是否启用Swagger刷新机制 - * @return 启用返回true, 不启用返回 false - */ - boolean enableSwaggerRefresh(); - /** * 设置初始化时插件启动的顺序. * @return 有顺序的插件id @@ -95,7 +106,7 @@ public interface IntegrationConfiguration { /** * 当前主程序的版本号, 用于校验插件是否可安装. * 插件中可通过插件配置信息 requires 来指定可安装的主程序版本 - * @return 系统版本号, 如果为: 0.0.0 的话, 表示不校验 + * @return 系统版本号, 如果为为空或者 0.0.0 表示不校验 */ String version(); @@ -105,18 +116,61 @@ public interface IntegrationConfiguration { * 默认为false * @return true or false */ - boolean exactVersionAllowed(); + boolean exactVersion(); + + + /** + * 检查配置 + */ + default void checkConfig(){}; + + + /** + * 是否是开发环境 + * @return boolean + */ + default boolean isDev(){ + return environment() == RuntimeMode.DEV; + } + + /** + * 是否是生产环境 + * @return boolean + */ + default boolean isProd(){ + return environment() == RuntimeMode.PROD; + } + /** - * 是否启用webSocket功能. 如需启用, 则需要引入springboot支持的WebSocket依赖 - * @return 启用返回true,不启用返回false + * 是否被启动 + * @param pluginId 插件id + * @return true: 启用, false: 未启用 */ - boolean enableWebSocket(); + default boolean isEnable(String pluginId){ + if(ObjectUtils.isEmpty(enablePluginIds())){ + return true; + } + if(isDisabled(pluginId)){ + return false; + } + return enablePluginIds().contains(pluginId); + } + /** - * 停止插件时, 是否停止依赖的插件 - * @return 停止返回true,不停止返回false + * 是否被禁用 + * @param pluginId 插件id + * @return true: 禁用, false: 未禁用 */ - boolean stopDependents(); + default boolean isDisabled(String pluginId){ + if(ObjectUtils.isEmpty(disablePluginIds())){ + return false; + } + if(disablePluginIds().contains(Constants.DISABLED_ALL_PLUGIN)){ + return true; + } + return disablePluginIds().contains(pluginId); + } } diff --git a/spring-brick/src/main/java/com/gitee/starblues/integration/SpringBootPluginStarter.java b/spring-brick/src/main/java/com/gitee/starblues/integration/SpringBootPluginStarter.java new file mode 100644 index 0000000000000000000000000000000000000000..bb5289dbf0b060a67fee7bf34b210ed4f72076f7 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/SpringBootPluginStarter.java @@ -0,0 +1,37 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.integration; + +import com.gitee.starblues.integration.application.AutoPluginApplication; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * spring-boot-starter + * + * @author starBlues + * @version 3.0.0 + */ +@Configuration(proxyBeanMethods = true) +@EnableConfigurationProperties(AutoIntegrationConfiguration.class) +@ConditionalOnExpression("${" + AutoIntegrationConfiguration.ENABLE_STARTER_KEY + ":true}") +@Import(AutoPluginApplication.class) +public class SpringBootPluginStarter { + +} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/application/AbstractPluginApplication.java b/spring-brick/src/main/java/com/gitee/starblues/integration/application/AbstractPluginApplication.java similarity index 33% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/application/AbstractPluginApplication.java rename to spring-brick/src/main/java/com/gitee/starblues/integration/application/AbstractPluginApplication.java index 4079a722bb31b5ab61abc6fa21dc81ddaea6b5f6..b666cfb8d660ca027723f7f62326f6839c7effca 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/application/AbstractPluginApplication.java +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/application/AbstractPluginApplication.java @@ -1,12 +1,24 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.gitee.starblues.integration.application; -import com.gitee.starblues.extension.AbstractExtension; -import com.gitee.starblues.extension.ExtensionFactory; import com.gitee.starblues.integration.IntegrationConfiguration; import com.gitee.starblues.integration.listener.PluginListener; import com.gitee.starblues.integration.listener.PluginListenerFactory; -import com.gitee.starblues.integration.listener.PluginStateListenerFactory; -import org.pf4j.PluginStateListener; import org.springframework.beans.factory.BeanCreationException; import org.springframework.context.ApplicationContext; @@ -16,63 +28,10 @@ import java.util.List; * 公用的的插件应用 * * @author starBlues - * @version 2.4.4 + * @version 3.0.0 */ public abstract class AbstractPluginApplication implements PluginApplication { - protected final PluginListenerFactory listenerFactory = new PluginListenerFactory(); - protected final PluginStateListenerFactory pluginStateListenerFactory = new PluginStateListenerFactory(); - - @Override - public PluginApplication addExtension(AbstractExtension extension) { - if(extension == null){ - return this; - } - extension.setPluginApplication(this); - ExtensionFactory.addExtension(extension); - return this; - } - - @Override - public void addListener(PluginListener pluginListener) { - this.listenerFactory.addPluginListener(pluginListener); - } - - @Override - public void addListener(Class pluginListenerClass) { - listenerFactory.addPluginListener(pluginListenerClass); - } - - @Override - public void addListener(List pluginListeners) { - if(pluginListeners == null || pluginListeners.isEmpty()){ - return; - } - for (PluginListener pluginListener : pluginListeners) { - this.listenerFactory.addPluginListener(pluginListener); - } - } - - @Override - public void addPf4jStateListener(PluginStateListener pluginListener) { - pluginStateListenerFactory.addStateListener(pluginListener); - } - - @Override - public void addPf4jStateListener(Class pluginListenerClass) { - pluginStateListenerFactory.addStateListener(pluginListenerClass); - } - - @Override - public void addPf4jStateListener(List pluginListeners) { - if(pluginListeners == null || pluginListeners.isEmpty()){ - return; - } - for (PluginStateListener pluginListener : pluginListeners) { - this.pluginStateListenerFactory.addStateListener(pluginListener); - } - } - /** * 子类可通过Application 获取插件定义的配置 * @param applicationContext applicationContext @@ -86,8 +45,8 @@ public abstract class AbstractPluginApplication implements PluginApplication { // no show exception } if(configuration == null){ - throw new BeanCreationException("Not Found IntegrationConfiguration, Please define " + - "IntegrationConfiguration to Spring Bean."); + throw new BeanCreationException("没有发现 Bean, " + + "请在 Spring 容器中将 定义为Bean"); } return configuration; } diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/application/AutoPluginApplication.java b/spring-brick/src/main/java/com/gitee/starblues/integration/application/AutoPluginApplication.java similarity index 48% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/application/AutoPluginApplication.java rename to spring-brick/src/main/java/com/gitee/starblues/integration/application/AutoPluginApplication.java index 889830761260fed018107ea11a88ebf2e084770b..7d1944073e8173a0e9083a8774f4c969debc3be9 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/application/AutoPluginApplication.java +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/application/AutoPluginApplication.java @@ -1,30 +1,45 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.gitee.starblues.integration.application; -import com.gitee.starblues.integration.pf4j.Pf4jFactory; +import com.gitee.starblues.integration.ExtendPointConfiguration; +import com.gitee.starblues.integration.ExtendPointWebConfiguration; import com.gitee.starblues.integration.listener.PluginInitializerListener; -import org.springframework.beans.factory.InitializingBean; +import com.gitee.starblues.integration.operator.PluginOperator; +import com.gitee.starblues.integration.user.PluginUser; +import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Import; /** * 自动初始化的 PluginApplication。该PluginApplication 基于 Spring InitializingBean 自动初始化插件。 * * @author starBlues - * @version 2.2.0 + * @version 3.0.0 */ +@Import({ExtendPointConfiguration.class, ExtendPointWebConfiguration.class}) public class AutoPluginApplication extends DefaultPluginApplication - implements PluginApplication, InitializingBean, ApplicationContextAware { + implements PluginApplication, ApplicationContextAware, ApplicationListener { private ApplicationContext applicationContext; private PluginInitializerListener pluginInitializerListener; - public AutoPluginApplication() { - super(); - } - - public AutoPluginApplication(Pf4jFactory pf4jFactory) { - super(pf4jFactory); - } /** * 设置插件初始化监听器 @@ -50,13 +65,19 @@ public class AutoPluginApplication extends DefaultPluginApplication /** * Spring boot bean属性被Set完后调用。会自动初始化插件 - * @throws Exception 初始化异常 */ @Override - public void afterPropertiesSet() throws Exception { - if(applicationContext == null){ - throw new Exception("Auto initialize failed. ApplicationContext Not injected."); - } + public void onApplicationEvent(ApplicationStartedEvent event) { super.initialize(applicationContext, pluginInitializerListener); } + + @Override + public PluginOperator getPluginOperator() { + return createPluginOperator(applicationContext); + } + + @Override + public PluginUser getPluginUser() { + return createPluginUser(applicationContext); + } } diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/application/DefaultPluginApplication.java b/spring-brick/src/main/java/com/gitee/starblues/integration/application/DefaultPluginApplication.java similarity index 46% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/application/DefaultPluginApplication.java rename to spring-brick/src/main/java/com/gitee/starblues/integration/application/DefaultPluginApplication.java index 01cd3ac358d6b6737b4b9aece64be427d3556260..c193dcee4c69e65fb4a56ddf772663826009c8c8 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/application/DefaultPluginApplication.java +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/application/DefaultPluginApplication.java @@ -1,24 +1,41 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.gitee.starblues.integration.application; -import com.gitee.starblues.integration.operator.PluginOperatorWrapper; -import com.gitee.starblues.integration.pf4j.DefaultPf4jFactory; +import com.gitee.starblues.annotation.Extract; import com.gitee.starblues.integration.IntegrationConfiguration; -import com.gitee.starblues.integration.pf4j.Pf4jFactory; import com.gitee.starblues.integration.listener.PluginInitializerListener; -import com.gitee.starblues.integration.operator.DefaultPluginOperator; import com.gitee.starblues.integration.operator.PluginOperator; -import com.gitee.starblues.integration.user.DefaultPluginUser; +import com.gitee.starblues.integration.operator.PluginOperatorWrapper; import com.gitee.starblues.integration.user.PluginUser; -import org.pf4j.PluginManager; -import org.pf4j.PluginStateListener; +import com.gitee.starblues.spring.extract.DefaultExtractFactory; +import com.gitee.starblues.spring.extract.DefaultOpExtractFactory; +import com.gitee.starblues.spring.extract.ExtractFactory; +import com.gitee.starblues.spring.extract.OpExtractFactory; +import com.gitee.starblues.utils.ObjectUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericApplicationContext; -import org.springframework.util.ObjectUtils; -import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; @@ -26,13 +43,11 @@ import java.util.concurrent.atomic.AtomicBoolean; /** * 默认的插件 PluginApplication * @author starBlues - * @version 2.4.4 + * @version 3.0.0 */ public class DefaultPluginApplication extends AbstractPluginApplication { - private final Logger log = LoggerFactory.getLogger(this.getClass()); - - protected Pf4jFactory integrationFactory; + private final static Logger LOG = LoggerFactory.getLogger(DefaultPluginApplication.class); private PluginUser pluginUser; private PluginOperator pluginOperator; @@ -42,11 +57,6 @@ public class DefaultPluginApplication extends AbstractPluginApplication { public DefaultPluginApplication() { } - public DefaultPluginApplication(Pf4jFactory integrationFactory){ - this.integrationFactory = integrationFactory; - } - - @Override public synchronized void initialize(ApplicationContext applicationContext, PluginInitializerListener listener) { @@ -54,54 +64,50 @@ public class DefaultPluginApplication extends AbstractPluginApplication { if(beInitialized.get()){ throw new RuntimeException("Plugin has been initialized"); } + // 获取Configuration IntegrationConfiguration configuration = getConfiguration(applicationContext); - if(integrationFactory == null){ - integrationFactory = new DefaultPf4jFactory(configuration); - } - PluginManager pluginManager = integrationFactory.getPluginManager(); - addPf4jStateListener(pluginManager, applicationContext); - pluginUser = createPluginUser(applicationContext, pluginManager); - pluginOperator = createPluginOperator(applicationContext, pluginManager, configuration); + // 检查配置 + configuration.checkConfig(); + createPluginUser(applicationContext); + createPluginOperator(applicationContext); try { - setBeanFactory(applicationContext); + if(!(pluginOperator instanceof PluginOperatorWrapper)){ + pluginOperator = new PluginOperatorWrapper(pluginOperator, configuration); + } + GenericApplicationContext genericApplicationContext = (GenericApplicationContext) applicationContext; + setBeanFactory(genericApplicationContext); + initExtractFactory(genericApplicationContext); pluginOperator.initPlugins(listener); beInitialized.set(true); } catch (Exception e) { - e.printStackTrace(); + LOG.error("初始化插件异常." + e.getMessage()); } } /** * 创建插件使用者。子类可扩展 * @param applicationContext Spring ApplicationContext - * @param pluginManager 插件管理器 - * @return PluginUser + * @return pluginUser */ - protected PluginUser createPluginUser(ApplicationContext applicationContext, - PluginManager pluginManager){ - return new DefaultPluginUser(applicationContext, pluginManager); + protected synchronized PluginUser createPluginUser(ApplicationContext applicationContext){ + if(pluginUser == null){ + pluginUser = applicationContext.getBean(PluginUser.class); + } + return pluginUser; } /** * 创建插件操作者。子类可扩展 * @param applicationContext Spring ApplicationContext - * @param pluginManager 插件管理器 - * @param configuration 当前集成的配置 - * @return PluginOperator + * @return pluginOperator */ - protected PluginOperator createPluginOperator(ApplicationContext applicationContext, - PluginManager pluginManager, - IntegrationConfiguration configuration){ - PluginOperator pluginOperator = new DefaultPluginOperator( - applicationContext, - configuration, - pluginManager, - this.listenerFactory - ); - return new PluginOperatorWrapper(pluginOperator, configuration); + protected synchronized PluginOperator createPluginOperator(ApplicationContext applicationContext){ + if(pluginOperator == null){ + pluginOperator = applicationContext.getBean(PluginOperator.class); + } + return pluginOperator; } - @Override public PluginOperator getPluginOperator() { assertInjected(); @@ -115,29 +121,38 @@ public class DefaultPluginApplication extends AbstractPluginApplication { } /** - * 将pf4j中的监听器加入 - * @param pluginManager pluginManager - * @param applicationContext ApplicationContext + * 初始化扩展工厂 + * @param applicationContext applicationContext */ - private void addPf4jStateListener(PluginManager pluginManager, ApplicationContext applicationContext){ - List pluginStateListeners = pluginStateListenerFactory - .buildListenerClass((GenericApplicationContext) applicationContext); - if(ObjectUtils.isEmpty(pluginStateListeners)){ + private void initExtractFactory(GenericApplicationContext applicationContext){ + ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory(); + DefaultExtractFactory defaultExtractFactory = (DefaultExtractFactory)ExtractFactory.getInstant(); + initMainExtract((OpExtractFactory)defaultExtractFactory.getTarget(), beanFactory); + } + + /** + * 初始化主程序中的扩展 + * @param opExtractFactory opExtractFactory + * @param beanFactory beanFactory + */ + private void initMainExtract(OpExtractFactory opExtractFactory, ListableBeanFactory beanFactory){ + // 获取主程序的扩展 + Map extractMap = beanFactory.getBeansWithAnnotation(Extract.class); + if(ObjectUtils.isEmpty(extractMap)){ return; } - for (PluginStateListener pluginStateListener : pluginStateListeners) { - pluginManager.addPluginStateListener(pluginStateListener); + for (Object extract : extractMap.values()) { + opExtractFactory.addOfMain(extract); } } - /** * 直接将 PluginOperator 和 PluginUser 注入到ApplicationContext容器中 * @param applicationContext ApplicationContext */ - private void setBeanFactory(ApplicationContext applicationContext){ - GenericApplicationContext genericApplicationContext = (GenericApplicationContext) applicationContext; - DefaultListableBeanFactory defaultListableBeanFactory = genericApplicationContext.getDefaultListableBeanFactory(); + @Deprecated + private void setBeanFactory(GenericApplicationContext applicationContext){ + DefaultListableBeanFactory defaultListableBeanFactory = applicationContext.getDefaultListableBeanFactory(); defaultListableBeanFactory.registerSingleton(pluginOperator.getClass().getName(), pluginOperator); defaultListableBeanFactory.registerSingleton(pluginUser.getClass().getName(), pluginUser); } diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/application/PluginApplication.java b/spring-brick/src/main/java/com/gitee/starblues/integration/application/PluginApplication.java similarity index 54% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/application/PluginApplication.java rename to spring-brick/src/main/java/com/gitee/starblues/integration/application/PluginApplication.java index c85f0e500299dcaef12026f294454f850489ea62..e41550329b70214d81eea7251313a3b3de5d4ca4 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/application/PluginApplication.java +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/application/PluginApplication.java @@ -1,8 +1,21 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.gitee.starblues.integration.application; -import com.gitee.starblues.extension.AbstractExtension; -import com.gitee.starblues.integration.PluginListenerContext; -import com.gitee.starblues.integration.PluginStateListenerContext; import com.gitee.starblues.integration.listener.PluginInitializerListener; import com.gitee.starblues.integration.operator.PluginOperator; import com.gitee.starblues.integration.user.PluginUser; @@ -11,9 +24,9 @@ import org.springframework.context.ApplicationContext; /** * 插件应用。 * @author starBlues - * @version 2.4.3 + * @version 3.0.0 */ -public interface PluginApplication extends PluginListenerContext, PluginStateListenerContext { +public interface PluginApplication{ /** * 初始化 @@ -34,12 +47,4 @@ public interface PluginApplication extends PluginListenerContext, PluginStateLis * @return 插件操作者 */ PluginUser getPluginUser(); - - /** - * 添加扩展 - * @param extension 扩展类 - * @return PluginApplication - */ - PluginApplication addExtension(AbstractExtension extension); - } diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/listener/DefaultInitializerListener.java b/spring-brick/src/main/java/com/gitee/starblues/integration/listener/DefaultInitializerListener.java similarity index 33% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/listener/DefaultInitializerListener.java rename to spring-brick/src/main/java/com/gitee/starblues/integration/listener/DefaultInitializerListener.java index f5d156385e23ca0261294b5bc5cd871a2458c69d..43ca5c09d195d52be66e101dc8b152e9b737c173 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/listener/DefaultInitializerListener.java +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/listener/DefaultInitializerListener.java @@ -1,36 +1,58 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.gitee.starblues.integration.listener; -import com.gitee.starblues.extension.ExtensionInitializer; +import com.gitee.starblues.utils.SpringBeanUtils; import org.springframework.context.ApplicationContext; /** * 默认的初始化监听者。内置注册 * * @author starBlues - * @version 1.0 + * @version 3.0.0 */ public class DefaultInitializerListener implements PluginInitializerListener{ - public final ApplicationContext applicationContext; + private final SwaggerListener swaggerListener; public DefaultInitializerListener(ApplicationContext applicationContext) { - this.applicationContext = applicationContext; + this.swaggerListener = SpringBeanUtils.getExistBean(applicationContext, SwaggerListener.class); } @Override public void before() { - // 初始化扩展注册信息 - ExtensionInitializer.initialize(applicationContext); + } @Override public void complete() { - + refresh(); } @Override public void failure(Throwable throwable) { + refresh(); + } + private void refresh(){ + if(swaggerListener != null){ + swaggerListener.refresh(); + } } + } diff --git a/spring-brick/src/main/java/com/gitee/starblues/integration/listener/DefaultPluginListenerFactory.java b/spring-brick/src/main/java/com/gitee/starblues/integration/listener/DefaultPluginListenerFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..070659afdfccaebd7b538cb934a5915d47a74d26 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/listener/DefaultPluginListenerFactory.java @@ -0,0 +1,142 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.integration.listener; + +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.utils.SpringBeanUtils; +import org.springframework.context.ApplicationContext; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +/** + * 默认的插件工厂 + * @author starBlues + * @version 3.0.0 + */ +public class DefaultPluginListenerFactory implements PluginListenerFactory{ + + private final List listeners; + + public DefaultPluginListenerFactory(ApplicationContext applicationContext){ + listeners = new ArrayList<>(); + addPluginListener(new SwaggerListener(applicationContext)); + addExtendPluginListener(applicationContext); + } + + public DefaultPluginListenerFactory(){ + listeners = new ArrayList<>(); + } + + + private void addExtendPluginListener(ApplicationContext applicationContext){ + List pluginListeners = SpringBeanUtils.getBeans(applicationContext, PluginListener.class); + listeners.addAll(pluginListeners); + } + + @Override + public synchronized void addPluginListener(PluginListener pluginListener) { + if(pluginListener != null){ + listeners.add(pluginListener); + } + } + + @Override + public List getListeners() { + return listeners; + } + + @Override + public void loadSuccess(PluginInfo pluginInfo) { + for (PluginListener listener : listeners) { + try { + listener.loadSuccess(pluginInfo); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Override + public void loadFailure(Path path, Throwable throwable) { + for (PluginListener listener : listeners) { + try { + listener.loadFailure(path, throwable); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Override + public void unLoadSuccess(PluginInfo pluginInfo) { + for (PluginListener listener : listeners) { + try { + listener.unLoadSuccess(pluginInfo); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Override + public void startSuccess(PluginInfo pluginInfo) { + for (PluginListener listener : listeners) { + try { + listener.startSuccess(pluginInfo); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Override + public void startFailure(PluginInfo pluginInfo, Throwable throwable) { + for (PluginListener listener : listeners) { + try { + listener.startFailure(pluginInfo, throwable); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Override + public void stopSuccess(PluginInfo pluginInfo) { + for (PluginListener listener : listeners) { + try { + listener.stopSuccess(pluginInfo); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Override + public void stopFailure(PluginInfo pluginInfo, Throwable throwable) { + for (PluginListener listener : listeners) { + try { + listener.stopFailure(pluginInfo, throwable); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + +} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/listener/PluginInitializerListener.java b/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginInitializerListener.java similarity index 37% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/listener/PluginInitializerListener.java rename to spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginInitializerListener.java index 5b3778770ea3838355d283c8319cdb326d923c8d..1da690a262457dd461cd8e8f9c1a7f8597bf2904 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/listener/PluginInitializerListener.java +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginInitializerListener.java @@ -1,3 +1,19 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.gitee.starblues.integration.listener; @@ -5,7 +21,7 @@ package com.gitee.starblues.integration.listener; * 插件初始化监听者 * * @author starBlues - * @version 1.0 + * @version 3.0.0 */ public interface PluginInitializerListener { diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/listener/PluginInitializerListenerFactory.java b/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginInitializerListenerFactory.java similarity index 62% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/listener/PluginInitializerListenerFactory.java rename to spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginInitializerListenerFactory.java index 747c84dab46998989f471e1f8b99d65ebc27e0fd..01d869836d515e82a2589b31b2e18c8ce94050e9 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/listener/PluginInitializerListenerFactory.java +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginInitializerListenerFactory.java @@ -1,5 +1,22 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.gitee.starblues.integration.listener; +import com.gitee.starblues.utils.SpringBeanUtils; import org.springframework.context.ApplicationContext; import java.util.ArrayList; @@ -9,7 +26,7 @@ import java.util.List; * 插件初始化监听者工厂 * * @author starBlues - * @version 1.0 + * @version 3.0.0 */ public class PluginInitializerListenerFactory implements PluginInitializerListener { @@ -21,6 +38,13 @@ public class PluginInitializerListenerFactory implements PluginInitializerListen this.applicationContext = applicationContext; // 添加默认的初始化监听者 pluginInitializerListeners.add(new DefaultInitializerListener(applicationContext)); + addExtendPluginListener(applicationContext); + } + + private void addExtendPluginListener(ApplicationContext applicationContext){ + List initializerListeners = SpringBeanUtils.getBeans(applicationContext, + PluginInitializerListener.class); + pluginInitializerListeners.addAll(initializerListeners); } @Override @@ -60,7 +84,7 @@ public class PluginInitializerListenerFactory implements PluginInitializerListen * 添加监听者 * @param pluginInitializerListener pluginInitializerListener */ - public void addPluginInitializerListeners(PluginInitializerListener pluginInitializerListener){ + public void addListener(PluginInitializerListener pluginInitializerListener){ if(pluginInitializerListener != null){ pluginInitializerListeners.add(pluginInitializerListener); } diff --git a/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginListener.java b/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginListener.java new file mode 100644 index 0000000000000000000000000000000000000000..53bd629af9ac749769a090c3361f259b729a085c --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginListener.java @@ -0,0 +1,86 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.integration.listener; + + +import com.gitee.starblues.core.PluginInfo; + +import java.nio.file.Path; + +/** + * 插件监听者 + * + * @author starBlues + * @version 3.0.0 + */ +public interface PluginListener { + + /** + * 加载插件成功 + * @param pluginInfo 插件信息 + */ + default void loadSuccess(PluginInfo pluginInfo){} + + /** + * 加载失败 + * @param path 要加载的插件路径 + * @param throwable 异常信息 + */ + default void loadFailure(Path path, Throwable throwable){} + + /** + * 卸载插件成功 + * @param pluginInfo 插件信息 + */ + default void unLoadSuccess(PluginInfo pluginInfo){} + + /** + * 卸载失败 + * @param pluginInfo 插件信息 + * @param throwable 异常信息 + */ + default void unLoadFailure(PluginInfo pluginInfo, Throwable throwable){} + + /** + * 注册插件成功 + * @param pluginInfo 插件信息 + */ + default void startSuccess(PluginInfo pluginInfo){} + + + /** + * 启动失败 + * @param pluginInfo 插件信息 + * @param throwable 异常信息 + */ + default void startFailure(PluginInfo pluginInfo, Throwable throwable){} + + /** + * 卸载插件成功 + * @param pluginInfo 插件信息 + */ + default void stopSuccess(PluginInfo pluginInfo){} + + + /** + * 停止失败 + * @param pluginInfo 插件信息 + * @param throwable 异常信息 + */ + default void stopFailure(PluginInfo pluginInfo, Throwable throwable){} + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginListenerFactory.java b/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginListenerFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..e5292627f761dd334256216516c9a2b6502378ef --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginListenerFactory.java @@ -0,0 +1,43 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.integration.listener; + +import java.util.List; + +/** + * 插件监听工厂 + * + * @author starBlues + * @version 3.0.0 + */ +public interface PluginListenerFactory extends PluginListener { + + /** + * 添加监听者 + * + * @param pluginListener 插件监听者 + */ + void addPluginListener(PluginListener pluginListener); + + /** + * 得到监听者 + * + * @return 监听者集合 + */ + List getListeners(); + +} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/listener/SwaggerListeningListener.java b/spring-brick/src/main/java/com/gitee/starblues/integration/listener/SwaggerListener.java similarity index 56% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/listener/SwaggerListeningListener.java rename to spring-brick/src/main/java/com/gitee/starblues/integration/listener/SwaggerListener.java index 41ebd26c475b59bebae68ea2c1b712e25a5d32ac..a8b40099c9b34228ddea661e97e8b6cf739db612 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/listener/SwaggerListeningListener.java +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/listener/SwaggerListener.java @@ -1,5 +1,22 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.gitee.starblues.integration.listener; +import com.gitee.starblues.core.PluginInfo; import com.gitee.starblues.utils.SpringBeanUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -9,41 +26,31 @@ import springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapp /** * Swagger 监听事件 * @author starBlues - * @version 2.4.0 + * @version 3.0.0 */ -public class SwaggerListeningListener implements PluginListener{ +public class SwaggerListener implements PluginListener{ private final Logger log = LoggerFactory.getLogger(this.getClass()); private final ApplicationContext mainApplicationContext; - public SwaggerListeningListener(ApplicationContext mainApplicationContext) { + public SwaggerListener(ApplicationContext mainApplicationContext) { this.mainApplicationContext = mainApplicationContext; } @Override - public void registry(String pluginId, boolean isInitialize) { - if(isInitialize){ + public void startSuccess(PluginInfo pluginInfo) { + if(pluginInfo.isFollowSystem()){ return; } refresh(); } @Override - public void unRegistry(String pluginId) { + public void stopSuccess(PluginInfo pluginInfo) { refresh(); } - @Override - public void registryFailure(String pluginId, Throwable throwable) { - - } - - @Override - public void unRegistryFailure(String pluginId, Throwable throwable) { - - } - - private void refresh(){ + void refresh(){ try { DocumentationPluginsBootstrapper documentationPluginsBootstrapper = SpringBeanUtils.getExistBean(mainApplicationContext, DocumentationPluginsBootstrapper.class); @@ -59,5 +66,4 @@ public class SwaggerListeningListener implements PluginListener{ } } - } diff --git a/spring-brick/src/main/java/com/gitee/starblues/integration/operator/DefaultPluginOperator.java b/spring-brick/src/main/java/com/gitee/starblues/integration/operator/DefaultPluginOperator.java new file mode 100644 index 0000000000000000000000000000000000000000..0a8c6b12c2e1223c89797a6c29f1e570129f4952 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/operator/DefaultPluginOperator.java @@ -0,0 +1,411 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.integration.operator; + +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.core.PluginLauncherManager; +import com.gitee.starblues.core.PluginManager; +import com.gitee.starblues.core.RealizeProvider; +import com.gitee.starblues.core.exception.PluginDisabledException; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.integration.listener.PluginInitializerListener; +import com.gitee.starblues.integration.listener.PluginInitializerListenerFactory; +import com.gitee.starblues.integration.operator.upload.UploadByInputStreamParam; +import com.gitee.starblues.integration.operator.upload.UploadByMultipartFileParam; +import com.gitee.starblues.integration.operator.upload.UploadParam; +import com.gitee.starblues.spring.web.PluginStaticResourceConfig; +import com.gitee.starblues.utils.*; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * 默认的插件操作者 + * @author starBlues + * @version 3.0.0 + */ +public class DefaultPluginOperator implements PluginOperator { + protected final Logger log = LoggerFactory.getLogger(this.getClass()); + + private final static DateTimeFormatter FORMAT = DateTimeFormatter.ofPattern("yyyyMMddHHmmss"); + + private final AtomicBoolean isInit = new AtomicBoolean(false); + + private final GenericApplicationContext applicationContext; + private final IntegrationConfiguration configuration; + + private final PluginManager pluginManager; + private final PluginInitializerListenerFactory pluginInitializerListenerFactory; + + public DefaultPluginOperator(GenericApplicationContext applicationContext, + RealizeProvider realizeProvider, + IntegrationConfiguration configuration) { + this.applicationContext = applicationContext; + this.configuration = configuration; + this.pluginManager = new PluginLauncherManager(realizeProvider, applicationContext, configuration); + this.pluginInitializerListenerFactory = new PluginInitializerListenerFactory(applicationContext); + } + + @Override + public synchronized boolean initPlugins(PluginInitializerListener pluginInitializerListener) throws PluginException { + if(isInit.get()){ + throw new RuntimeException("插件已经被初始化了, 不能再初始化."); + } + try { + log.info("插件加载环境: {}", configuration.environment().toString()); + pluginInitializerListenerFactory.addListener(pluginInitializerListener); + List pluginsRoots = pluginManager.getPluginsRoots(); + if(pluginsRoots.isEmpty()){ + return true; + } + initBeforeLogPrint(); + // 触发插件初始化监听器 + pluginInitializerListenerFactory.before(); + if(!configuration.enable()){ + log.info("插件功能已被禁用!"); + // 如果禁用的话, 直接返回 + pluginInitializerListenerFactory.complete(); + return false; + } + // 开始加载插件 + List pluginInfos = pluginManager.loadPlugins(); + if(ObjectUtils.isEmpty(pluginInfos)){ + return false; + } + boolean isFoundException = false; + for (PluginInfo pluginInfo : pluginInfos) { + try { + pluginManager.start(pluginInfo.getPluginId()); + } catch (Exception e){ + if(e instanceof PluginDisabledException){ + log.info(e.getMessage()); + continue; + } + log.error(e.getMessage(), e); + isFoundException = true; + } + } + isInit.set(true); + if(isFoundException){ + log.error("插件初始化失败"); + pluginInitializerListenerFactory.failure(new PluginException("插件初始化存在异常")); + return false; + } else { + pluginInitializerListenerFactory.complete(); + log.info("插件初始化完成"); + return true; + } + } catch (Exception e){ + pluginInitializerListenerFactory.failure(e); + throw e; + } + } + + /** + * 初始化之前日志打印 + */ + private void initBeforeLogPrint() { + List pluginsRoots = pluginManager.getPluginsRoots(); + log.info("开始加载插件, 插件根路径为: \n{}", String.join("\n", pluginsRoots)); + PluginStaticResourceConfig resourceConfig = SpringBeanUtils.getExistBean(applicationContext, + PluginStaticResourceConfig.class); + if(resourceConfig != null){ + resourceConfig.logPathPrefix(); + } + } + + @Override + public boolean verify(Path jarPath) throws PluginException { + return pluginManager.verify(jarPath); + } + + @Override + public PluginInfo parse(Path pluginPath) throws PluginException { + return pluginManager.parse(pluginPath); + } + + @Override + public PluginInfo install(Path jarPath, boolean unpackPlugin) throws PluginException { + return pluginManager.install(jarPath, unpackPlugin); + } + + @Override + public void uninstall(String pluginId, boolean isDelete, boolean isBackup) throws PluginException { + uninstallBackup(pluginId, isDelete, isBackup); + } + + @Override + public PluginInfo load(Path pluginPath, boolean unpackPlugin) throws PluginException { + return pluginManager.load(pluginPath, unpackPlugin); + } + + @Override + public boolean unload(String pluginId) throws PluginException { + pluginManager.unLoad(pluginId); + return true; + } + + @Override + public boolean start(String pluginId) throws PluginException { + return pluginManager.start(pluginId) != null; + } + + @Override + public boolean stop(String pluginId) throws PluginException { + PluginInfo pluginInfo = pluginManager.getPluginInfo(pluginId); + if(pluginInfo == null){ + throw new PluginException("没有发现插件: " + pluginId); + } + return pluginManager.stop(pluginId) != null; + } + + @Override + public PluginInfo uploadPlugin(UploadParam uploadParam) throws PluginException { + Assert.isNotNull(uploadParam, "参数 uploadParam 不能为空"); + try { + if(uploadParam instanceof UploadByInputStreamParam){ + UploadByInputStreamParam param = (UploadByInputStreamParam) uploadParam; + return uploadPlugin(param.getPluginFileName(), param.getInputStream(), + param.isBackOldPlugin(), param.isUnpackPlugin()); + } else if(uploadParam instanceof UploadByMultipartFileParam){ + UploadByMultipartFileParam param = (UploadByMultipartFileParam) uploadParam; + MultipartFile file = param.getPluginMultipartFile(); + return uploadPlugin(file.getOriginalFilename(), file.getInputStream(), + param.isBackOldPlugin(), param.isUnpackPlugin()); + } else { + throw new PluginException("不支持上传参数: " + uploadParam.getClass().getName()); + } + } catch (Exception e) { + throw PluginException.getPluginException(e, ()-> new PluginException(e.getMessage(), e)); + } + } + + @Override + public Path backupPlugin(Path backDirPath, String sign) throws PluginException { + if(configuration.isDev()){ + // 开发环境下不备份 + return backDirPath; + } + Objects.requireNonNull(backDirPath); + return backup(backDirPath, sign, false); + } + + @Override + public Path backupPlugin(String pluginId, String sign) throws PluginException { + if(configuration.isDev()){ + // 开发环境下不备份 + return null; + } + PluginInfo pluginInfo = getPluginInfo(pluginId); + return backupPlugin(Paths.get(pluginInfo.getPluginPath()), sign); + } + + @Override + public List getPluginInfo() { + return pluginManager.getPluginInfos(); + } + + @Override + public PluginInfo getPluginInfo(String pluginId) { + return pluginManager.getPluginInfo(pluginId); + } + + /** + * 卸载插件 + * @param pluginId 插件id + * @param isDelete 是否删除插件 + * @param isBackup 是否备份插件 + * @return 如果备份插件, 则返回备份后的插件路径 + */ + protected Path uninstallBackup(String pluginId, boolean isDelete, boolean isBackup){ + PluginInfo pluginInfo = pluginManager.getPluginInfo(pluginId); + if(pluginInfo == null){ + throw new PluginException(pluginId, "没有发现"); + } + pluginManager.uninstall(pluginId); + if(!isDelete || configuration.isDev()){ + return null; + } + // 删除插件 + Path pluginPath = Paths.get(pluginInfo.getPluginPath()); + Path backupPath = null; + if(isBackup){ + // 将插件文件移到备份文件中 + backupPath = backup(pluginPath, "uninstall", true); + } + return backupPath; + } + + protected PluginInfo uploadPlugin(String pluginFileName, InputStream inputStream, + boolean isBackOldPlugin, boolean isUnpackPluginFile) throws Exception{ + // 获取文件的后缀名 + if(ObjectUtils.isEmpty(pluginFileName)){ + throw new PluginException("上传的插件文件名称不能为空"); + } + //检查文件格式是否合法 + if(!ResourceUtils.isJar(pluginFileName) && !ResourceUtils.isZip(pluginFileName)){ + throw new PluginException("插件文件类型错误, 请上传 jar/zip 类型文件"); + } + + String tempPathString = FilesUtils.joiningFilePath(configuration.uploadTempPath(), pluginFileName); + Path tempFilePath = Paths.get(tempPathString); + File tempFile = PluginFileUtils.createExistFile(tempFilePath); + // 将上传的插件拷贝到临时目录 + try (FileOutputStream outputStream = new FileOutputStream(tempFile)){ + IOUtils.copy(inputStream, outputStream); + } + try { + // 解析该插件包 + PluginInfo uploadPluginInfo = parse(tempFilePath); + if(uploadPluginInfo == null){ + Exception exception = new Exception(pluginFileName + " 文件校验失败"); + verifyFailureDelete(tempFilePath, exception); + throw exception; + } + PluginInfo oldPluginInfo = getPluginInfo(uploadPluginInfo.getPluginId()); + PluginInfo pluginInfo = null; + if(oldPluginInfo != null){ + // 判断版本 + Path oldPluginPath = Paths.get(oldPluginInfo.getPluginPath()); + if(isBackOldPlugin){ + // 先备份一次旧插件 + backupPlugin(oldPluginPath, "upload"); + } + // 然后进入更新模式 + pluginInfo = pluginManager.upgrade(tempFilePath, isUnpackPluginFile); + } else { + // 不存在则进入安装插件模式 + pluginInfo = pluginManager.install(tempFilePath, isUnpackPluginFile); + } + return pluginInfo; + } catch (Exception e){ + // 出现异常, 删除刚才上传的临时文件 + verifyFailureDelete(tempFilePath, e); + throw e; + } finally { + IOUtils.closeQuietly(inputStream); + // 删除临时文件 + tempFile.deleteOnExit(); + } + } + + /** + * 备份 + * @param sourcePath 源文件的路径 + * @param sign 文件标志 + * @param deletedSourceFile 是否删除源文件 + * @return 返回备份的插件路径 + */ + protected Path backup(Path sourcePath, String sign, boolean deletedSourceFile) { + try { + if(configuration.isDev()){ + // 如果是开发环境, 则不进行备份 + return null; + } + if(sourcePath == null){ + return null; + } + if(!Files.exists(sourcePath)){ + log.error("Path '{}' does not exist", sourcePath.toString()); + return null; + } + File sourceFile = sourcePath.toFile(); + touchBackupPath(); + String targetPathStr = configuration.backupPath() + File.separator; + if(!ObjectUtils.isEmpty(sign)){ + targetPathStr = targetPathStr + sign; + } + targetPathStr = targetPathStr + "_" + getNowTimeByFormat() + "_" +sourceFile.getName(); + Path targetPath = Paths.get(targetPathStr); + File targetFile = targetPath.toFile(); + copyFile(sourceFile, targetFile); + log.info("备份插件文件到: {}", targetFile.getAbsolutePath()); + if(deletedSourceFile){ + if(sourceFile.isFile()){ + FileUtils.delete(sourceFile); + } else { + FileUtils.deleteDirectory(sourceFile); + } + } + return targetPath; + } catch (IOException e) { + log.error("Backup plugin jar '{}' failure. {}", sourcePath.toString(), e.getMessage(), e); + return null; + } + } + + private void copyFile(File sourceFile, File targetFile) throws IOException { + if(sourceFile.isDirectory()){ + FileUtils.copyDirectory(sourceFile, targetFile); + } else if(sourceFile.isFile()){ + FileUtils.copyFile(sourceFile, targetFile); + } + } + + + /** + * 校验文件失败后, 删除临时文件 + * @param tempPluginFile 临时文件路径 + * @param e 异常信息 + * @throws Exception Exception + */ + protected void verifyFailureDelete(Path tempPluginFile, Exception e) throws Exception { + try { + Files.deleteIfExists(tempPluginFile); + }catch (IOException e1){ + log.error("删除临时文件失败: {}. {}", tempPluginFile, e.getMessage()); + } + } + + /** + * 获取现在的时间 + * @return String + */ + protected String getNowTimeByFormat(){ + LocalDateTime localDateTime = LocalDateTime.now(); + return FORMAT.format(localDateTime); + } + + + protected void touchBackupPath() throws IOException { + String backupPath = configuration.backupPath(); + final File file = new File(backupPath); + if(file.exists()){ + return; + } + FileUtils.forceMkdir(file); + } + + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/integration/operator/PluginOperator.java b/spring-brick/src/main/java/com/gitee/starblues/integration/operator/PluginOperator.java new file mode 100644 index 0000000000000000000000000000000000000000..c16758bbd0be56f9d4f81f9fc46450a2fe58da50 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/operator/PluginOperator.java @@ -0,0 +1,167 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.integration.operator; + +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.integration.listener.PluginInitializerListener; +import com.gitee.starblues.integration.operator.upload.UploadByInputStreamParam; +import com.gitee.starblues.integration.operator.upload.UploadByMultipartFileParam; +import com.gitee.starblues.integration.operator.upload.UploadParam; + +import java.nio.file.Path; +import java.util.List; + +/** + * 操作插件的接口 + * @author starBlues + * @version 3.0.0 + * @see DefaultPluginOperator + */ +public interface PluginOperator { + + /** + * 初始化插件 [适用于 dev、prod 环境] + * 该方法只能执行一次。因程序启动时已经调用了该方法,在使用时不要再调用该方法。 + * @param pluginInitializerListener 插件初始化监听者 + * @return true初始化成功, false初始化失败 + * @throws PluginException 抛出异常说明初始化失败 + */ + boolean initPlugins(PluginInitializerListener pluginInitializerListener) throws PluginException; + + /** + * 校验插件jar包 [适用于 dev、prod 环境] + * @param pluginPath 插件包绝对路径 + * @return true校验成功, false校验失败 + * @throws PluginException 抛出异常说明校验插件包失败 + */ + boolean verify(Path pluginPath) throws PluginException; + + /** + * 解析插件包 [适用于 dev、prod 环境] + * @param pluginPath 插件包路基 + * @return 解析的插件信息 + * @throws PluginException 抛出异常说明解析插件包失败 + */ + PluginInfo parse(Path pluginPath) throws PluginException; + + /** + * 通过路径安装启动插件 [适用于 dev、prod 环境] + * @param pluginPath 插件路径 + * @param unpackPlugin 是否解压插件包. (如果插件包为压缩包时生效) + * @return 成功: 返回插件信息PluginInfo; 失败: 抛出异常或者返回null + * @throws PluginException 异常信息 + */ + PluginInfo install(Path pluginPath, boolean unpackPlugin) throws PluginException; + + /** + * 卸载插件 [适用于 dev、prod 环境] + * @param pluginId 插件id + * @param isDelete 卸载后是否删除插件文件. [适用于 prod 环境] + * @param isBackup 删除插件文件前, 是否备份插件文件。备份文件命名规则为;[uninstall][时间]_原jar名.jar [适用于 prod 环境] + * @throws PluginException 异常信息 + */ + void uninstall(String pluginId, boolean isDelete, boolean isBackup) throws PluginException; + + /** + * 加载插件, 但不启动 [适用于 dev、prod 环境] + * @param pluginPath 插件路径 + * @param unpackPlugin 是否解压插件包. (如果插件包为压缩包时生效) + * @return 成功: 返回插件信息PluginInfo; 失败: 抛出异常或者返回null + * @throws PluginException 异常信息 + */ + PluginInfo load(Path pluginPath, boolean unpackPlugin) throws PluginException; + + /** + * 配合load使用. 针对load的插件进行unload [适用于 dev、prod 环境] + * @param pluginId 插件id + * @return 成功返回true.不成功抛出异常或者返回false + * @throws PluginException 异常信息 + */ + boolean unload(String pluginId) throws PluginException; + + /** + * 启用插件 [适用于 dev、prod 环境] + * @param pluginId 插件id + * @return 成功返回true.不成功抛出异常或者返回false + * @throws PluginException 异常信息 + */ + boolean start(String pluginId) throws PluginException; + + + /** + * 停止插件 [适用于 dev、prod 环境] + * @param pluginId 插件id + * @return 成功: 返回true; 失败: 抛出异常或者返回false + * @throws PluginException 异常信息 + */ + boolean stop(String pluginId) throws PluginException; + + /** + * 上传插件. [适用于 dev、prod 环境] + * dev模式: + * 如果不存在相同插件(插件id不相同), 则正常上传到临时目录, 然后加载、启动。 + * 如果存在相同插件(插件id相同) + * 相同插件在启动状态, 则进入更新模式(满足上传的插件包版本必须大于已启动的插件版本), 系统会自动卸载旧版本, 然后对临时目录中的插件进行安装、启动。 + * 相同插件不在启动状态, 则直接对临时目录中的插件进行安装、启动。 + * prod模式: + * 如果不存在相同插件(插件id不相同), 则正常上传到插件目录, 然后加载、启动。 + * 如果存在相同插件(插件id相同) + * 相同插件在启动状态, 则进入更新模式(满足上传的插件包版本必须大于已启动的插件版本), 系统会自动卸载旧版本, 安装新版本。 + * 相同插件不在启动状态, 则进入覆盖模式, 会对旧插件进行备份(可根据isBackOldPlugin配置不备份), 然后上传新插件包到插件目录, 然后安装、启动。 + * 如果在插件根目录存在同文件名称插件, 系统会抛出异常, 建议重命名插件名称, 再上传。 + * + * @param uploadParam 上传参数 + * @return 成功: 返回插件信息PluginInfo; 失败: 抛出异常或者返回null + * @throws PluginException 异常信息 + * @see UploadByInputStreamParam + * @see UploadByMultipartFileParam + */ + PluginInfo uploadPlugin(UploadParam uploadParam) throws PluginException; + + /** + * 通过路径备份插件文件。[适用于 prod 环境] + * @param backDirPath 备份的目录路径 + * @param sign 备份文件的自定义标识 + * @return 备份插件的路径 + * @throws PluginException 异常信息 + */ + Path backupPlugin(Path backDirPath, String sign) throws PluginException; + + /** + * 通过插件id备份插件。 [适用于 prod 环境] + * @param pluginId 插件id + * @param sign 备份文件的自定义标识 + * @return 备份插件的路径 + * @throws PluginException 异常信息 + */ + Path backupPlugin(String pluginId, String sign) throws PluginException; + + /** + * 获取插件信息 [适用于 dev、prod 环境] + * @return 返回插件信息列表 + */ + List getPluginInfo(); + + /** + * 根据插件id获取插件信息 [适用于 dev、prod 环境] + * @param pluginId 插件id + * @return 插件信息 + */ + PluginInfo getPluginInfo(String pluginId); + +} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/PluginOperatorWrapper.java b/spring-brick/src/main/java/com/gitee/starblues/integration/operator/PluginOperatorWrapper.java similarity index 38% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/PluginOperatorWrapper.java rename to spring-brick/src/main/java/com/gitee/starblues/integration/operator/PluginOperatorWrapper.java index 14b43dcc3048cce9981baabae3630007d35c429f..aaf90076494cbb1ff23b230e1d2b3a15b1f5099a 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/PluginOperatorWrapper.java +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/operator/PluginOperatorWrapper.java @@ -1,48 +1,61 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.gitee.starblues.integration.operator; -import com.gitee.starblues.factory.process.pipe.PluginInfoContainers; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.core.PluginInfo; import com.gitee.starblues.integration.IntegrationConfiguration; import com.gitee.starblues.integration.listener.PluginInitializerListener; -import com.gitee.starblues.integration.operator.module.PluginInfo; -import com.gitee.starblues.realize.UnRegistryValidator; -import com.gitee.starblues.utils.SpringBeanUtils; -import org.pf4j.PluginWrapper; -import org.pf4j.util.StringUtils; +import com.gitee.starblues.integration.operator.upload.UploadParam; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.web.multipart.MultipartFile; import java.nio.file.Path; import java.util.Collections; import java.util.List; -import java.util.Set; /** * 插件操作包装者 * @author starBlues - * @version 2.4.4 + * @version 3.0.0 */ public class PluginOperatorWrapper implements PluginOperator{ protected final Logger log = LoggerFactory.getLogger(this.getClass()); private final PluginOperator pluginOperator; - private final IntegrationConfiguration integrationConfiguration; + private final IntegrationConfiguration configuration; public PluginOperatorWrapper(PluginOperator pluginOperator, - IntegrationConfiguration integrationConfiguration) { + IntegrationConfiguration configuration) { this.pluginOperator = pluginOperator; - this.integrationConfiguration = integrationConfiguration; + this.configuration = configuration; } @Override - public boolean initPlugins(PluginInitializerListener pluginInitializerListener) throws Exception { + public boolean initPlugins(PluginInitializerListener pluginInitializerListener) throws PluginException { + if(isDisable()){ + return false; + } return pluginOperator.initPlugins(pluginInitializerListener); } @Override - public boolean verify(Path jarPath) throws Exception { + public boolean verify(Path jarPath) throws PluginException { if(isDisable()){ return false; } @@ -50,48 +63,47 @@ public class PluginOperatorWrapper implements PluginOperator{ } @Override - public PluginInfo install(Path jarPath) throws Exception { + public PluginInfo parse(Path pluginPath) throws PluginException { if(isDisable()){ return null; } - return pluginOperator.install(jarPath); + return pluginOperator.parse(pluginPath); } @Override - public PluginInfo load(Path jarPath) throws Exception { + public PluginInfo install(Path jarPath, boolean unpackPlugin) throws PluginException { if(isDisable()){ return null; } - return pluginOperator.install(jarPath); + return pluginOperator.install(jarPath, unpackPlugin); } @Override - public PluginInfo load(MultipartFile pluginFile) throws Exception { + public boolean unload(String pluginId) throws PluginException { if(isDisable()){ - return null; + return false; } - return pluginOperator.load(pluginFile); + return pluginOperator.unload(pluginId); } @Override - public boolean unload(String pluginId, boolean isBackup) throws Exception { + public void uninstall(String pluginId, boolean isDelete, boolean isBackup) throws PluginException { if(isDisable()){ - return false; + return; } - return pluginOperator.unload(pluginId, isBackup); + pluginOperator.uninstall(pluginId, isDelete, isBackup); } @Override - public boolean uninstall(String pluginId, boolean isBackup) throws Exception { + public PluginInfo load(Path pluginPath, boolean unpackPlugin) throws PluginException { if(isDisable()){ - return false; + return null; } - checkIsUnRegistry(pluginId); - return pluginOperator.uninstall(pluginId, isBackup); + return pluginOperator.install(pluginPath, unpackPlugin); } @Override - public boolean start(String pluginId) throws Exception { + public boolean start(String pluginId) throws PluginException { if(isDisable()){ return false; } @@ -99,50 +111,33 @@ public class PluginOperatorWrapper implements PluginOperator{ } @Override - public boolean stop(String pluginId) throws Exception { + public boolean stop(String pluginId) throws PluginException { if(isDisable()){ return false; } - checkIsUnRegistry(pluginId); return pluginOperator.stop(pluginId); } @Override - public PluginInfo uploadPluginAndStart(MultipartFile pluginFile) throws Exception { + public PluginInfo uploadPlugin(UploadParam uploadParam) throws PluginException { if(isDisable()){ return null; } - return pluginOperator.uploadPluginAndStart(pluginFile); + return pluginOperator.uploadPlugin(uploadParam); } @Override - public boolean installConfigFile(Path configFilePath) throws Exception { + public Path backupPlugin(Path backDirPath, String sign) throws PluginException { if(isDisable()){ - return false; - } - return pluginOperator.installConfigFile(configFilePath); - } - - @Override - public boolean uploadConfigFile(MultipartFile configFile) throws Exception { - if(isDisable()){ - return false; - } - return pluginOperator.uploadConfigFile(configFile); - } - - @Override - public boolean backupPlugin(Path backDirPath, String sign) throws Exception { - if(isDisable()){ - return false; + return null; } return pluginOperator.backupPlugin(backDirPath, sign); } @Override - public boolean backupPlugin(String pluginId, String sign) throws Exception { + public Path backupPlugin(String pluginId, String sign) throws PluginException { if(isDisable()){ - return false; + return null; } return pluginOperator.backupPlugin(pluginId, sign); } @@ -163,68 +158,17 @@ public class PluginOperatorWrapper implements PluginOperator{ return pluginOperator.getPluginInfo(pluginId); } - @Override - public Set getPluginFilePaths() throws Exception { - if(isDisable()){ - return Collections.emptySet(); - } - return pluginOperator.getPluginFilePaths(); - } - - @Override - public List getPluginWrapper() { - if(isDisable()){ - return Collections.emptyList(); - } - return pluginOperator.getPluginWrapper(); - } - - @Override - public PluginWrapper getPluginWrapper(String pluginId) { - if(isDisable()){ - return null; - } - return pluginOperator.getPluginWrapper(pluginId); - } - /** * 是否被禁用 * @return true 禁用 */ private boolean isDisable(){ - if(integrationConfiguration.enable()){ + if(configuration.enable()){ return false; } // 如果禁用的话, 直接返回 - log.info("The Plugin module is disabled!"); + log.info("插件功能已被禁用!"); return true; } - /** - * 检查是否可卸载 - * @param pluginId 插件id - * @throws Exception 检查异常 - */ - private void checkIsUnRegistry(String pluginId) throws Exception{ - GenericApplicationContext pluginApplicationContext = PluginInfoContainers.getPluginApplicationContext(pluginId); - if(pluginApplicationContext == null){ - log.error("Plugin '{}' Not found ApplicationContext. So cannot found and execute unRegistryValidator", - pluginId); - return; - } - List unRegistryValidators = SpringBeanUtils.getBeans(pluginApplicationContext, UnRegistryValidator.class); - for (UnRegistryValidator unRegistryValidator : unRegistryValidators) { - UnRegistryValidator.Result result = unRegistryValidator.verify(); - if(result.isVerify()){ - return; - } - String message = result.getMessage(); - if(StringUtils.isNullOrEmpty(message)){ - message = "Plugin [" + pluginId + "] Stop or Uninstall be banned"; - } - throw new Exception(message); - } - } - - } diff --git a/spring-brick/src/main/java/com/gitee/starblues/integration/operator/upload/UploadByInputStreamParam.java b/spring-brick/src/main/java/com/gitee/starblues/integration/operator/upload/UploadByInputStreamParam.java new file mode 100644 index 0000000000000000000000000000000000000000..356356d8739f5e928eb355c3fa6675f077d7fc5c --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/operator/upload/UploadByInputStreamParam.java @@ -0,0 +1,52 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.integration.operator.upload; + +import com.gitee.starblues.utils.Assert; + +import java.io.InputStream; + +/** + * InputStream 上传插件参数 + * @author starBlues + * @version 3.0.0 + */ +public class UploadByInputStreamParam extends UploadParam{ + + /** + * 插件文件名称 + */ + private final String pluginFileName; + + /** + * 插件输入流 + */ + private final InputStream inputStream; + + public UploadByInputStreamParam(String pluginFileName, InputStream inputStream) { + this.pluginFileName = Assert.isNotEmpty(pluginFileName, "参数pluginFileName不能为空"); + this.inputStream = Assert.isNotNull(inputStream, "参数inputStream不能为空"); + } + + public String getPluginFileName() { + return pluginFileName; + } + + public InputStream getInputStream() { + return inputStream; + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/integration/operator/upload/UploadByMultipartFileParam.java b/spring-brick/src/main/java/com/gitee/starblues/integration/operator/upload/UploadByMultipartFileParam.java new file mode 100644 index 0000000000000000000000000000000000000000..b9b7c47aa6a8dfd29214ceeab400d760bd60abde --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/operator/upload/UploadByMultipartFileParam.java @@ -0,0 +1,38 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.integration.operator.upload; + +import com.gitee.starblues.utils.Assert; +import org.springframework.web.multipart.MultipartFile; + +/** + * 上传插件参数 + * @author starBlues + * @version 3.0.0 + */ +public class UploadByMultipartFileParam extends UploadParam{ + + private final MultipartFile pluginMultipartFile; + + public UploadByMultipartFileParam(MultipartFile pluginMultipartFile) { + this.pluginMultipartFile = Assert.isNotNull(pluginMultipartFile, "参数pluginMultipartFile不能为空"); + } + + public MultipartFile getPluginMultipartFile() { + return pluginMultipartFile; + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/integration/operator/upload/UploadParam.java b/spring-brick/src/main/java/com/gitee/starblues/integration/operator/upload/UploadParam.java new file mode 100644 index 0000000000000000000000000000000000000000..0f2b418ce87aeaeb13d381d9b95403d0ae9e0a87 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/operator/upload/UploadParam.java @@ -0,0 +1,98 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.integration.operator.upload; + +import org.springframework.web.multipart.MultipartFile; + +import java.io.InputStream; + +/** + * + * 上传插件参数 + * @author starBlues + * @version 3.0.0 + */ +public abstract class UploadParam { + + /** + * 上传后是否启动插件. 默认启动 + */ + private boolean isStartPlugin = true; + + /** + * 如果存在旧插件, 是否备份旧插件 + */ + private boolean isBackOldPlugin = true; + + /** + * 是否解压插件文件 + */ + private boolean isUnpackPlugin = false; + + protected UploadParam(){} + + public static UploadByInputStreamParam byInputStream(String pluginFileName, InputStream inputStream){ + return new UploadByInputStreamParam(pluginFileName, inputStream); + } + + public static UploadByMultipartFileParam byMultipartFile(MultipartFile pluginMultipartFile){ + return new UploadByMultipartFileParam(pluginMultipartFile); + } + + /** + * 设置上传后是否启动插件. 默认 true + * @param isStartPlugin true: 启动, false 不启动 + * @return UploadParam + */ + public UploadParam setStartPlugin(boolean isStartPlugin) { + this.isStartPlugin = isStartPlugin; + return this; + } + + /** + * 设置是否备份旧插件. 默认: true + * @param isBackOldPlugin true: 备份, false 不备份 + * @return UploadParam + */ + public UploadParam setBackOldPlugin(boolean isBackOldPlugin) { + this.isBackOldPlugin = isBackOldPlugin; + return this; + } + + /** + * 设置是否解压插件. 默认: false + * @param isUnpackPlugin true: 解压, false 不解压 + * @return UploadParam + */ + public UploadParam setUnpackPlugin(boolean isUnpackPlugin) { + this.isUnpackPlugin = isUnpackPlugin; + return this; + } + + public boolean isStartPlugin() { + return isStartPlugin; + } + + public boolean isBackOldPlugin() { + return isBackOldPlugin; + } + + public boolean isUnpackPlugin() { + return isUnpackPlugin; + } + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/integration/user/BeanWrapper.java b/spring-brick/src/main/java/com/gitee/starblues/integration/user/BeanWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..6fb78a4664ec0bb170363eafdf6ee0658e46f6eb --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/user/BeanWrapper.java @@ -0,0 +1,50 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.integration.user; + +import java.util.Map; + +/** + * bean包装类 + * @author starBlues + * @version 3.0.0 + */ +public class BeanWrapper { + + /** + * 主程序bean + */ + private final T mainBean; + + /** + * 插件bean. key为插件id + */ + private final Map pluginBean; + + public BeanWrapper(T mainBean, Map pluginBean) { + this.mainBean = mainBean; + this.pluginBean = pluginBean; + } + + public T getMainBean() { + return mainBean; + } + + public Map getPluginBean() { + return pluginBean; + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/integration/user/DefaultPluginUser.java b/spring-brick/src/main/java/com/gitee/starblues/integration/user/DefaultPluginUser.java new file mode 100644 index 0000000000000000000000000000000000000000..d63c7053293e7ecb19796d31dd123b10b31754af --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/user/DefaultPluginUser.java @@ -0,0 +1,157 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.integration.user; + +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.core.launcher.plugin.involved.PluginApplicationContextGetter; +import com.gitee.starblues.spring.ApplicationContext; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.SpringBeanUtils; +import com.gitee.starblues.utils.SpringBeanCustomUtils; +import org.springframework.context.support.GenericApplicationContext; + +import java.lang.annotation.Annotation; +import java.util.*; + +/** + * 默认插件使用者 + * @author starBlues + * @version 3.0.0 + */ +public class DefaultPluginUser implements PluginUser{ + + protected final GenericApplicationContext parentApplicationContext; + + public DefaultPluginUser(GenericApplicationContext parentApplicationContext) { + Objects.requireNonNull(parentApplicationContext, "ApplicationContext can't be null"); + this.parentApplicationContext = parentApplicationContext; + } + + @Override + public BeanWrapper> getBeanName(boolean includeMainBeans) { + Set mainBeanSet = new HashSet<>(); + if(includeMainBeans){ + mainBeanSet = SpringBeanUtils.getBeanName(parentApplicationContext); + } + Map applicationContexts = PluginApplicationContextGetter.get(); + Map> pluginBeanNames = new HashMap<>(applicationContexts.size()); + applicationContexts.forEach((k,v)->{ + pluginBeanNames.put(k, SpringBeanCustomUtils.getBeanName(v)); + }); + return new BeanWrapper<>(mainBeanSet, pluginBeanNames); + } + + @Override + public Set getBeanName(String pluginId) { + return null; + } + + @Override + public BeanWrapper getBean(String name, boolean includeMainBeans){ + Object mainBean = null; + if(includeMainBeans){ + mainBean = SpringBeanUtils.getExistBean(parentApplicationContext, name); + } + Map applicationContexts = PluginApplicationContextGetter.get(); + Map pluginBeans = new HashMap<>(applicationContexts.size()); + applicationContexts.forEach((k,v)->{ + Object existBean = SpringBeanCustomUtils.getExistBean(v, name); + if(existBean != null){ + pluginBeans.put(k, v); + } + }); + return new BeanWrapper<>(mainBean, pluginBeans); + } + + @Override + public Object getBean(String pluginId, String name) { + ApplicationContext applicationContext = PluginApplicationContextGetter.get(pluginId); + if(applicationContext == null){ + return null; + } + return SpringBeanCustomUtils.getExistBean(applicationContext, name); + } + + @Override + public BeanWrapper> getBeanByInterface(Class interfaceClass, boolean includeMainBeans) { + checkInterface(interfaceClass); + List mainBeans = new ArrayList<>(); + if(includeMainBeans){ + mainBeans = SpringBeanUtils.getBeans(parentApplicationContext, interfaceClass); + } + Map applicationContexts = PluginApplicationContextGetter.get(); + Map> pluginBeans = new HashMap<>(applicationContexts.size()); + applicationContexts.forEach((k,v)->{ + List beans = SpringBeanCustomUtils.getBeans(v, interfaceClass); + if(!ObjectUtils.isEmpty(beans)){ + pluginBeans.put(k, beans); + } + }); + return new BeanWrapper<>(mainBeans, pluginBeans); + } + + @Override + public List getBeanByInterface(String pluginId, Class interfaceClass) { + checkInterface(interfaceClass); + List result = new ArrayList<>(); + ApplicationContext applicationContext = PluginApplicationContextGetter.get(pluginId); + if(applicationContext != null){ + result.addAll(SpringBeanCustomUtils.getBeans(applicationContext, interfaceClass)); + } + return result; + } + + @Override + public BeanWrapper> getBeansWithAnnotation(Class annotationType, + boolean includeMainBeans) { + List mainBeans = new ArrayList<>(); + if(includeMainBeans){ + mainBeans = SpringBeanUtils.getBeansWithAnnotation(parentApplicationContext, annotationType); + } + Map applicationContexts = PluginApplicationContextGetter.get(); + Map> pluginBeans = new HashMap<>(applicationContexts.size()); + applicationContexts.forEach((k,v)->{ + List beans = SpringBeanCustomUtils.getBeansWithAnnotation(v, annotationType); + if(!ObjectUtils.isEmpty(beans)){ + pluginBeans.put(k, beans); + } + }); + return new BeanWrapper<>(mainBeans, pluginBeans); + } + + + @Override + public List getBeansWithAnnotation(String pluginId, Class annotationType) { + ApplicationContext applicationContext = PluginApplicationContextGetter.get(pluginId); + if(applicationContext != null){ + return SpringBeanCustomUtils.getBeansWithAnnotation(applicationContext, annotationType); + } + return new ArrayList<>(0); + } + + /** + * 判断clazz是否是接口 + * @param clazz clazz + */ + private void checkInterface(Class clazz) { + if (clazz.isInterface()) { + return; + } + throw new PluginException("[" + clazz.getName() + "]不是一个接口"); + } + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/integration/user/PluginUser.java b/spring-brick/src/main/java/com/gitee/starblues/integration/user/PluginUser.java new file mode 100644 index 0000000000000000000000000000000000000000..1864e8a8feca6c9e9372c4db926bd1e1b5935c6c --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/integration/user/PluginUser.java @@ -0,0 +1,98 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.integration.user; + +import java.lang.annotation.Annotation; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 该接口用于在主程序操作获取主程序/插件中 Spring 管理 Bean + * @author starBlues + * @version 3.0.0 + */ +public interface PluginUser { + + /** + * 获取 Bean名称 + * @param includeMainBeans 是否包含主程序 Bean + * @return Bean 包装对象 + */ + BeanWrapper> getBeanName(boolean includeMainBeans); + + /** + * 获取插件中的Bean名称 + * @param pluginId 插件id + * @return Bean名称集合 + */ + Set getBeanName(String pluginId); + + + /** + * 通过 Bean名称获取 Bean 对象。 + * @param name Bean的名称。 + * @param includeMainBeans 是否包含主程序 Bean + * @return Bean包装对象 + */ + BeanWrapper getBean(String name, boolean includeMainBeans); + + /** + * 通过 Bean名称获取具体插件中的 Bean 对象 + * @param pluginId 插件id。 + * @param name Bean名称 + * @return Object + */ + Object getBean(String pluginId, String name); + + + /** + * 通过接口获取实现的对象集合 + * @param interfaceClass 接口的类 + * @param includeMainBeans 是否包含主程序 Bean + * @param Bean的类型 + * @return Bean包装对象 + */ + BeanWrapper> getBeanByInterface(Class interfaceClass, boolean includeMainBeans); + + /** + * 通过接口获取具体插件中的实现对象集合 + * @param pluginId 插件id + * @param interfaceClass 接口的类 + * @param Bean的类型 + * @return List + */ + List getBeanByInterface(String pluginId, Class interfaceClass); + + /** + * 通过注解获取 Bean + * @param annotationType 注解类型 + * @param includeMainBeans 是否包含主程序 Bean + * @return Bean包装对象 + */ + BeanWrapper> getBeansWithAnnotation(Class annotationType, boolean includeMainBeans); + + /** + * 通过注解获取具体插件中的 Bean + * @param pluginId 插件id + * @param annotationType 注解类型 + * @return 该注解的 Bean 集合 + */ + List getBeansWithAnnotation(String pluginId, Class annotationType); + + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/spring/ApplicationContext.java b/spring-brick/src/main/java/com/gitee/starblues/spring/ApplicationContext.java new file mode 100644 index 0000000000000000000000000000000000000000..905623503ab6497ca1ae96710ca323744f3b6b30 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/ApplicationContext.java @@ -0,0 +1,32 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring; + +/** + * 自定义ApplicationContext + * @author starBlues + * @version 3.0.0 + */ +public interface ApplicationContext extends AutoCloseable { + + /** + * 得到 SpringBeanFactory + * @return SpringBeanFactory + */ + SpringBeanFactory getSpringBeanFactory(); + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/spring/ApplicationContextProxy.java b/spring-brick/src/main/java/com/gitee/starblues/spring/ApplicationContextProxy.java new file mode 100644 index 0000000000000000000000000000000000000000..706c7a957f1d2e3f019918905dc88652e5901b02 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/ApplicationContextProxy.java @@ -0,0 +1,41 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring; + +/** + * ApplicationContext 代理 + * @author starBlues + * @version 3.0.0 + */ +public class ApplicationContextProxy extends GenericApplicationContext{ + + public ApplicationContextProxy(Object targetBeanFactory, + AutoCloseable autoCloseable) { + super(autoCloseable); + setSpringBeanFactory(createSpringBeanFactory(targetBeanFactory)); + } + + public ApplicationContextProxy(Object targetBeanFactory) { + super(); + setSpringBeanFactory(createSpringBeanFactory(targetBeanFactory)); + } + + protected SpringBeanFactory createSpringBeanFactory(Object targetBeanFactory){ + ProxyFactory proxyFactory = new CacheJdkSameTypeParamProxyFactory(targetBeanFactory); + return proxyFactory.getObject(SpringBeanFactory.class); + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/spring/CacheJdkSameTypeParamProxyFactory.java b/spring-brick/src/main/java/com/gitee/starblues/spring/CacheJdkSameTypeParamProxyFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..5c3f7f76091a3e4e48a59b5a1b7c056540a6d3c8 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/CacheJdkSameTypeParamProxyFactory.java @@ -0,0 +1,59 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring; + +import com.gitee.starblues.utils.ReflectionUtils; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 可缓存的代理工厂 + * @author starBlues + * @version 3.0.0 + */ +public class CacheJdkSameTypeParamProxyFactory extends JdkSameTypeParamProxyFactory{ + + private final Map methodCache = new ConcurrentHashMap<>(); + + public CacheJdkSameTypeParamProxyFactory(Object target) { + super(target); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + Method targetMethod = methodCache.get(method); + if(targetMethod == null){ + Class[] paramTypes = null; + if(args != null){ + paramTypes = new Class[args.length]; + for (int i = 0; i < args.length; i++) { + paramTypes[i] = args[i].getClass(); + } + } + targetMethod = ReflectionUtils.findMethod(target.getClass(), method.getName(), paramTypes); + if(targetMethod != null){ + methodCache.put(method, targetMethod); + } else { + throw ReflectionUtils.getNoSuchMethodException(target.getClass(), method.getName(), paramTypes); + } + } + return targetMethod.invoke(target, args); + } + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/spring/GenericApplicationContext.java b/spring-brick/src/main/java/com/gitee/starblues/spring/GenericApplicationContext.java new file mode 100644 index 0000000000000000000000000000000000000000..922899c5d4c43149b232af914896624656542b10 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/GenericApplicationContext.java @@ -0,0 +1,55 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring; + +import com.gitee.starblues.utils.Assert; + +/** + * 基本的ApplicationContext + * @author starBlues + * @version 3.0.0 + */ +public class GenericApplicationContext implements ApplicationContext{ + + public final AutoCloseable autoCloseable; + + protected SpringBeanFactory springBeanFactory; + + public GenericApplicationContext() { + this(null); + } + + public GenericApplicationContext(AutoCloseable autoCloseable) { + this.autoCloseable = autoCloseable; + } + + public void setSpringBeanFactory(SpringBeanFactory springBeanFactory){ + this.springBeanFactory = Assert.isNotNull(springBeanFactory, "参数 springBeanFactory 不能为空"); + } + + @Override + public SpringBeanFactory getSpringBeanFactory() { + return springBeanFactory; + } + + @Override + public void close() throws Exception { + if(autoCloseable != null){ + autoCloseable.close(); + } + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/spring/JdkSameTypeParamProxyFactory.java b/spring-brick/src/main/java/com/gitee/starblues/spring/JdkSameTypeParamProxyFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..2e9cfd0a2883e0729429b49e7b535eae6d6c26f6 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/JdkSameTypeParamProxyFactory.java @@ -0,0 +1,51 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring; + +import com.gitee.starblues.utils.ReflectionUtils; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * jdk 同类型参数的代理工厂 + * @author starBlues + * @version 3.0.0 + */ +public class JdkSameTypeParamProxyFactory implements ProxyFactory, InvocationHandler { + + protected final Object target; + + public JdkSameTypeParamProxyFactory(Object target) { + this.target = target; + } + + @SuppressWarnings("unchecked") + @Override + public T getObject(Class interfacesClass) { + ClassLoader classLoader = interfacesClass.getClassLoader(); + Class[] interfaces = new Class[]{ interfacesClass }; + return (T) Proxy.newProxyInstance(classLoader, interfaces, this); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return ReflectionUtils.invoke(target, method.getName(), args); + } + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/spring/MainApplicationContext.java b/spring-brick/src/main/java/com/gitee/starblues/spring/MainApplicationContext.java new file mode 100644 index 0000000000000000000000000000000000000000..070e04a6b4f0d567069f32925f772b48cad0eb1e --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/MainApplicationContext.java @@ -0,0 +1,35 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring; + +import java.util.Map; + +/** + * 主程序 ApplicationContext 接口 + * @author starBlues + * @version 3.0.0 + */ +public interface MainApplicationContext extends ApplicationContext { + + /** + * 得到主程序所有配置的 env + * + * @return 主程序配置的 env 集合 + */ + Map> getConfigurableEnvironment(); + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/spring/MainApplicationContextProxy.java b/spring-brick/src/main/java/com/gitee/starblues/spring/MainApplicationContextProxy.java new file mode 100644 index 0000000000000000000000000000000000000000..a430ae8a831491cd595ffc4b39d603bfee909f97 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/MainApplicationContextProxy.java @@ -0,0 +1,70 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring; + +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertySource; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * 主程序 ApplicationContext 的实现 + * @author starBlues + * @version 3.0.0 + */ +public class MainApplicationContextProxy extends ApplicationContextProxy implements MainApplicationContext{ + + private final GenericApplicationContext applicationContext; + + public MainApplicationContextProxy(GenericApplicationContext applicationContext) { + super(applicationContext.getBeanFactory()); + this.applicationContext = applicationContext; + } + + public MainApplicationContextProxy(GenericApplicationContext applicationContext, AutoCloseable autoCloseable) { + super(applicationContext.getBeanFactory(), autoCloseable); + this.applicationContext = applicationContext; + } + + @Override + public Map> getConfigurableEnvironment() { + ConfigurableEnvironment environment = applicationContext.getEnvironment(); + MutablePropertySources propertySources = environment.getPropertySources(); + Map> environmentMap = new LinkedHashMap<>(propertySources.size()); + for (PropertySource propertySource : propertySources) { + if (!(propertySource instanceof EnumerablePropertySource)) { + continue; + } + EnumerablePropertySource enumerablePropertySource = (EnumerablePropertySource) propertySource; + String[] propertyNames = enumerablePropertySource.getPropertyNames(); + Map values = new HashMap<>(propertyNames.length); + for (String propertyName : propertyNames) { + values.put(propertyName, enumerablePropertySource.getProperty(propertyName)); + } + if (!values.isEmpty()) { + environmentMap.put(propertySource.getName(), values); + } + } + return environmentMap; + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/spring/ProxyFactory.java b/spring-brick/src/main/java/com/gitee/starblues/spring/ProxyFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..9ff2087a7692412a1f49d82f6462ed8a9503f303 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/ProxyFactory.java @@ -0,0 +1,34 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring; + +/** + * 代理工厂 + * @author starBlues + * @version 3.0.0 + */ +public interface ProxyFactory { + + /** + * 获取代理类 + * @param interfacesClass 需代理的接口 + * @param 代理实现的泛型 + * @return 代理实现 + */ + T getObject(Class interfacesClass); + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/spring/SpringBeanFactory.java b/spring-brick/src/main/java/com/gitee/starblues/spring/SpringBeanFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..8830eed81e85d88f54c787733e25aa814f7a1a8c --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/SpringBeanFactory.java @@ -0,0 +1,27 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring; + +import org.springframework.beans.factory.ListableBeanFactory; + +/** + * spring bean factory 封装接口 + * @author starBlues + * @version 3.0.0 + */ +public interface SpringBeanFactory extends ListableBeanFactory { +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/spring/SpringPluginHook.java b/spring-brick/src/main/java/com/gitee/starblues/spring/SpringPluginHook.java new file mode 100644 index 0000000000000000000000000000000000000000..55cdc24cf133ee55f0f4fc3dfe5b96c066177b86 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/SpringPluginHook.java @@ -0,0 +1,54 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring; + + +import com.gitee.starblues.core.exception.PluginProhibitStopException; +import com.gitee.starblues.spring.web.thymeleaf.ThymeleafConfig; + +/** + * 插件把柄接口 + * @author starBlues + * @version 3.0.0 + */ +public interface SpringPluginHook extends AutoCloseable{ + + /** + * 停止前校验. 如果抛出 PluginProhibitStopException 异常, 表示当前插件不可停止 + * @throws PluginProhibitStopException 插件禁止停止 + */ + void stopVerify() throws PluginProhibitStopException; + + /** + * 返回插件 ApplicationContext + * @return ApplicationContext + */ + ApplicationContext getApplicationContext(); + + /** + * 得到插件中对 web 的配置 + * @return WebConfig + */ + WebConfig getWebConfig(); + + /** + * 获取插件中对 Thymeleaf 的配置 + * @return ThymeleafConfig + */ + ThymeleafConfig getThymeleafConfig(); + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/spring/WebConfig.java b/spring-brick/src/main/java/com/gitee/starblues/spring/WebConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..a056f33bce5d528d3ecd856b10967bca3f265814 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/WebConfig.java @@ -0,0 +1,34 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring; + +import lombok.Data; + +import java.util.Set; + +/** + * 插件中对web的配置 + * @author starBlues + * @version 3.0.0 + */ +@Data +public class WebConfig { + + private boolean enable = false; + private Set resourceLocations = null; + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/spring/extract/DefaultExtractFactory.java b/spring-brick/src/main/java/com/gitee/starblues/spring/extract/DefaultExtractFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..ed5d69bf75d92e0e6a5479c461c272b8296985fb --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/extract/DefaultExtractFactory.java @@ -0,0 +1,72 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring.extract; + +import java.util.*; + +/** + * 默认的扩展工厂 + * @author starBlues + * @version 3.0.0 + */ +public class DefaultExtractFactory implements ExtractFactory{ + + private final ExtractFactory target; + + public DefaultExtractFactory() { + this.target = new DefaultOpExtractFactory(); + } + + public ExtractFactory getTarget() { + return target; + } + + @Override + public T getExtractByCoordinate(ExtractCoordinate coordinate) { + return target.getExtractByCoordinate(coordinate); + } + + @Override + public T getExtractByCoordinate(String pluginId, ExtractCoordinate coordinate) { + return target.getExtractByCoordinate(pluginId, coordinate); + } + + @Override + public T getExtractByCoordinateOfMain(ExtractCoordinate coordinate) { + return target.getExtractByCoordinate(coordinate); + } + + @Override + public List getExtractByInterClass(Class interfaceClass) { + return target.getExtractByInterClass(interfaceClass); + } + + @Override + public List getExtractByInterClass(String pluginId, Class interfaceClass) { + return target.getExtractByInterClass(pluginId, interfaceClass); + } + + @Override + public List getExtractByInterClassOfMain(Class interfaceClass) { + return target.getExtractByInterClassOfMain(interfaceClass); + } + + @Override + public Map> getExtractCoordinates() { + return target.getExtractCoordinates(); + } +} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/extract/ExtractFactory.java b/spring-brick/src/main/java/com/gitee/starblues/spring/extract/DefaultOpExtractFactory.java similarity index 63% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/extract/ExtractFactory.java rename to spring-brick/src/main/java/com/gitee/starblues/spring/extract/DefaultOpExtractFactory.java index 3c218be0e7b8aff67781ba0db5ec2fcb65014fac..48b05fc06481a60d777efd7c4b6d75958a29e60e 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/extract/ExtractFactory.java +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/extract/DefaultOpExtractFactory.java @@ -1,49 +1,49 @@ -package com.gitee.starblues.factory.process.pipe.extract; +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring.extract; import com.gitee.starblues.annotation.Extract; +import com.gitee.starblues.utils.ObjectUtils; +import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.util.ClassUtils; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; /** - * 扩展工厂 + * 默认的可扩展的工厂 * @author starBlues - * @version 2.4.4 + * @version 3.0.0 */ -public class ExtractFactory { +public class DefaultOpExtractFactory implements OpExtractFactory { public static final String MAIN_EXTRACT_KEY = ExtractFactory.class.getName() + UUID.randomUUID().toString(); private final Map> extractMap = new ConcurrentHashMap<>(); - private final static ExtractFactory EXTRACT_FACTORY = new ExtractFactory(); - - private ExtractFactory(){} - - /** - * 得到全局的扩展工厂 - * @return ExtractFactory - */ - public static ExtractFactory getInstant(){ - return EXTRACT_FACTORY; - } - /** - * 添加扩展 - * @param extractObject 扩展的bean - */ - void addOfMain(Object extractObject){ + @Override + public void addOfMain(Object extractObject) { add(MAIN_EXTRACT_KEY, extractObject); } - - /** - * 添加扩展 - * @param pluginId 插件id - * @param extractObject 扩展的bean - */ - void add(String pluginId, Object extractObject){ + @Override + public void add(String pluginId, Object extractObject) { if(extractObject == null){ return; } @@ -51,26 +51,20 @@ public class ExtractFactory { if(extract == null){ return; } - Map extractObjects = extractMap.computeIfAbsent(pluginId, k -> new ConcurrentHashMap<>()); + Map extractObjects = extractMap.computeIfAbsent(pluginId, k -> + new ConcurrentHashMap<>()); ExtractWrapper extractWrapper = new ExtractWrapper(extractObject, extract.order()); extractObjects.put(new ExtractCoordinate(extract, extractObject.getClass()), extractWrapper); } - /** - * 根据插件id来移除扩展 - * @param pluginId 插件id - */ - void remove(String pluginId){ + @Override + public void remove(String pluginId) { extractMap.remove(pluginId); } - /** - * 通过坐标得到扩展 - * @param coordinate 扩展的坐标 - * @param 扩展的泛型 - * @return 扩展实例, 如果不存在则抛出 RuntimeException 异常 - */ - public T getExtractByCoordinate(ExtractCoordinate coordinate){ + @SuppressWarnings("unchecked") + @Override + public T getExtractByCoordinate(ExtractCoordinate coordinate) { Objects.requireNonNull(coordinate, "ExtractCoordinate can't be null"); int currentOrder = Integer.MIN_VALUE; Object currentObject = null; @@ -91,14 +85,9 @@ public class ExtractFactory { throw new RuntimeException("Not found " + coordinate); } - /** - * 根据插件id和坐标得到扩展 - * @param pluginId 插件id - * @param coordinate 扩展的坐标 - * @param 扩展的泛型 - * @return 扩展实例, 如果不存在则抛出 RuntimeException 异常 - */ - public T getExtractByCoordinate(String pluginId, ExtractCoordinate coordinate){ + @SuppressWarnings("unchecked") + @Override + public T getExtractByCoordinate(String pluginId, ExtractCoordinate coordinate) { Objects.requireNonNull(coordinate, "ExtractCoordinate can't be null"); Map extractCoordinates = extractMap.get(pluginId); if(extractCoordinates == null){ @@ -111,16 +100,9 @@ public class ExtractFactory { return (T) extracts; } - - /** - * 根据坐标得到主程序的扩展 - * 主程序扩展必须使用 @Extract+@Component 进行定义 - * @param coordinate 扩展的坐标 - * @param 扩展的泛型 - * @return 扩展实例, 如果不存在则抛出 RuntimeException 异常 - */ @SuppressWarnings("unchecked") - public T getExtractByCoordinateOfMain(ExtractCoordinate coordinate){ + @Override + public T getExtractByCoordinateOfMain(ExtractCoordinate coordinate) { Objects.requireNonNull(coordinate, "ExtractCoordinate can't be null"); Map extractCoordinates = extractMap.get(MAIN_EXTRACT_KEY); if(extractCoordinates == null){ @@ -133,71 +115,51 @@ public class ExtractFactory { return (T) extractWrapper.getObject(); } - /** - * 根据接口类型获取扩展 - * @param interfaceClass 接口类类型 - * @param 接口类型泛型 - * @return 扩展实现集合 - */ - @SuppressWarnings("unchecked") - public List getExtractByInterClass(Class interfaceClass){ + @Override + public List getExtractByInterClass(Class interfaceClass) { if(interfaceClass == null){ return Collections.emptyList(); } - List extracts = new ArrayList<>(); + List extracts = new ArrayList<>(); for (Map value : extractMap.values()) { for (ExtractWrapper extractWrapper : value.values()) { Set> allInterfacesForClassAsSet = ClassUtils.getAllInterfacesForClassAsSet( extractWrapper.getObject().getClass()); if(allInterfacesForClassAsSet.contains(interfaceClass)){ - extracts.add((T)extractWrapper.getObject()); + extracts.add(extractWrapper); } } } - return extracts; + return sort(extracts); } - /** - * 根据插件id和接口类型获取扩展 - * @param pluginId 插件id - * @param interfaceClass 接口类类型 - * @param 接口类型泛型 - * @return 扩展实现集合 - */ - public List getExtractByInterClass(String pluginId, Class interfaceClass){ + @Override + public List getExtractByInterClass(String pluginId, Class interfaceClass) { if(interfaceClass == null){ return Collections.emptyList(); } - List extracts = new ArrayList<>(); + List extracts = new ArrayList<>(); Map extractCoordinateObjectMap = extractMap.get(pluginId); if(extractCoordinateObjectMap == null || extractCoordinateObjectMap.isEmpty()){ return Collections.emptyList(); } - for (Object o : extractCoordinateObjectMap.values()) { - Set> allInterfacesForClassAsSet = ClassUtils.getAllInterfacesForClassAsSet(o.getClass()); + for (ExtractWrapper wrapper : extractCoordinateObjectMap.values()) { + Object object = wrapper.getObject(); + Set> allInterfacesForClassAsSet = ClassUtils.getAllInterfacesForClassAsSet(object.getClass()); if(allInterfacesForClassAsSet.contains(interfaceClass)){ - extracts.add((T)o); + extracts.add(wrapper); } } - return extracts; + return sort(extracts); } - /** - * 根据接口类型获取主程序的扩展 - * 主程序扩展必须使用 @Extract+@Component 进行定义 - * @param interfaceClass 接口类类型 - * @param 接口类型泛型 - * @return 扩展实现集合 - */ - public List getExtractByInterClassOfMain(Class interfaceClass){ + @Override + public List getExtractByInterClassOfMain(Class interfaceClass) { return getExtractByInterClass(MAIN_EXTRACT_KEY, interfaceClass); } - /** - * 得到所有的扩展坐标 - * @return 扩展坐标集合, key 为插件id, 值为所有扩展坐标集合 - */ - public Map> getExtractCoordinates(){ + @Override + public Map> getExtractCoordinates() { Map> extractCoordinateMap = new HashMap<>(extractMap.size()); extractMap.forEach((k, v)->{ Set extractCoordinates = new HashSet<>(v.size()); @@ -207,23 +169,27 @@ public class ExtractFactory { return extractCoordinateMap; } - /** - * 得到扩展的对象注解 - * @param extractObject 扩展对象 - * @return Extract 注解 - */ + @SuppressWarnings("unchecked") + private List sort(List extractWrappers){ + if(ObjectUtils.isEmpty(extractWrappers)){ + return new ArrayList<>(0); + } + return extractWrappers.stream() + .sorted(Comparator.comparing(ExtractWrapper::getOrder, + Comparator.nullsLast(Comparator.reverseOrder()))) + .map(extractWrapper -> (T) extractWrapper.getObject()) + .collect(Collectors.toList()); + } + private Extract getExtract(Object extractObject){ - Extract annotation = extractObject.getClass().getAnnotation(Extract.class); - if(annotation == null){ - return null; - } - return annotation; + return extractObject.getClass().getAnnotation(Extract.class); } + /** * 扩展对象包装类型 **/ - private class ExtractWrapper{ + private static class ExtractWrapper{ private final Object object; private final int order; diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/extract/ExtractCoordinate.java b/spring-brick/src/main/java/com/gitee/starblues/spring/extract/ExtractCoordinate.java similarity index 72% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/extract/ExtractCoordinate.java rename to spring-brick/src/main/java/com/gitee/starblues/spring/extract/ExtractCoordinate.java index a23407ea2e8c27da105e5f7c81dbd29a07a72c42..813998354556bd7a86614165948136db91aef11c 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/extract/ExtractCoordinate.java +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/extract/ExtractCoordinate.java @@ -1,14 +1,30 @@ -package com.gitee.starblues.factory.process.pipe.extract; +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring.extract; import com.gitee.starblues.annotation.Extract; -import org.pf4j.util.StringUtils; +import com.gitee.starblues.utils.ObjectUtils; import java.util.Objects; /** * 执行器坐标 * @author starBlues - * @version 2.4.4 + * @version 3.0.0 */ public class ExtractCoordinate { @@ -69,20 +85,18 @@ public class ExtractCoordinate { return false; } ExtractCoordinate that = (ExtractCoordinate) o; - if(StringUtils.isNotNullOrEmpty(bus) && - StringUtils.isNotNullOrEmpty(scene) && - StringUtils.isNotNullOrEmpty(useCase)){ + if(!ObjectUtils.isEmpty(bus) && !ObjectUtils.isEmpty(scene) && !ObjectUtils.isEmpty(useCase)){ return Objects.equals(getBus(), that.getBus()) && Objects.equals(getScene(), that.getScene()) && Objects.equals(getUseCase(), that.getUseCase()); } - if(StringUtils.isNotNullOrEmpty(bus) && StringUtils.isNotNullOrEmpty(scene)){ + if(!ObjectUtils.isEmpty(bus) && !ObjectUtils.isEmpty(scene)){ return Objects.equals(getBus(), that.getBus()) && Objects.equals(getScene(), that.getScene()); } - if(StringUtils.isNotNullOrEmpty(bus)){ + if(!ObjectUtils.isEmpty(bus)){ return Objects.equals(getBus(), that.getBus()); } diff --git a/spring-brick/src/main/java/com/gitee/starblues/spring/extract/ExtractFactory.java b/spring-brick/src/main/java/com/gitee/starblues/spring/extract/ExtractFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..898c5727d5de2495740a9db4bc30a857aa2010f4 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/extract/ExtractFactory.java @@ -0,0 +1,97 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring.extract; + +import java.util.*; + +/** + * 扩展工厂 + * @author starBlues + * @version 3.0.0 + */ +public interface ExtractFactory { + + ExtractFactory INSTANT = new DefaultExtractFactory(); + + /** + * 获取实例 + * @return ExtractFactory + */ + static ExtractFactory getInstant(){ + return INSTANT; + } + + /** + * 通过坐标得到扩展 + * @param coordinate 扩展的坐标 + * @param 扩展的泛型 + * @return 扩展实例, 如果不存在则抛出 RuntimeException 异常 + */ + T getExtractByCoordinate(ExtractCoordinate coordinate); + + /** + * 根据插件id和坐标得到扩展 + * @param pluginId 插件id + * @param coordinate 扩展的坐标 + * @param 扩展的泛型 + * @return 扩展实例, 如果不存在则抛出 RuntimeException 异常 + */ + T getExtractByCoordinate(String pluginId, ExtractCoordinate coordinate); + + + /** + * 根据坐标得到主程序的扩展 + * 主程序扩展必须使用 @Extract+@Component 进行定义 + * @param coordinate 扩展的坐标 + * @param 扩展的泛型 + * @return 扩展实例, 如果不存在则抛出 RuntimeException 异常 + */ + T getExtractByCoordinateOfMain(ExtractCoordinate coordinate); + + /** + * 根据接口类型获取扩展 + * @param interfaceClass 接口类类型 + * @param 接口类型泛型 + * @return 扩展实现集合 + */ + List getExtractByInterClass(Class interfaceClass); + + /** + * 根据插件id和接口类型获取扩展 + * @param pluginId 插件id + * @param interfaceClass 接口类类型 + * @param 接口类型泛型 + * @return 扩展实现集合 + */ + List getExtractByInterClass(String pluginId, Class interfaceClass); + + /** + * 根据接口类型获取主程序的扩展 + * 主程序扩展必须使用 @Extract+@Component 进行定义 + * @param interfaceClass 接口类类型 + * @param 接口类型泛型 + * @return 扩展实现集合 + */ + List getExtractByInterClassOfMain(Class interfaceClass); + + /** + * 得到所有的扩展坐标 + * @return 扩展坐标集合, key 为插件id, 值为所有扩展坐标集合 + */ + Map> getExtractCoordinates(); + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/spring/extract/OpExtractFactory.java b/spring-brick/src/main/java/com/gitee/starblues/spring/extract/OpExtractFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..0a934b63208a618113673302d04cdad9e69a16ca --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/extract/OpExtractFactory.java @@ -0,0 +1,45 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring.extract; + +/** + * 可操作的扩展工厂 + * @author starBlues + * @version 3.0.0 + */ +public interface OpExtractFactory extends ExtractFactory{ + + /** + * 添加main中的扩展 + * @param extractObject extractObject + */ + void addOfMain(Object extractObject); + + /** + * 添加插件中的扩展 + * @param pluginId 插件 + * @param extractObject 扩展对象 + */ + void add(String pluginId, Object extractObject); + + /** + * 移除插件中的扩展 + * @param pluginId 插件id + */ + void remove(String pluginId); + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/spring/invoke/DefaultInvokeSupperCache.java b/spring-brick/src/main/java/com/gitee/starblues/spring/invoke/DefaultInvokeSupperCache.java new file mode 100644 index 0000000000000000000000000000000000000000..2917f8d79c6326c14f2fe7b024f4fe1fc472bd32 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/invoke/DefaultInvokeSupperCache.java @@ -0,0 +1,81 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring.invoke; + +import com.gitee.starblues.spring.ApplicationContext; +import com.gitee.starblues.spring.SpringBeanFactory; +import com.gitee.starblues.utils.ObjectUtils; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 默认的 InvokeSupperCache + * @author starBlues + * @version 3.0.0 + */ +public class DefaultInvokeSupperCache implements InvokeSupperCache{ + + private final Map> invokeSupplierCache = new ConcurrentHashMap<>(); + + @Override + public Object getSupperBean(String supperKey){ + return getSupperBean(null, supperKey); + } + + @Override + public Object getSupperBean(String pluginId, String supperKey){ + if(!ObjectUtils.isEmpty(pluginId)){ + Map cacheMap = invokeSupplierCache.get(pluginId); + if(cacheMap == null){ + return null; + } + return getSupperBean(cacheMap.get(supperKey)); + } + for (Map value : invokeSupplierCache.values()) { + Object supperBean = getSupperBean(value.get(supperKey)); + if(supperBean != null){ + return supperBean; + } + } + return null; + } + + @Override + public void add(String pluginId, SupperCache cache){ + Map supperCache = invokeSupplierCache.computeIfAbsent(pluginId, k -> new HashMap<>()); + supperCache.put(cache.getSupperKey(), cache); + } + + @Override + public void remove(String pluginId){ + invokeSupplierCache.remove(pluginId); + } + + private static Object getSupperBean(SupperCache cache){ + if(cache == null){ + return null; + } + ApplicationContext applicationContext = cache.getApplicationContext(); + SpringBeanFactory springBeanFactory = applicationContext.getSpringBeanFactory(); + if(springBeanFactory.containsBean(cache.getBeanName())){ + return springBeanFactory.getBean(cache.getBeanName()); + } + return null; + } +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/spring/invoke/InvokeSupperCache.java b/spring-brick/src/main/java/com/gitee/starblues/spring/invoke/InvokeSupperCache.java new file mode 100644 index 0000000000000000000000000000000000000000..8422c5c8ca00eb1dc78430fed22960e2d435af7b --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/invoke/InvokeSupperCache.java @@ -0,0 +1,57 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring.invoke; + + +/** + * 插件调用提供者缓存 + * @author starBlues + * @version 3.0.0 + */ +public interface InvokeSupperCache { + + /** + * 获取提供者bean + * @param pluginId 插件id + * @param supperKey 提供者key + * @return Object + */ + Object getSupperBean(String pluginId, String supperKey); + + /** + * 获取提供者bean + * @param supperKey 提供者key + * @return Object + */ + Object getSupperBean(String supperKey); + + /** + * 添加提供者 + * @param pluginId 插件id + * @param cache 提供者缓存 + */ + void add(String pluginId, SupperCache cache); + + /** + * 移除插件提供者 + * @param pluginId 插件id + */ + void remove(String pluginId); + + + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/spring/invoke/SupperCache.java b/spring-brick/src/main/java/com/gitee/starblues/spring/invoke/SupperCache.java new file mode 100644 index 0000000000000000000000000000000000000000..de290f06ff7a3fb3ebc56d63cfe07089cd876be3 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/invoke/SupperCache.java @@ -0,0 +1,50 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring.invoke; + +import com.gitee.starblues.spring.ApplicationContext; + +/** + * 提供者缓存包装 + * @author starBlues + * @version 3.0.0 + */ +public class SupperCache { + + private final String supperKey; + private final String beanName; + private final ApplicationContext applicationContext; + + public SupperCache(String supperKey, String beanName, ApplicationContext applicationContext) { + this.supperKey = supperKey; + this.beanName = beanName; + this.applicationContext = applicationContext; + } + + public String getSupperKey() { + return supperKey; + } + + public String getBeanName() { + return beanName; + } + + public ApplicationContext getApplicationContext() { + return applicationContext; + } + +} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/loader/PluginResource.java b/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginResource.java similarity index 76% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/loader/PluginResource.java rename to spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginResource.java index 59543fb0922c50e4a065f92bae409c83ab19822e..d7e9bf9c75c09c0c7c2c3321a521eb5b7442fa34 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/loader/PluginResource.java +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginResource.java @@ -1,11 +1,27 @@ -package com.gitee.starblues.factory.process.pipe.loader; +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring.web; -import com.gitee.starblues.factory.PluginRegistryInfo; -import org.pf4j.PluginWrapper; +import com.gitee.starblues.core.descriptor.PluginDescriptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.NestedIOException; import org.springframework.core.io.Resource; +import org.springframework.util.ClassUtils; import org.springframework.util.ResourceUtils; import org.springframework.util.StringUtils; @@ -21,14 +37,14 @@ import java.net.URLConnection; * 插件资源实现类.主要是对Spring中的抽象的Resource实现. * 功能: 主要是获取插件包中的文件资源。 * @author starBlues - * @version 2.4.0 + * @version 3.0.0 */ public class PluginResource implements Resource { private final static Logger log = LoggerFactory.getLogger(PluginResource.class); - private ClassLoader classLoader; - private final PluginWrapper pluginWrapper; + private ClassLoader classLoader = ClassUtils.getDefaultClassLoader(); + private final PluginDescriptor pluginDescriptor; private final long lastModified; private final String path; @@ -37,20 +53,16 @@ public class PluginResource implements Resource { /** * 相对Classpath 路径 * @param path 路径 - * @param pluginRegistryInfo pluginRegistryInfo bean + * @param pluginDescriptor pluginDescriptor */ - public PluginResource(String path, PluginRegistryInfo pluginRegistryInfo) { + public PluginResource(String path, PluginDescriptor pluginDescriptor) { String pathToUse = StringUtils.cleanPath(path); if (pathToUse.startsWith("/")) { pathToUse = pathToUse.substring(1); } this.path = pathToUse; - - PluginWrapper pluginWrapper = pluginRegistryInfo.getPluginWrapper(); - this.classLoader = pluginRegistryInfo.getPluginClassLoader(); - this.pluginWrapper = pluginWrapper; - - this.lastModified = pluginRegistryInfo.getBasePlugin().getBasePluginExtend().getStartTimestamp(); + this.pluginDescriptor = pluginDescriptor; + this.lastModified = System.currentTimeMillis(); } public void setClassLoader(ClassLoader classLoader) { @@ -94,7 +106,7 @@ public class PluginResource implements Resource { @Override public String getDescription() { - return pluginWrapper.getDescriptor().getPluginDescription(); + return pluginDescriptor.getDescription(); } @Override @@ -169,3 +181,4 @@ public class PluginResource implements Resource { } } + diff --git a/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginStaticResourceConfig.java b/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginStaticResourceConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..75eeafd646c33b6c343bfb15105dc5d60bf60a9f --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginStaticResourceConfig.java @@ -0,0 +1,84 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring.web; + +import com.gitee.starblues.utils.Assert; +import com.gitee.starblues.utils.UrlUtils; +import lombok.Data; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.CacheControl; + +/** + * 插件PluginStaticResourceConfig + * @author starBlues + * @version 3.0.0 + */ +public class PluginStaticResourceConfig { + + public static final String DEFAULT_PLUGIN_STATIC_RESOURCE_PATH_PREFIX = "static-plugin"; + public static final String DEFAULT_INDEX_PAGE_NAME = "index.html"; + private static final Logger log = LoggerFactory.getLogger(PluginStaticResourceConfig.class); + + /** + * 插件静态资源访问前缀 + */ + private String pathPrefix = DEFAULT_PLUGIN_STATIC_RESOURCE_PATH_PREFIX; + + /** + * 默认首页页面名称 + */ + private String indexPageName = DEFAULT_INDEX_PAGE_NAME; + + /** + * 页面是否缓存 + */ + private CacheControl cacheControl = CacheControl.noCache(); + + + public void logPathPrefix(){ + log.info("插件静态资源访问前缀配置为: /{}/{pluginId}", pathPrefix); + } + + + public String getPathPrefix() { + return pathPrefix; + } + + public void setPathPrefix(String pathPrefix) { + Assert.isNotEmpty(pathPrefix, "配置 pathPrefix 不能为空"); + this.pathPrefix = UrlUtils.format(pathPrefix); + } + + public String getIndexPageName() { + return indexPageName; + } + + public void setIndexPageName(String indexPageName) { + Assert.isNotEmpty(pathPrefix, "配置 indexPageName 不能为空"); + this.indexPageName = indexPageName; + } + + public CacheControl getCacheControl() { + return cacheControl; + } + + public void setCacheControl(CacheControl cacheControl) { + Assert.isNotNull(pathPrefix, "配置 cacheControl 不能为空"); + this.cacheControl = cacheControl; + } +} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/resolver/PluginResourceResolver.java b/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginStaticResourceResolver.java similarity index 59% rename from springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/resolver/PluginResourceResolver.java rename to spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginStaticResourceResolver.java index 1a8d2b1d8d7baa010ca7f6f303b93c5cefadb062..ea2a84cb7a1394ca7ac7fe743fdaa1df8c11ee3d 100644 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/resolver/PluginResourceResolver.java +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginStaticResourceResolver.java @@ -1,9 +1,26 @@ -package com.gitee.starblues.extension.resources.resolver; +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring.web; -import com.gitee.starblues.extension.resources.StaticResourceConfig; -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.pipe.loader.PluginResource; -import com.gitee.starblues.realize.BasePlugin; +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.spring.WebConfig; +import com.gitee.starblues.utils.MsgUtils; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.UrlUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.FileUrlResource; @@ -23,20 +40,22 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; /** - * 插件资源发现者 - * + * 插件web静态资源Resolver * @author starBlues - * @version 2.4.0 + * @version 3.0.0 */ -public class PluginResourceResolver extends AbstractResourceResolver { +public class PluginStaticResourceResolver extends AbstractResourceResolver { - private final static Logger logger = LoggerFactory.getLogger(PluginResourceResolver.class); + private final static Logger logger = LoggerFactory.getLogger(PluginStaticResourceResolver.class); private final static String RESOLVED_RESOURCE_CACHE_KEY_PREFIX = "resolvedPluginResource:"; private final static Map PLUGIN_RESOURCE_MAP = new ConcurrentHashMap<>(); - PluginResourceResolver() { + private final PluginStaticResourceConfig config; + + public PluginStaticResourceResolver(PluginStaticResourceConfig config) { + this.config = config; } @@ -44,42 +63,66 @@ public class PluginResourceResolver extends AbstractResourceResolver { protected Resource resolveResourceInternal(HttpServletRequest request, String requestPath, List locations, ResourceResolverChain chain) { + if(request != null){ + String requestUri = request.getRequestURI(); + String formatUri = UrlUtils.format(requestUri); + requestPath = UrlUtils.format(formatUri.replace(config.getPathPrefix(), "")); + } + int startOffset = requestPath.indexOf("/"); + String pluginId = null; + String partialPath = null; + if (startOffset == -1) { + pluginId = requestPath; + partialPath = config.getIndexPageName(); + } else { + pluginId = requestPath.substring(0, startOffset); + partialPath = requestPath.substring(startOffset + 1); + } - int startOffset = (requestPath.startsWith("/") ? 1 : 0); - int endOffset = requestPath.indexOf('/', 1); - if (endOffset != -1) { - String pluginId = requestPath.substring(startOffset, endOffset); - String partialPath = requestPath.substring(endOffset + 1); - PluginStaticResource pluginResource = PLUGIN_RESOURCE_MAP.get(pluginId); + PluginStaticResource pluginResource = PLUGIN_RESOURCE_MAP.get(pluginId); - if(pluginResource == null){ - return null; - } + if(pluginResource == null){ + return chain.resolveResource(request, requestPath, locations); + } - String key = computeKey(request, requestPath); - // 先判断缓存中是否存在。 - Resource resource = pluginResource.getCacheResource(key); - if(resource != null){ - return resource; + String key = computeKey(request, requestPath); + // 先判断缓存中是否存在。 + Resource resource = pluginResource.getCacheResource(key); + if(resource != null){ + return resource; + } + resource = findResource(pluginResource, partialPath); + if(resource != null){ + pluginResource.putCacheResource(key, resource); + return resource; + } else { + // 尝试获取首页页面 + String indexPageName = config.getIndexPageName(); + if(ObjectUtils.isEmpty(indexPageName)){ + indexPageName = PluginStaticResourceConfig.DEFAULT_INDEX_PAGE_NAME; } - - // 从classpath 获取资源 - resource = resolveClassPath(pluginResource, partialPath); - if(resource != null){ - pluginResource.putCacheResource(key, resource); - return resource; + if(partialPath.lastIndexOf(".") > -1){ + // 存在后缀 + return null; } + partialPath = UrlUtils.joiningUrlPath(partialPath, indexPageName); + return findResource(pluginResource, partialPath); + } + } - // 从外置文件路径获取资源 - resource = resolveFilePath(pluginResource, partialPath); - if(resource != null){ - pluginResource.putCacheResource(key, resource); - return resource; - } - return null; + private Resource findResource(PluginStaticResource pluginResource, String partialPath){ + // 从classpath 获取资源 + Resource resource = resolveClassPath(pluginResource, partialPath); + if(resource != null){ + return resource; + } + // 从外置文件路径获取资源 + resource = resolveFilePath(pluginResource, partialPath); + if(resource != null){ + return resource; } - return chain.resolveResource(request, requestPath, locations); + return resource; } /** @@ -88,22 +131,16 @@ public class PluginResourceResolver extends AbstractResourceResolver { * @param partialPath 部分路径 * @return 资源。没有发现则返回null */ - private Resource resolveClassPath(PluginStaticResource pluginResource, - String partialPath){ + private Resource resolveClassPath(PluginStaticResource pluginResource, String partialPath){ Set classPaths = pluginResource.getClassPaths(); if(classPaths == null || classPaths.isEmpty()){ return null; } - PluginRegistryInfo pluginRegistryInfo = pluginResource.getPluginRegistryInfo(); - BasePlugin basePlugin = pluginRegistryInfo.getBasePlugin(); - if(basePlugin == null){ - return null; - } - ClassLoader pluginClassLoader = pluginRegistryInfo.getPluginClassLoader(); + ClassLoader pluginClassLoader = pluginResource.getPluginClassLoader(); for (String classPath : classPaths) { try { - PluginResource resource = new PluginResource(classPath + partialPath, pluginRegistryInfo); + PluginResource resource = new PluginResource(classPath + partialPath, pluginResource.getPluginDescriptor()); resource.setClassLoader(pluginClassLoader); if(resource.exists()){ return resource; @@ -163,7 +200,7 @@ public class PluginResourceResolver extends AbstractResourceResolver { key.append(requestPath); if (request != null) { String codingKey = getContentCodingKey(request); - if (StringUtils.hasText(codingKey)) { + if (ObjectUtils.hasText(codingKey)) { key.append("+encoding=").append(codingKey); } } @@ -177,7 +214,7 @@ public class PluginResourceResolver extends AbstractResourceResolver { */ private String getContentCodingKey(HttpServletRequest request) { String header = request.getHeader(HttpHeaders.ACCEPT_ENCODING); - if (!StringUtils.hasText(header)) { + if (!ObjectUtils.hasText(header)) { return null; } return Arrays.stream(StringUtils.tokenizeToStringArray(header, ",")) @@ -193,27 +230,34 @@ public class PluginResourceResolver extends AbstractResourceResolver { /** * 每新增一个插件, 都需要调用该方法,来解析该插件的 StaticResourceConfig 配置。并将其保存到 StaticResourceConfig bean 中。 - * @param pluginRegistryInfo 插件信息 - * @param locations 静态资源配置 + * @param pluginDescriptor 插件信息 + * @param pluginClassLoader 插件classloader + * @param webConfig web配置 */ - public static synchronized void parse(PluginRegistryInfo pluginRegistryInfo, - Set locations){ - if(locations == null || locations.isEmpty()){ + public static synchronized void parse(PluginDescriptor pluginDescriptor, + ClassLoader pluginClassLoader, + WebConfig webConfig){ + if(!webConfig.isEnable()){ + return; + } + final Set locations = webConfig.getResourceLocations(); + if(ObjectUtils.isEmpty(locations)){ return; } Set classPaths = new HashSet<>(); Set filePaths = new HashSet<>(); - String pluginId = pluginRegistryInfo.getPluginWrapper().getPluginId(); + String pluginId = pluginDescriptor.getPluginId(); for (String location : locations) { - if(StringUtils.isEmpty(location)){ + if(ObjectUtils.isEmpty(location)){ continue; } final int first = location.indexOf(":"); if(first == -1){ - logger.warn("This plugin '{}' location config '{}' cannot be resolved", pluginId, location); + logger.warn("插件[{}]配置的静态资源格式错误: {}", + MsgUtils.getPluginUnique(pluginDescriptor), location); continue; } String type = location.substring(0, first); @@ -233,16 +277,17 @@ public class PluginResourceResolver extends AbstractResourceResolver { } filePaths.add(path); } else { - logger.warn("The plugin '{}' type '{}' cannot be resolved", pluginId, type); + logger.warn("插件[{}]配置的静态资源类型不能识别: {}", MsgUtils.getPluginUnique(pluginDescriptor), type); } } PluginStaticResource pluginResource = new PluginStaticResource(); pluginResource.setClassPaths(classPaths); pluginResource.setFilePaths(filePaths); - pluginResource.setPluginRegistryInfo(pluginRegistryInfo); + pluginResource.setPluginDescriptor(pluginDescriptor); + pluginResource.setPluginClassLoader(pluginClassLoader); - logger.info("PluginResources '{}' set classpath resources: {}, set file resources: {}", pluginId, + logger.info("插件[{}]配置的静态资源: classpath[{}], file[{}]", MsgUtils.getPluginUnique(pluginDescriptor), classPaths, filePaths); if(PLUGIN_RESOURCE_MAP.containsKey(pluginId)){ @@ -266,8 +311,6 @@ public class PluginResourceResolver extends AbstractResourceResolver { PLUGIN_RESOURCE_MAP.remove(pluginId); } - - /** * 插件资源解析后的信息 */ @@ -276,7 +319,12 @@ public class PluginResourceResolver extends AbstractResourceResolver { /** * basePlugin bean */ - private PluginRegistryInfo pluginRegistryInfo; + private PluginDescriptor pluginDescriptor; + + /** + * 插件classloader + */ + private ClassLoader pluginClassLoader; /** * 定义的classpath集合 @@ -291,14 +339,22 @@ public class PluginResourceResolver extends AbstractResourceResolver { /** * 缓存的资源。key 为资源的可以。值为资源 */ - private Map cacheResourceMaps = new ConcurrentHashMap<>(); + private final Map cacheResourceMaps = new ConcurrentHashMap<>(); + + PluginDescriptor getPluginDescriptor() { + return pluginDescriptor; + } + + void setPluginDescriptor(PluginDescriptor pluginDescriptor) { + this.pluginDescriptor = pluginDescriptor; + } - PluginRegistryInfo getPluginRegistryInfo() { - return pluginRegistryInfo; + ClassLoader getPluginClassLoader() { + return pluginClassLoader; } - void setPluginRegistryInfo(PluginRegistryInfo pluginRegistryInfo) { - this.pluginRegistryInfo = pluginRegistryInfo; + void setPluginClassLoader(ClassLoader pluginClassLoader) { + this.pluginClassLoader = pluginClassLoader; } Set getClassPaths() { @@ -328,8 +384,9 @@ public class PluginResourceResolver extends AbstractResourceResolver { } cacheResourceMaps.put(key, resource); } - } - } + + + diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/resolver/ResourceWebMvcConfigurer.java b/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginStaticResourceWebMvcConfigurer.java similarity index 40% rename from springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/resolver/ResourceWebMvcConfigurer.java rename to spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginStaticResourceWebMvcConfigurer.java index 9a568306940a9c7c7ab6621188d1855d01f4d462..984a1984307cd2304c39292fae0d82474c2d371f 100644 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/resolver/ResourceWebMvcConfigurer.java +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginStaticResourceWebMvcConfigurer.java @@ -1,29 +1,44 @@ -package com.gitee.starblues.extension.resources.resolver; +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.spring.web; -import com.gitee.starblues.extension.resources.StaticResourceExtension; import org.springframework.http.CacheControl; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** - * 注册插件的WebMvc的配置 - * + * 插件静态资源webMvc配置 * @author starBlues - * @version 2.2.1 + * @version 3.0.0 */ -public class ResourceWebMvcConfigurer implements WebMvcConfigurer { +public class PluginStaticResourceWebMvcConfigurer implements WebMvcConfigurer { + private final PluginStaticResourceConfig resourceConfig; - public ResourceWebMvcConfigurer() { + public PluginStaticResourceWebMvcConfigurer(PluginStaticResourceConfig resourceConfig) { + this.resourceConfig = resourceConfig; } - @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { - String pathPattern = "/" + StaticResourceExtension.getPluginStaticResourcePathPrefix() + "/**"; + String pathPattern = "/" + resourceConfig.getPathPrefix() + "/**"; ResourceHandlerRegistration resourceHandlerRegistration = registry.addResourceHandler(pathPattern); - CacheControl cacheControl = StaticResourceExtension.getPluginStaticResourcesCacheControl(); + CacheControl cacheControl = resourceConfig.getCacheControl(); if(cacheControl != null){ resourceHandlerRegistration.setCacheControl(cacheControl); } else { @@ -31,7 +46,7 @@ public class ResourceWebMvcConfigurer implements WebMvcConfigurer { } resourceHandlerRegistration .resourceChain(false) - .addResolver(new PluginResourceResolver()); + .addResolver(new PluginStaticResourceResolver(resourceConfig)); } diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/thymeleaf/ThymeleafProcessor.java b/spring-brick/src/main/java/com/gitee/starblues/spring/web/thymeleaf/PluginThymeleafInvolved.java similarity index 32% rename from springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/thymeleaf/ThymeleafProcessor.java rename to spring-brick/src/main/java/com/gitee/starblues/spring/web/thymeleaf/PluginThymeleafInvolved.java index c1eca8e5ee9344796779690d1e1e0d8c8eafe7f0..999816eb97da94b155342fea1cbcd4267fdf2688 100644 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/thymeleaf/ThymeleafProcessor.java +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/web/thymeleaf/PluginThymeleafInvolved.java @@ -1,84 +1,76 @@ -package com.gitee.starblues.extension.resources.thymeleaf; +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -import com.gitee.starblues.extension.resources.PropertyKey; -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.pipe.PluginPipeProcessorExtend; +package com.gitee.starblues.spring.web.thymeleaf; + +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.core.launcher.plugin.involved.PluginLaunchInvolved; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.spring.SpringPluginHook; import com.gitee.starblues.utils.ClassUtils; -import com.gitee.starblues.utils.OrderPriority; -import com.gitee.starblues.utils.SpringBeanUtils; +import com.gitee.starblues.utils.ObjectUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.boot.context.properties.bind.Bindable; -import org.springframework.boot.context.properties.bind.Binder; import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringUtils; import org.thymeleaf.spring5.SpringTemplateEngine; import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; import org.thymeleaf.templateresolver.ITemplateResolver; -import java.lang.reflect.Field; +import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; /** - * Thymeleaf 处理者 + * 插件 Thymeleaf 注册 * @author starBlues - * @version 2.4.0 + * @version 3.0.0 */ -public class ThymeleafProcessor implements PluginPipeProcessorExtend { +public class PluginThymeleafInvolved implements PluginLaunchInvolved { - private static final String TEMPLATE_RESOLVER_BEAN = "ClassLoaderTemplateResolver"; - private static final Logger LOGGER = LoggerFactory.getLogger(ThymeleafProcessor.class); + private static final Logger logger = LoggerFactory.getLogger(PluginThymeleafInvolved.class); - public ThymeleafProcessor() { - } + private Set templateResolvers; - @Override - public String key() { - return "ThymeleafProcessor"; - } + private final Map pluginTemplateResolver = new ConcurrentHashMap<>(); @Override - public OrderPriority order() { - return OrderPriority.getMiddlePriority(); + public void initialize(GenericApplicationContext applicationContext, IntegrationConfiguration configuration) { + this.templateResolvers = getTemplateResolvers(getSpringTemplateEngine(applicationContext)); } @Override - public void initialize() throws Exception { - - } - - @Override - public void registry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - SpringTemplateEngine springTemplateEngine = getSpringTemplateEngine(pluginRegistryInfo); - if(springTemplateEngine == null){ + public void after(InsidePluginDescriptor descriptor, ClassLoader classLoader, SpringPluginHook pluginHook) throws Exception { + if(templateResolvers == null){ return; } - ThymeleafConfig thymeleafConfig = pluginRegistryInfo.getPluginBinder() - .bind(PropertyKey.THYMELEAF_CONFIG, ThymeleafConfig.class) - .orElse(null); - if(thymeleafConfig == null){ - SpringBootThymeleafConfig config = SpringBeanUtils.getObjectByInterfaceClass( - pluginRegistryInfo.getConfigSingletons(), - SpringBootThymeleafConfig.class); - if(config == null){ - return; - } - thymeleafConfig = new ThymeleafConfig(); - config.config(thymeleafConfig); + ThymeleafConfig thymeleafConfig = pluginHook.getThymeleafConfig(); + if(thymeleafConfig == null || !thymeleafConfig.isEnabled()){ + return; } String prefix = thymeleafConfig.getPrefix(); - if(StringUtils.isEmpty(prefix)){ + if(ObjectUtils.isEmpty(prefix)){ throw new IllegalArgumentException("prefix can't be empty"); } else { if(!prefix.endsWith("/")){ thymeleafConfig.setPrefix(prefix + "/"); } } - if(StringUtils.isEmpty(thymeleafConfig.getSuffix())){ + if(ObjectUtils.isEmpty(thymeleafConfig.getSuffix())){ throw new IllegalArgumentException("suffix can't be empty"); } @@ -86,10 +78,7 @@ public class ThymeleafProcessor implements PluginPipeProcessorExtend { throw new IllegalArgumentException("mode can't be null"); } - ClassLoader pluginClassLoader = pluginRegistryInfo.getPluginWrapper().getPluginClassLoader(); - ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver( - pluginClassLoader - ); + ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver(classLoader); resolver.setPrefix(thymeleafConfig.getPrefix() + "/"); resolver.setSuffix(thymeleafConfig.getSuffix()); resolver.setTemplateMode(thymeleafConfig.getMode()); @@ -103,55 +92,45 @@ public class ThymeleafProcessor implements PluginPipeProcessorExtend { resolver.setOrder(order); } resolver.setCheckExistence(true); - Set templateResolvers = getITemplateResolvers(springTemplateEngine); - if(templateResolvers != null){ - templateResolvers.add(resolver); - } else { - LOGGER.error("You can't use Thymeleaf, because not fount 'Set' " + - "from Bean:SpringTemplateEngine by reflect"); - return; + templateResolvers.add(resolver); + if(!pluginTemplateResolver.containsKey(descriptor.getPluginId())){ + pluginTemplateResolver.put(descriptor.getPluginId(), resolver); } - pluginRegistryInfo.addExtension(TEMPLATE_RESOLVER_BEAN, resolver); } @Override - public void unRegistry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - Object resolver = pluginRegistryInfo.getExtension(TEMPLATE_RESOLVER_BEAN); - if(resolver == null){ - return; - } - try { - SpringTemplateEngine springTemplateEngine = getSpringTemplateEngine(pluginRegistryInfo); - Set templateResolvers = getITemplateResolvers(springTemplateEngine); - if(templateResolvers != null && resolver instanceof ClassLoaderTemplateResolver){ - templateResolvers.remove(resolver); - } - } catch (Exception e){ - LOGGER.error("unRegistry plugin '{}' templateResolver failure", - pluginRegistryInfo.getPluginWrapper().getPluginId(),e); - } + public void close(InsidePluginDescriptor descriptor, ClassLoader classLoader) throws Exception { + pluginTemplateResolver.remove(descriptor.getPluginId()); } - private SpringTemplateEngine getSpringTemplateEngine(PluginRegistryInfo pluginRegistryInfo){ - GenericApplicationContext applicationContext = pluginRegistryInfo.getMainApplicationContext(); - String[] beanNamesForType = applicationContext.getBeanNamesForType(SpringTemplateEngine.class, + private SpringTemplateEngine getSpringTemplateEngine(GenericApplicationContext context){ + String[] beanNamesForType = context.getBeanNamesForType(SpringTemplateEngine.class, false, false); if(beanNamesForType.length == 0){ return null; } try { - return applicationContext.getBean(SpringTemplateEngine.class); + return context.getBean(SpringTemplateEngine.class); } catch (Exception e){ return null; } } - private Set getITemplateResolvers (SpringTemplateEngine springTemplateEngine) throws IllegalAccessException { + private Set getTemplateResolvers(SpringTemplateEngine springTemplateEngine) { + String errorMsg = "当前插件不能使用Thymeleaf, 主程序未发现Thymeleaf注册入口"; if(springTemplateEngine == null){ + logger.error(errorMsg); + } + try { + Set templateResolvers = ClassUtils.getReflectionField(springTemplateEngine, "templateResolvers"); + if(templateResolvers == null) { + logger.error(errorMsg); + } + return templateResolvers; + } catch (Exception e){ + logger.error("当前插件不能使用Thymeleaf, 获取主程序注册入口失败. {}", e.getMessage()); return null; } - return ClassUtils.getReflectionField(springTemplateEngine, "templateResolvers"); } - } diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/thymeleaf/ThymeleafConfig.java b/spring-brick/src/main/java/com/gitee/starblues/spring/web/thymeleaf/ThymeleafConfig.java similarity index 63% rename from springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/thymeleaf/ThymeleafConfig.java rename to spring-brick/src/main/java/com/gitee/starblues/spring/web/thymeleaf/ThymeleafConfig.java index 3b11388c85b0e842dc45bc6330ef8ad8de9ee7fa..479b1c7f62cb478f44df27e8587891683e51e058 100644 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/thymeleaf/ThymeleafConfig.java +++ b/spring-brick/src/main/java/com/gitee/starblues/spring/web/thymeleaf/ThymeleafConfig.java @@ -1,23 +1,39 @@ -package com.gitee.starblues.extension.resources.thymeleaf; +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -import org.thymeleaf.templatemode.TemplateMode; +package com.gitee.starblues.spring.web.thymeleaf; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; /** + * 插件 Thymeleaf 配置 * @author starBlues - * @version 2.3 + * @version 3.0.0 */ public class ThymeleafConfig { - public static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8; public static final String DEFAULT_PREFIX = "templates/"; public static final String DEFAULT_SUFFIX = ".html"; + private boolean enabled = true; + /** * 存放模板引擎的前缀 */ @@ -30,9 +46,9 @@ public class ThymeleafConfig { /** * 模型引入的模型 - * @see TemplateMode + * HTML、XML、TEXT、JAVASCRIPT、CSS、RAW */ - private TemplateMode mode = TemplateMode.HTML; + private String mode = "HTML"; /** * 模板引擎的编码 @@ -49,6 +65,14 @@ public class ThymeleafConfig { */ private Integer templateResolverOrder; + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + public String getPrefix() { return prefix; } @@ -65,12 +89,12 @@ public class ThymeleafConfig { this.suffix = suffix; } - public TemplateMode getMode() { + public String getMode() { return mode; } public void setMode(String mode) { - this.mode = TemplateMode.parse(mode); + this.mode = mode; } public Charset getEncoding() { @@ -97,15 +121,4 @@ public class ThymeleafConfig { this.templateResolverOrder = templateResolverOrder; } - @Override - public String toString() { - return "ThymeleafConfig{" + - "prefix='" + prefix + '\'' + - ", suffix='" + suffix + '\'' + - ", mode=" + mode.name() + - ", encoding=" + encoding.name() + - ", cache=" + cache + - ", templateResolverOrder=" + templateResolverOrder + - '}'; - } } diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/AnnotationsUtils.java b/spring-brick/src/main/java/com/gitee/starblues/utils/AnnotationsUtils.java similarity index 65% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/AnnotationsUtils.java rename to spring-brick/src/main/java/com/gitee/starblues/utils/AnnotationsUtils.java index 7ed5143b0ae87c93575a26017f61eb24f6df5c7c..134725801d8b3f10615928b4d1dd49a191a82869 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/AnnotationsUtils.java +++ b/spring-brick/src/main/java/com/gitee/starblues/utils/AnnotationsUtils.java @@ -1,3 +1,19 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.gitee.starblues.utils; import java.lang.annotation.Annotation; @@ -6,7 +22,7 @@ import java.lang.annotation.Annotation; * 注解工具 * * @author starBlues - * @version 1.0 + * @version 3.0.0 */ public class AnnotationsUtils { diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/ClassUtils.java b/spring-brick/src/main/java/com/gitee/starblues/utils/ClassUtils.java similarity index 63% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/ClassUtils.java rename to spring-brick/src/main/java/com/gitee/starblues/utils/ClassUtils.java index 6d25abbf6d05132df65db82262e05ebf2b0dc790..4ca552718d4e1f74c5a205ad3317caed9b5b6a16 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/ClassUtils.java +++ b/spring-brick/src/main/java/com/gitee/starblues/utils/ClassUtils.java @@ -1,8 +1,21 @@ -package com.gitee.starblues.utils; +/** + * Copyright [2019-2022] [starBlues] + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -import org.springframework.util.ReflectionUtils; +package com.gitee.starblues.utils; -import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; @@ -13,18 +26,19 @@ import java.util.Map; /** * 类工具类 + * * @author starBlues * @version 2.4.0 */ public class ClassUtils { - private ClassUtils(){ + private ClassUtils() { } public static List getAllFields(Class clazz) { List fieldList = new ArrayList<>(); - while (clazz != null){ + while (clazz != null) { fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()))); clazz = clazz.getSuperclass(); } @@ -33,6 +47,7 @@ public class ClassUtils { /** * 通过反射获取字段 + * * @param o 对象 * @param fieldName 字段名称 * @param 字段类型 @@ -40,7 +55,7 @@ public class ClassUtils { * @throws IllegalAccessException 异常信息 */ public static T getReflectionField(Object o, String fieldName) throws IllegalAccessException { - if(o == null){ + if (o == null) { return null; } @@ -51,6 +66,7 @@ public class ClassUtils { /** * 通过反射获取字段 + * * @param o 对象 * @param fieldName 字段名称 * @param fieldClassType 字段类型 @@ -59,7 +75,7 @@ public class ClassUtils { * @throws IllegalAccessException 异常信息 */ public static T getReflectionField(Object o, String fieldName, Class fieldClassType) throws IllegalAccessException { - if(o == null){ + if (o == null) { return null; } Field templateResolversField = ReflectionUtils.findField(o.getClass(), @@ -69,34 +85,56 @@ public class ClassUtils { /** * 通过反射Field获取字段 + * * @param field Field字段 * @param o 当前对象 * @param 字段类型 * @return 字段值 * @throws IllegalAccessException 异常信息 */ + @SuppressWarnings("unchecked") public static T getReflectionField(Field field, Object o) throws IllegalAccessException { if (field == null) { return null; } - if(!field.isAccessible()){ - field.setAccessible(true); - } + field.setAccessible(true); Object fieldObject = field.get(o); return (T) fieldObject; } /** * 得到注解修改者 + * + * TODO 可能某个java版本不生效 * @param annotation 注解 * @return 修改者集合 * @throws Exception 异常 */ + @SuppressWarnings("unchecked") public static Map getAnnotationsUpdater(Object annotation) throws Exception { InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation); - Field field = invocationHandler.getClass().getDeclaredField("memberValues"); - field.setAccessible(true); + Field field = getAnnotationsUpdaterField(invocationHandler); + if (field == null) { + return null; + } return (Map) field.get(invocationHandler); } + private static Field getAnnotationsUpdaterField(InvocationHandler invocationHandler) { + Class aClass = invocationHandler.getClass(); + Field field = ReflectionUtils.findField(aClass, "memberValues", Map.class); + if (field == null) { + field = ReflectionUtils.findField(aClass, "valueCache", Map.class); + } + if (field == null) { + field = ReflectionUtils.findField(aClass, Map.class); + } + if (field != null) { + field.setAccessible(true); + return field; + } else { + return null; + } + } + } diff --git a/spring-brick/src/main/java/com/gitee/starblues/utils/LogUtils.java b/spring-brick/src/main/java/com/gitee/starblues/utils/LogUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..c637552d6c06fff9f3d8b7e4638a2129e54a1a4d --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/utils/LogUtils.java @@ -0,0 +1,34 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.utils; + +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import org.slf4j.Logger; + +/** + * @author starBlues + * @version 3.0.0 + */ +public class LogUtils { + + private LogUtils(){} + + public static void info(Logger logger, PluginDescriptor pluginDescriptor, String msg){ + logger.info("插件[{}]{}", MsgUtils.getPluginUnique(pluginDescriptor), msg); + } + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/utils/MsgUtils.java b/spring-brick/src/main/java/com/gitee/starblues/utils/MsgUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..ed4da06086fddbb1ad0529b4a8773ff8e26f0af5 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/utils/MsgUtils.java @@ -0,0 +1,49 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.utils; + + +import com.gitee.starblues.core.descriptor.PluginDescriptor; + +/** + * @author starBlues + * @version 3.0.0 + */ +public abstract class MsgUtils { + + private MsgUtils(){} + + public static String getPluginUnique(PluginDescriptor pluginDescriptor){ + return pluginDescriptor.getPluginId() + "@" + pluginDescriptor.getPluginVersion(); + } + + public static String getPluginUnique(String pluginId, String version){ + if(ObjectUtils.isEmpty(version)){ + return pluginId; + } + return pluginId + "@" + version; + } + + public static String getThrowableMsg(Throwable throwable){ + return ObjectUtils.isEmpty(throwable.getMessage()) ? "" : throwable.getMessage(); + } + + public static String getThrowableMsg(String message){ + return ObjectUtils.isEmpty(message) ? "" : message; + } + +} diff --git a/spring-brick/src/main/java/com/gitee/starblues/utils/Order.java b/spring-brick/src/main/java/com/gitee/starblues/utils/Order.java new file mode 100644 index 0000000000000000000000000000000000000000..904c60ede569b2ea4f24a05d01994b4a5f085809 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/utils/Order.java @@ -0,0 +1,33 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.utils; + +/** + * 排序接口 + * @author starBlues + * @version 1.0 + */ +public interface Order { + + /** + * 排序, 数字越大越先执行 + * @return OrderPriority + */ + OrderPriority order(); + + +} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/OrderPriority.java b/spring-brick/src/main/java/com/gitee/starblues/utils/OrderPriority.java similarity index 77% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/OrderPriority.java rename to spring-brick/src/main/java/com/gitee/starblues/utils/OrderPriority.java index 82b2551038605f22f7d87993610f2843c8cae0b7..e96f4eb3c73696f6fc766a4eab8a605164d968db 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/OrderPriority.java +++ b/spring-brick/src/main/java/com/gitee/starblues/utils/OrderPriority.java @@ -1,3 +1,19 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.gitee.starblues.utils; /** diff --git a/spring-brick/src/main/java/com/gitee/starblues/utils/OrderUtils.java b/spring-brick/src/main/java/com/gitee/starblues/utils/OrderUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..ed154202dd626ba6c2e95270a0cf2e1dac103001 --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/utils/OrderUtils.java @@ -0,0 +1,67 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.utils; + + +import java.util.Comparator; +import java.util.List; +import java.util.function.Function; + +/** + * 通用工具 + * + * @author starBlues + * @version 3.0.0 + */ +public class OrderUtils { + + private OrderUtils(){} + + /** + * list按照int排序. 数字越大, 越排在前面 + * @param list list集合 + * @param orderImpl 排序实现 + * @param T + * @return List + */ + public static List order(List list, Function orderImpl){ + if(list == null){ + return null; + } + list.sort(Comparator.comparing(orderImpl, Comparator.nullsLast(Comparator.reverseOrder()))); + return list; + } + + + /** + * 对 OrderPriority 进行排序操作 + * @param order OrderPriority + * @param 当前操作要被排序的bean + * @return Comparator + */ + public static Comparator orderPriority(final Function order){ + return Comparator.comparing(t -> { + OrderPriority orderPriority = order.apply(t); + if(orderPriority == null){ + orderPriority = OrderPriority.getLowPriority(); + } + return orderPriority.getPriority(); + }, Comparator.nullsLast(Comparator.reverseOrder())); + } + + +} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/PluginConfigUtils.java b/spring-brick/src/main/java/com/gitee/starblues/utils/PluginConfigUtils.java similarity index 56% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/PluginConfigUtils.java rename to spring-brick/src/main/java/com/gitee/starblues/utils/PluginConfigUtils.java index b9d73888a215ff6a131e8832e7890e288df0ae49..961ce0946b727253e1f9d3eac01ae047d6c61007 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/PluginConfigUtils.java +++ b/spring-brick/src/main/java/com/gitee/starblues/utils/PluginConfigUtils.java @@ -1,13 +1,28 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.gitee.starblues.utils; -import com.gitee.starblues.annotation.ConfigDefinition; -import org.pf4j.RuntimeMode; -import org.pf4j.util.StringUtils; +import com.gitee.starblues.core.RuntimeMode; +import com.gitee.starblues.integration.IntegrationConfiguration; /** * 插件配置工具类 * @author starBlues - * @version 2.4.3 + * @version 3.0.0 */ public class PluginConfigUtils { @@ -27,14 +42,14 @@ public class PluginConfigUtils { String prodSuffix, String devSuffix, RuntimeMode runtimeMode){ - if(StringUtils.isNullOrEmpty(fileName)){ + if(ObjectUtils.isEmpty(fileName)){ return null; } String suffix = ""; - if(runtimeMode == RuntimeMode.DEPLOYMENT){ + if(runtimeMode == RuntimeMode.PROD){ // 生产环境 suffix = prodSuffix; - } else if(runtimeMode == RuntimeMode.DEVELOPMENT){ + } else if(runtimeMode == RuntimeMode.DEV){ // 开发环境 suffix = devSuffix; } @@ -50,7 +65,7 @@ public class PluginConfigUtils { } public static String joinConfigFileName(String fileName, String suffix){ - if(StringUtils.isNullOrEmpty(fileName)){ + if(ObjectUtils.isEmpty(fileName)){ return null; } String fileNamePrefix; @@ -67,12 +82,35 @@ public class PluginConfigUtils { if(suffix == null){ suffix = ""; } - if(StringUtils.isNotNullOrEmpty(suffix) && !suffix.startsWith(DO)){ + if(ObjectUtils.isEmpty(suffix) && !suffix.startsWith(DO)){ suffix = DO + suffix; } return fileNamePrefix + suffix + fileNamePrefixSuffix; } + /** + * 得到插件接口前缀 + * @param configuration 配置 + * @param pluginId 插件id + * @return 接口前缀 + */ + public static String getPluginRestPrefix(IntegrationConfiguration configuration, String pluginId){ + String pathPrefix = configuration.pluginRestPathPrefix(); + if(configuration.enablePluginIdRestPathPrefix()){ + if(pathPrefix != null && !"".equals(pathPrefix)){ + pathPrefix = UrlUtils.restJoiningPath(pathPrefix, pluginId); + } else { + pathPrefix = pluginId; + } + return pathPrefix; + } else { + if(pathPrefix == null || "".equals(pathPrefix)){ + // 不启用插件id作为路径前缀, 并且路径前缀为空, 则直接返回。 + return null; + } + } + return pathPrefix; + } public static class FileNamePack { private final String sourceFileName; diff --git a/spring-brick/src/main/java/com/gitee/starblues/utils/PluginFileUtils.java b/spring-brick/src/main/java/com/gitee/starblues/utils/PluginFileUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..3e1423ea39e138dc36856d073b0fddb70ff2794d --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/utils/PluginFileUtils.java @@ -0,0 +1,223 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.utils; + + +import com.gitee.starblues.common.PackageStructure; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; + +import java.io.*; +import java.math.BigInteger; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.MessageDigest; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +/** + * 插件文件工具类 + * + * @author starBlues + * @version 3.0.0 + */ +public final class PluginFileUtils { + + private static final String FILE_POINT = "."; + + 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 { + IOUtils.closeQuietly(in); + } + return value; + } + + + public static void cleanEmptyFile(List paths){ + if(ObjectUtils.isEmpty(paths)){ + return; + } + for (String pathStr : paths) { + Path path = Paths.get(pathStr); + if(!Files.exists(path)){ + continue; + } + try { + Files.list(path) + .forEach(subPath -> { + File file = subPath.toFile(); + if(!file.isFile()){ + return; + } + long length = file.length(); + if(length == 0){ + try { + Files.deleteIfExists(subPath); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + + /** + * 如果文件不存在, 则会创建 + * @param path 插件路径 + * @return 插件路径 + * @throws IOException 没有发现文件异常 + */ + public static File createExistFile(Path path) throws IOException { + Path parent = path.getParent(); + if(!Files.exists(parent)){ + Files.createDirectories(parent); + } + if(!Files.exists(path)){ + Files.createFile(path); + } + return path.toFile(); + } + + /** + * 得到文件名称 + * @param file 原始文件 + * @return String + */ + public static String getFileName(File file){ + String fileName = file.getName(); + if(!file.exists() | file.isDirectory()){ + return fileName; + } + return getFileName(fileName); + } + + + /** + * 得到文件名称 + * @param fileName 原始文件名称. 比如: file.txt + * @return String + */ + public static String getFileName(String fileName){ + if(ObjectUtils.isEmpty(fileName)){ + return fileName; + } + if(fileName.lastIndexOf(FILE_POINT) > 0){ + return fileName.substring(0, fileName.lastIndexOf(FILE_POINT)); + } else { + return fileName; + } + } + + public static Manifest getManifest(InputStream inputStream) throws IOException { + Manifest manifest = new Manifest(); + Attributes attributes = manifest.getMainAttributes(); + List lines = IOUtils.readLines(inputStream, PackageStructure.CHARSET_NAME); + for (String line : lines) { + String[] split = line.split(":"); + if(split.length == 2){ + String key = split[0]; + String value = split[1]; + attributes.putValue(trim(key), trim(value)); + } + } + return manifest; + } + + private static String trim(String value){ + if(ObjectUtils.isEmpty(value)){ + return value; + } + return value.trim(); + } + + public static void deleteFile(File file) throws IOException { + if(file == null || !file.exists()){ + return; + } + if(file.isDirectory()){ + FileUtils.deleteDirectory(file); + } else { + FileUtils.delete(file); + } + } + + public static void decompressZip(String zipPath, String targetDir) throws IOException { + File zipFile = new File(zipPath); + if(!ResourceUtils.isZip(zipPath) && !ResourceUtils.isJar(zipPath)){ + throw new IOException("文件[" + zipFile.getName() + "]非压缩包, 不能解压"); + } + File targetDirFile = new File(targetDir); + if(!targetDirFile.exists()){ + targetDirFile.mkdirs(); + } + try (ZipFile zip = new ZipFile(zipFile, Charset.forName(PackageStructure.CHARSET_NAME))) { + Enumeration zipEnumeration = zip.entries(); + ZipEntry zipEntry = null; + while (zipEnumeration.hasMoreElements()) { + zipEntry = zipEnumeration.nextElement(); + String zipEntryName = zipEntry.getName(); + String currentZipPath = PackageStructure.resolvePath(zipEntryName); + String currentTargetPath = FilesUtils.joiningFilePath(targetDir, currentZipPath); + //判断路径是否存在,不存在则创建文件路径 + if (zipEntry.isDirectory()) { + FileUtils.forceMkdir(new File(currentTargetPath)); + continue; + } + InputStream in = null; + FileOutputStream out = null; + try { + in = zip.getInputStream(zipEntry); + out = new FileOutputStream(currentTargetPath); + IOUtils.copy(in, out); + } finally { + if (in != null) { + IOUtils.closeQuietly(in); + } + if (out != null) { + IOUtils.closeQuietly(out); + } + } + } + } + } + +} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/ScanUtils.java b/spring-brick/src/main/java/com/gitee/starblues/utils/ScanUtils.java similarity index 76% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/ScanUtils.java rename to spring-brick/src/main/java/com/gitee/starblues/utils/ScanUtils.java index ffc0ff692bb255480b2996402be05df62cd92b99..d80fed911a473b5fccb8a342983b9cd388644a02 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/ScanUtils.java +++ b/spring-brick/src/main/java/com/gitee/starblues/utils/ScanUtils.java @@ -1,7 +1,23 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.gitee.starblues.utils; -import org.pf4j.PluginWrapper; +import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.util.ClassUtils; import java.io.File; @@ -45,6 +61,22 @@ public class ScanUtils { } } + /** + * 得到扫描的包 + * @param pluginClass 插件入口class + * @return 包集合 + */ + public static String[] getScanBasePackages(Class pluginClass){ + SpringBootApplication springBootApplication = pluginClass.getAnnotation(SpringBootApplication.class); + if(springBootApplication != null){ + String[] scanBasePackages = springBootApplication.scanBasePackages(); + if(scanBasePackages.length > 0){ + return scanBasePackages; + } + } + return new String[]{ pluginClass.getPackage().getName() }; + } + /** * 扫描windows环境下的类。包括子包中的类 * @param basePackage 包名 @@ -114,31 +146,4 @@ public class ScanUtils { }); } - - /** - * 扫描jar包中的类。 - * - * @param basePackage 包名 - * @param pluginWrapper jar的PluginWrapper - * @return 类全路径 - * @throws IOException 扫描异常 - */ - public static Set scanClassPackageName(String basePackage, PluginWrapper pluginWrapper) throws IOException { - String pluginPath = pluginWrapper.getPluginPath().toString(); - Set classPackageNames = new HashSet<>(); - try (JarFile jarFile = new JarFile(pluginPath)) { - Enumeration jarEntries = jarFile.entries(); - while (jarEntries.hasMoreElements()) { - JarEntry entry = jarEntries.nextElement(); - String jarEntryName = entry.getName(); - if (jarEntryName.contains(".class") && jarEntryName.replaceAll("/", ".").startsWith(basePackage)) { - String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replace("/", "."); - classPackageNames.add(className); - } - } - } - return classPackageNames; - } - - } diff --git a/spring-brick/src/main/java/com/gitee/starblues/utils/SpringBeanCustomUtils.java b/spring-brick/src/main/java/com/gitee/starblues/utils/SpringBeanCustomUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..f44d7455d3dce7f3750a43aa9fa12a2bf260fc7a --- /dev/null +++ b/spring-brick/src/main/java/com/gitee/starblues/utils/SpringBeanCustomUtils.java @@ -0,0 +1,110 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gitee.starblues.utils; + +import com.gitee.starblues.spring.ApplicationContext; +import com.gitee.starblues.spring.SpringBeanFactory; +import org.springframework.util.ClassUtils; + +import java.lang.annotation.Annotation; +import java.util.*; + +/** + * 自定义插件bean工具类 + * @author starBlues + * @version 3.0.0 + */ +public class SpringBeanCustomUtils { + + /** + * 获取bean名称 + * @param applicationContext ApplicationContext + * @return bean名称集合 + */ + public static Set getBeanName(ApplicationContext applicationContext){ + SpringBeanFactory springBeanFactory = applicationContext.getSpringBeanFactory(); + String[] beanDefinitionNames = springBeanFactory.getBeanDefinitionNames(); + Set set = new HashSet<>(beanDefinitionNames.length); + set.addAll(Arrays.asList(beanDefinitionNames)); + return set; + } + + /** + * 得到ApplicationContext中的bean的实现 + * @param applicationContext applicationContext + * @param aClass 接口或者抽象类型bean类型 + * @param 接口或者抽象类型bean类型 + * @return 所有的实现对象 + */ + public static List getBeans(ApplicationContext applicationContext, Class aClass) { + SpringBeanFactory springBeanFactory = applicationContext.getSpringBeanFactory(); + Map beansOfTypeMap = springBeanFactory.getBeansOfType(aClass); + if(beansOfTypeMap.isEmpty()){ + return new ArrayList<>(); + } + return new ArrayList<>(beansOfTypeMap.values()); + } + + /** + * 得到存在的bean, 不存在则返回null + * @param applicationContext applicationContext + * @param aClass bean 类型 + * @param bean 类型 + * @return 存在bean对象, 不存在返回null + */ + public static T getExistBean(ApplicationContext applicationContext, Class aClass){ + SpringBeanFactory springBeanFactory = applicationContext.getSpringBeanFactory(); + String[] beanNamesForType = springBeanFactory.getBeanNamesForType(aClass, false, false); + if(beanNamesForType.length > 0){ + return springBeanFactory.getBean(aClass); + } else { + return null; + } + } + + /** + * 得到存在的bean, 不存在则返回null + * @param applicationContext applicationContext + * @param beanName bean 名称 + * @param 返回的bean类型 + * @return 存在bean对象, 不存在返回null + */ + @SuppressWarnings("unchecked") + public static T getExistBean(ApplicationContext applicationContext, String beanName){ + SpringBeanFactory springBeanFactory = applicationContext.getSpringBeanFactory(); + if(springBeanFactory.containsBean(beanName)){ + Object bean = springBeanFactory.getBean(beanName); + return (T) bean; + } else { + return null; + } + } + + /** + * 通过注解获取bean + * @param applicationContext applicationContext + * @param annotationType 注解类型 + * @return List + */ + public static List getBeansWithAnnotation(ApplicationContext applicationContext, + Class annotationType){ + SpringBeanFactory springBeanFactory = applicationContext.getSpringBeanFactory(); + Map beanMap = springBeanFactory.getBeansWithAnnotation(annotationType); + return new ArrayList<>(beanMap.values()); + } + +} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/SpringBeanUtils.java b/spring-brick/src/main/java/com/gitee/starblues/utils/SpringBeanUtils.java similarity index 53% rename from springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/SpringBeanUtils.java rename to spring-brick/src/main/java/com/gitee/starblues/utils/SpringBeanUtils.java index 33d4fb5fa3813760c12fc1d442330d6d4ff1f3a8..8a971c90a7660d0c9007273215a3a3ff01cd53c5 100644 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/SpringBeanUtils.java +++ b/spring-brick/src/main/java/com/gitee/starblues/utils/SpringBeanUtils.java @@ -1,17 +1,45 @@ +/** + * Copyright [2019-2022] [starBlues] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.gitee.starblues.utils; import org.springframework.context.ApplicationContext; -import org.springframework.util.ClassUtils; +import java.lang.annotation.Annotation; import java.util.*; /** * 插件bean工具类 * @author starBlues - * @version 2.4.0 + * @version 3.0.0 */ public class SpringBeanUtils { + /** + * 获取bean名称 + * @param applicationContext ApplicationContext + * @return bean名称集合 + */ + public static Set getBeanName(ApplicationContext applicationContext){ + String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); + Set set = new HashSet<>(beanDefinitionNames.length); + set.addAll(Arrays.asList(beanDefinitionNames)); + return set; + } + /** * 得到ApplicationContext中的bean的实现 * @param applicationContext ApplicationContext @@ -22,51 +50,11 @@ public class SpringBeanUtils { public static List getBeans(ApplicationContext applicationContext, Class aClass) { Map beansOfTypeMap = applicationContext.getBeansOfType(aClass); if(beansOfTypeMap.isEmpty()){ - return Collections.emptyList(); + return new ArrayList<>(); } return new ArrayList<>(beansOfTypeMap.values()); } - /** - * 得到某个接口的实现对象 - * @param sourceObject 遍历的对象 - * @param interfaceClass 接口类类型 - * @param 接口类型 - * @return 实现对象 - */ - public static T getObjectByInterfaceClass(Set sourceObject, Class interfaceClass){ - if(sourceObject == null || sourceObject.isEmpty()){ - return null; - } - for (Object configSingletonObject : sourceObject) { - Set> allInterfacesForClassAsSet = ClassUtils - .getAllInterfacesAsSet(configSingletonObject); - if(allInterfacesForClassAsSet.contains(interfaceClass)){ - return (T) configSingletonObject; - } - } - return null; - } - - /** - * 获取具体类的对象 - * @param sourceObject 源对象集合 - * @param aClass 对象对应的类类型 - * @param 类实现 - * @return T - */ - public static T getObjectClass(Set sourceObject, Class aClass){ - if(sourceObject == null || sourceObject.isEmpty()){ - return null; - } - for (Object configSingletonObject : sourceObject) { - if(Objects.equals(configSingletonObject.getClass(), aClass)){ - return (T) configSingletonObject; - } - } - return null; - } - /** * 得到存在的bean, 不存在则返回null * @param applicationContext ApplicationContext容器 @@ -90,6 +78,7 @@ public class SpringBeanUtils { * @param 返回的bean类型 * @return 存在bean对象, 不存在返回null */ + @SuppressWarnings("unchecked") public static T getExistBean(ApplicationContext applicationContext, String beanName){ if(applicationContext.containsBean(beanName)){ Object bean = applicationContext.getBean(beanName); @@ -99,4 +88,16 @@ public class SpringBeanUtils { } } + /** + * 通过注解获取bean + * @param applicationContext applicationContext + * @param annotationType 注解类型 + * @return List + */ + public static List getBeansWithAnnotation(ApplicationContext applicationContext, + Class annotationType){ + Map beanMap = applicationContext.getBeansWithAnnotation(annotationType); + return new ArrayList<>(beanMap.values()); + } + } diff --git a/springboot-plugin-framework/src/main/resources/META-INF/spring-configuration-metadata.json b/spring-brick/src/main/resources/META-INF/spring-configuration-metadata.json similarity index 78% rename from springboot-plugin-framework/src/main/resources/META-INF/spring-configuration-metadata.json rename to spring-brick/src/main/resources/META-INF/spring-configuration-metadata.json index 0a221d1d8e6bdee6ec0b7e7ab578f0a12bf65bff..4eec8b41335758607b41fd0d35692123bbfe07dd 100644 --- a/springboot-plugin-framework/src/main/resources/META-INF/spring-configuration-metadata.json +++ b/spring-brick/src/main/resources/META-INF/spring-configuration-metadata.json @@ -7,13 +7,6 @@ } ], "properties": [ - { - "name": "plugin.runMode", - "type": "java.lang.String", - "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", - "description": "运行模式. 开发环境: development、dev; 生产/部署 环境: deployment、prod", - "defaultValue": "dev" - }, { "name": "plugin.enable", "type": "java.lang.Boolean", @@ -22,39 +15,32 @@ "defaultValue": true }, { - "name": "plugin.pluginPath", - "type": "java.lang.String", + "name": "plugin.enableStarter", + "type": "java.lang.Boolean", "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", - "description": "插件的路径. 开发环境下配置为插件模块上级目录; 生产环境下配置到插件jar包存放目录。建议配置绝对路径", - "defaultValue": "/plugins" + "description": "是否启用starter自动装配功能", + "defaultValue": true }, { - "name": "plugin.pluginConfigFilePath", + "name": "plugin.runMode", "type": "java.lang.String", "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", - "description": "插件对应的配置文件存放目录, 只作用于生产环境下", - "defaultValue": "/plugin-config" + "description": "运行模式. 开发环境: dev; 生产/部署 环境: prod", + "defaultValue": "dev" }, { - "name": "plugin.pluginRestPathPrefix", + "name": "plugin.mainPackage", "type": "java.lang.String", "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", - "description": "统一配置访问插件rest接口前缀", - "defaultValue": "/plugins" - }, - { - "name": "plugin.pluginRestPathPrefix", - "type": "java.lang.Boolean", - "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", - "description": "是否启用插件id作为rest接口前缀", + "description": "主程序包名, 包名建议设置到范围最大级别, 主要用于插件依赖主程序时的类时, 进行包名匹配", "defaultValue": true }, { - "name": "plugin.enableSwaggerRefresh", - "type": "java.lang.Boolean", + "name": "plugin.pluginPath", + "type": "java.lang.String", "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", - "description": "是否启用Swagger刷新机制", - "defaultValue": true + "description": "插件的路径. 开发环境下配置为插件模块上级目录; 生产环境下配置到插件jar包存放目录。建议配置绝对路径", + "defaultValue": "~/plugins/" }, { "name": "plugin.backupPath", @@ -69,25 +55,18 @@ "description": "上传的插件所存储的临时目录" }, { - "name": "plugin.version", - "type": "java.lang.String", - "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", - "description": "当前主程序的版本号, 用于校验插件是否可安装。插件中可通过插件配置信息 requires 来指定可安装的主程序版本。如果为: 0.0.0 的话, 表示不校验", - "defaultValue": "0.0.0" - }, - { - "name": "plugin.exactVersionAllowed", + "name": "plugin.pluginRestPathPrefix", "type": "java.lang.String", "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", - "description": "是否完全匹配版本。设置为true表示插件设置的requires的版本号完全匹配version版本号才可允许插件安装, 即: requires=x.y.z; 设置为false表示插件设置的requires的版本号小于等于version值, 插件就可安装, 即requires<=x.y.z", - "defaultValue": false + "description": "统一配置访问插件rest接口前缀", + "defaultValue": "/plugins" }, { - "name": "plugin.stopDependents", - "type": "java.lang.String", + "name": "plugin.enablePluginIdRestPathPrefix", + "type": "java.lang.Boolean", "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", - "description": "停止插件时, 是否停止当前插件依赖的插件", - "defaultValue": false + "description": "是否启用插件id作为rest接口前缀", + "defaultValue": true }, { "name": "plugin.enablePluginIds", @@ -108,10 +87,17 @@ "description": "设置初始化时插件启动的顺序" }, { - "name": "plugin.enableWebSocket", - "type": "java.lang.Boolean", + "name": "plugin.version", + "type": "java.lang.String", "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", - "description": "是否启用webSocket的功能. 默认禁用", + "description": "当前主程序的版本号, 用于校验插件是否可安装。插件中可通过插件配置信息 requires 来指定可安装的主程序版本。如果为: 0.0.0 的话, 表示不校验", + "defaultValue": "0.0.0" + }, + { + "name": "plugin.exactVersion", + "type": "java.lang.String", + "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", + "description": "是否完全匹配版本。设置为true表示插件设置的requires的版本号完全匹配version版本号才可允许插件安装, 即: requires=x.y.z; 设置为false表示插件设置的requires的版本号小于等于version值, 插件就可安装, 即requires<=x.y.z", "defaultValue": false } ] diff --git a/spring-brick/src/main/resources/META-INF/spring.factories b/spring-brick/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000000000000000000000000000000000000..cc6e350e7253e234d2dc700b2c40a642dfea1f9b --- /dev/null +++ b/spring-brick/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.gitee.starblues.integration.SpringBootPluginStarter \ No newline at end of file diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/pom.xml b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/pom.xml deleted file mode 100644 index 31a1b6a548522329c1a851057699924bce94168f..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/pom.xml +++ /dev/null @@ -1,213 +0,0 @@ - - - - org.sonatype.oss - oss-parent - 7 - - - 4.0.0 - - com.gitee.starblues - springboot-plugin-framework-extension-log - 2.4.6-RELEASE - - 插件扩展-日志模块扩展 - - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - repo - - - - - https://gitee.com/starblues/springboot-plugin-framework-parent - scm:https://gitee.com/starblues/springboot-plugin-framework-parent.git - scm:https://gitee.com/starblues/springboot-plugin-framework-parent.git - 1.0 - - - - - sonatype-nexus-snapshots - oss Snapshots Repository - https://oss.sonatype.org/content/repositories/snapshots - - - sonatype-nexus-staging - oss Staging Repository - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - - - sousouki - caoshx@outlook.com - https://gitee.com/caoshx_sousouki/ - - - - - 1.8 - UTF-8 - - 3.8.1 - 3.1.0 - 3.1.0 - 3.1.0 - 1.6 - - 2.4.6-RELEASE - 5.0.7.RELEASE - 1.2.3 - 2.14.1 - - - - - ch.qos.logback - logback-classic - ${logback.version} - provided - - - org.apache.logging.log4j - log4j-core - ${log4j.version} - provided - - - org.springframework - spring-context - ${spring-version} - provided - - - com.gitee.starblues - springboot-plugin-framework - ${springboot-plugin-framework.version} - provided - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - ${java.version} - ${java.version} - - - - - org.apache.maven.plugins - maven-assembly-plugin - ${maven-assembly-plugin.version} - - - jar-with-dependencies - - ${project.artifactId}-${project.version} - false - false - - - true - true - - - - - - make-assembly - package - - single - - - - - - - org.apache.maven.plugins - maven-source-plugin - ${maven-source-plugin.version} - - - package - - jar-no-fork - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - ${maven-javadoc-plugin.version} - - ${plugin.skip} - - - - package - - jar - - - - - - - org.apache.maven.plugins - maven-gpg-plugin - ${maven-gpg-plugin.version} - - ${plugin.skip} - - - - sign-artifacts - verify - - sign - - - - - - - - - - - - - dev - - true - - - true - - - - - release - - false - - - - - \ No newline at end of file diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/LogRegistry.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/LogRegistry.java deleted file mode 100644 index aab1010249d0566d375b53eda64715c85f33d5c0..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/LogRegistry.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.gitee.starblues.extension.log; - -import com.gitee.starblues.factory.PluginRegistryInfo; -import org.pf4j.PluginWrapper; -import org.springframework.core.io.Resource; - -import java.util.List; - -/** - * 日志注册统一接口 - * @author starBlues - * @version 2.4.3 - */ -public interface LogRegistry { - - /** - * 注册日志 - * @param resources 日志配置文件资源 - * @param pluginRegistryInfo 当前插件的信息 - * @throws Exception 注册异常 - **/ - void registry(List resources, PluginRegistryInfo pluginRegistryInfo) throws Exception; - - /** - * 注册日志 - * @param pluginRegistryInfo 当前插件的信息 - * @throws Exception 卸载异常 - **/ - void unRegistry(PluginRegistryInfo pluginRegistryInfo) throws Exception; - -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/PluginLogConfigProcessor.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/PluginLogConfigProcessor.java deleted file mode 100644 index 9659cc75c6064c951a154ae2c23c0cb5f40321d7..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/PluginLogConfigProcessor.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.gitee.starblues.extension.log; - -import com.gitee.starblues.extension.log.log4j.Log4jLogRegistry; -import com.gitee.starblues.extension.log.logback.LogbackLogRegistry; -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.pipe.PluginPipeProcessorExtend; -import com.gitee.starblues.utils.OrderPriority; -import com.gitee.starblues.utils.ResourceUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.FileSystemResource; -import org.springframework.core.io.Resource; -import org.springframework.util.ObjectUtils; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * 接口处理者 - * @author sousouki - * @version 2.4.3 - */ -class PluginLogConfigProcessor implements PluginPipeProcessorExtend { - - private final static Logger LOG = LoggerFactory.getLogger(PluginLogConfigProcessor.class); - private final LogRegistry logRegistry; - - public PluginLogConfigProcessor(SpringBootLogExtension.Type type){ - if(type == SpringBootLogExtension.Type.LOG4J){ - logRegistry = new Log4jLogRegistry(); - } else if(type == SpringBootLogExtension.Type.LOGBACK){ - logRegistry = new LogbackLogRegistry(); - } else { - logRegistry = null; - } - } - - @Override - public String key() { - return "SpringBootLogConfigProcessor"; - } - - @Override - public OrderPriority order() { - return OrderPriority.getLowPriority(); - } - - @Override - public void initialize() throws Exception { - - } - - @Override - public void registry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - if (logRegistry == null) { - return; - } - Resource resource = getLogConfigFile(pluginRegistryInfo); - List resources = new ArrayList<>(1); - resources.add(resource); - logRegistry.registry(resources, pluginRegistryInfo); - } - - @Override - public void unRegistry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - if (logRegistry == null) { - return; - } - logRegistry.unRegistry(pluginRegistryInfo); - } - - /** - * 加载日志配置文件资源 - * 文件路径配置为

file:D://log.xml


- * resources路径配置为

classpath:log.xml


- * @param pluginRegistryInfo 当前插件注册的信息 - * @throws IOException 获取不到配置文件异常 - **/ - private Resource getLogConfigFile(PluginRegistryInfo pluginRegistryInfo) throws IOException { - GenericApplicationContext pluginApplicationContext = pluginRegistryInfo.getPluginApplicationContext(); - String logConfigLocation = pluginApplicationContext.getEnvironment() - .getProperty(PropertyKey.LOG_CONFIG_LOCATION); - if (ObjectUtils.isEmpty(logConfigLocation)) { - return null; - } - String pluginId = pluginRegistryInfo.getPluginWrapper().getPluginId(); - String matchLocation = ResourceUtils.getMatchLocation(logConfigLocation); - if (matchLocation == null || "".equals(matchLocation)) { - LOG.warn("Plugin '{}' not match {}: {}", pluginId, PropertyKey.LOG_CONFIG_LOCATION, - logConfigLocation); - return null; - } - if(ResourceUtils.isFile(logConfigLocation)){ - String absolutePath = ResourceUtils.getAbsolutePath(pluginRegistryInfo, matchLocation); - return new FileSystemResource(absolutePath); - } else { - return new ClassPathResource(matchLocation, pluginRegistryInfo.getPluginClassLoader()); - } - } - -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/PropertyKey.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/PropertyKey.java deleted file mode 100644 index 2f48a09de272ccd678829fdc241a50a5709ea896..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/PropertyKey.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.gitee.starblues.extension.log; - -/** - * 配置文件key - * @author starBlues - * @version 2.4.3 - */ -public class PropertyKey { - - public final static String LOG_CONFIG_LOCATION = "plugin.log-config-location"; - -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/SpringBootLogExtension.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/SpringBootLogExtension.java deleted file mode 100644 index 93cb475eafd74ecba1be70a94ea07fc23409635b..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/SpringBootLogExtension.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.gitee.starblues.extension.log; - - -import com.gitee.starblues.extension.AbstractExtension; -import com.gitee.starblues.factory.process.pipe.PluginPipeProcessorExtend; -import org.springframework.context.ApplicationContext; - -import java.util.ArrayList; -import java.util.List; - -/** - * 日志扩展 - * @author sousouki starBlues - * @version 2.4.3 - */ -public class SpringBootLogExtension extends AbstractExtension { - - private static final String KEY = "SpringBootLogExtension"; - - private final Type type; - - public SpringBootLogExtension(Type type){ - this.type = type; - } - - - @Override - public String key() { - return KEY; - } - - @Override - public List getPluginPipeProcessor(ApplicationContext mainApplicationContext) { - List pipeProcessorExtends = new ArrayList<>(); - pipeProcessorExtends.add(new PluginLogConfigProcessor(type)); - return pipeProcessorExtends; - } - - public enum Type{ - /** - * 集成log4j - **/ - LOG4J, - - /** - * 集成 logback - **/ - LOGBACK - } -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/annotation/ConfigItem.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/annotation/ConfigItem.java deleted file mode 100644 index 38d98117edca3edbadab1c279f4af1763491b646..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/annotation/ConfigItem.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.gitee.starblues.extension.log.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 配置的默认值注解 - * @author sousouki - * @version 2.4.3 - */ -@Documented -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.RUNTIME) -public @interface ConfigItem { - - - /** - * 默认值 - * @return String - */ - String defaultValue() default ""; - -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/config/LogConfig.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/config/LogConfig.java deleted file mode 100644 index 596d4e9012397179b672f485816ef60069014960..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/config/LogConfig.java +++ /dev/null @@ -1,140 +0,0 @@ -package com.gitee.starblues.extension.log.config; - -import com.gitee.starblues.extension.log.annotation.ConfigItem; - -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlTransient; - -/** - * 日志配置 - * @author sousouki - * @version 2.4.3 - */ -@XmlRootElement(name = "log") -public class LogConfig { - - public static final String ROOT_PLUGIN_SIGN = "~"; - - /** - * 日志存储根目录,默认为当前插件存放目录。 - * ~: 符号表示当前插件根目录 - **/ - @XmlElement(name = "rootDir") - @ConfigItem(defaultValue = ROOT_PLUGIN_SIGN + "/logs/") - private String rootDir; - - - /** - * 日志文件名称 - **/ - @XmlElement(name = "fileName") - private String fileName; - - /** - * 日志级别 - **/ - @XmlElement(name = "level") - @ConfigItem(defaultValue = "INFO") - private String level; - - /** - * 日志文件最大容量 - **/ - @XmlElement(name = "maxFileSize") - @ConfigItem(defaultValue = "10MB") - private String maxFileSize; - - /** - * 日志文件总容量 - **/ - @XmlElement(name = "totalFileSize") - @ConfigItem(defaultValue = "10GB") - private String totalFileSize; - - /** - * 最大保存时间 - **/ - @XmlElement(name = "maxHistory") - @ConfigItem(defaultValue = "30") - private Integer maxHistory; - - /** - * 日志内容格式 - **/ - @XmlElement(name = "pattern") - @ConfigItem(defaultValue = "%d{yyyy-MM-dd HH:mm:ss.SSS} -%5p --- [%t] %-40.40logger{39} : %m%n") - private String pattern; - - /** - * 包名, 自定义当前插件的日志包名, 默认为 BasePlugin 实现类的 包名 - **/ - @XmlTransient - private String packageName; - - @XmlTransient - public String getRootDir() { - return rootDir; - } - - public void setRootDir(String rootDir) { - this.rootDir = rootDir; - } - - @XmlTransient - public String getFileName() { - return fileName; - } - - public void setFileName(String fileName) { - this.fileName = fileName; - } - @XmlTransient - public String getLevel() { - return level; - } - - public void setLevel(String level) { - this.level = level; - } - @XmlTransient - public String getMaxFileSize() { - return maxFileSize; - } - - public void setMaxFileSize(String maxFileSize) { - this.maxFileSize = maxFileSize; - } - @XmlTransient - public String getTotalFileSize() { - return totalFileSize; - } - - public void setTotalFileSize(String totalFileSize) { - this.totalFileSize = totalFileSize; - } - @XmlTransient - public Integer getMaxHistory() { - return maxHistory; - } - - public void setMaxHistory(Integer maxHistory) { - this.maxHistory = maxHistory; - } - @XmlTransient - public String getPattern() { - return pattern; - } - - public void setPattern(String pattern) { - this.pattern = pattern; - } - @XmlTransient - public String getPackageName() { - return packageName; - } - - public void setPackageName(String packageName) { - this.packageName = packageName; - } -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/log4j/Log4jLogRegistry.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/log4j/Log4jLogRegistry.java deleted file mode 100644 index c314075af12fa6065153f51580a9bb492318d31d..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/log4j/Log4jLogRegistry.java +++ /dev/null @@ -1,209 +0,0 @@ -package com.gitee.starblues.extension.log.log4j; - -import com.gitee.starblues.extension.log.LogRegistry; -import com.gitee.starblues.extension.log.config.LogConfig; -import com.gitee.starblues.extension.log.logback.LogbackLogRegistry; -import com.gitee.starblues.extension.log.util.LogConfigUtil; -import com.gitee.starblues.factory.PluginRegistryInfo; -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Marker; -import org.apache.logging.log4j.core.*; -import org.apache.logging.log4j.core.appender.RollingFileAppender; -import org.apache.logging.log4j.core.appender.rolling.*; -import org.apache.logging.log4j.core.config.Configuration; -import org.apache.logging.log4j.core.config.DefaultConfiguration; -import org.apache.logging.log4j.core.config.LoggerConfig; -import org.apache.logging.log4j.core.filter.AbstractFilter; -import org.apache.logging.log4j.core.layout.PatternLayout; -import org.apache.logging.log4j.message.Message; -import org.pf4j.PluginWrapper; -import org.slf4j.LoggerFactory; -import org.springframework.core.io.Resource; - -import java.nio.charset.Charset; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -/** - * 日志注册统一接口 - * @author starBlues - * @version 2.4.3 - */ -public class Log4jLogRegistry implements LogRegistry { - - private final org.slf4j.Logger log = LoggerFactory.getLogger(LogbackLogRegistry.class); - private final Map> pluginAppenderInfo = new ConcurrentHashMap<>(); - - - @Override - public void registry(List resources, PluginRegistryInfo pluginRegistryInfo) throws Exception { - PluginWrapper pluginWrapper = pluginRegistryInfo.getPluginWrapper(); - LoggerContext loggerContext = (LoggerContext)LogManager.getContext(false); - Configuration configuration = loggerContext.getConfiguration(); - LoggerConfig rootLogger = configuration.getRootLogger(); - Set allAppender = new HashSet<>(); - for (Resource resource : resources) { - if(resource == null){ - continue; - } - LogConfig logConfig; - try { - logConfig = LogConfigUtil.getLogConfig(resource, pluginRegistryInfo); - } catch (Exception e){ - log.error("Failed to read log configuration.", e); - continue; - } - Set appenderSet = getAppender(pluginRegistryInfo, logConfig); - for (Appender appender : appenderSet) { - configuration.addAppender(appender); - rootLogger.addAppender(appender, Level.toLevel(logConfig.getLevel()), null); - allAppender.add(appender); - } - } - pluginAppenderInfo.put(pluginWrapper.getPluginId(), allAppender); - } - - - private Set getAppender(PluginRegistryInfo pluginRegistryInfo, LogConfig logConfig){ - PluginWrapper pluginWrapper = pluginRegistryInfo.getPluginWrapper(); - Filter filter = new LogFilter(pluginRegistryInfo.getBasePlugin().scanPackage()); - PatternLayout patternLayout = PatternLayout.newBuilder() - .withPattern(logConfig.getPattern()) - .withCharset(Charset.defaultCharset()) - .build(); - - final TriggeringPolicy policy = - CompositeTriggeringPolicy.createPolicy( - SizeBasedTriggeringPolicy.createPolicy( - logConfig.getMaxFileSize() - ), - TimeBasedTriggeringPolicy.createPolicy("1", "true") - ); - - RolloverStrategy strategy = DefaultRolloverStrategy.newBuilder() - .withFileIndex(logConfig.getTotalFileSize()) - .withConfig(new DefaultConfiguration()) - .withMax(String.valueOf(logConfig.getMaxHistory())) - .build(); - - RollingFileAppender appender = RollingFileAppender.newBuilder() - .withFilter(filter) - .withName(pluginWrapper.getPluginId()) - .withLayout(patternLayout) - .withIgnoreExceptions(false) - .withFileName(LogConfigUtil.getLogFile(pluginRegistryInfo, logConfig).concat(".log")) - .withFilePattern(".%d{yyyy-MM-dd}-%i.log") - .withAppend(true) - .withPolicy(policy) - .withStrategy(strategy) - .build(); - - appender.start(); - filter.start(); - - return Collections.singleton(appender); - } - - @Override - public void unRegistry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - Set allAppender = pluginAppenderInfo.get(pluginRegistryInfo.getPluginWrapper().getPluginId()); - if(allAppender == null || allAppender.isEmpty()){ - return; - } - LoggerContext loggerContext = (LoggerContext)LogManager.getContext(false); - Configuration configuration = loggerContext.getConfiguration(); - for (Appender appender : allAppender) { - configuration.getAppenders().remove(appender.getName()); - } - } - - private static class LogFilter extends AbstractFilter{ - - private final String packageName; - - private LogFilter(String packageName) { - this.packageName = packageName; - } - - @Override - public Result filter(Logger logger, Level level, Marker marker, String msg, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6, Object p7, Object p8, Object p9) { - return filter(logger.getName()); - } - - @Override - public Result filter(LogEvent event) { - return filter(event.getLoggerName()); - } - - @Override - public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) { - return filter(logger.getName()); - } - - @Override - public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) { - return filter(logger.getName()); - } - - @Override - public Result filter(Logger logger, Level level, Marker marker, String msg, Object... params) { - return filter(logger.getName()); - } - - @Override - public Result filter(Logger logger, Level level, Marker marker, String msg, Object p0) { - return filter(logger.getName()); - } - - @Override - public Result filter(Logger logger, Level level, Marker marker, String msg, Object p0, Object p1) { - return filter(logger.getName()); - } - - @Override - public Result filter(Logger logger, Level level, Marker marker, String msg, Object p0, Object p1, Object p2) { - return filter(logger.getName()); - } - - @Override - public Result filter(Logger logger, Level level, Marker marker, String msg, Object p0, Object p1, Object p2, Object p3) { - return filter(logger.getName()); - } - - @Override - public Result filter(Logger logger, Level level, Marker marker, String msg, Object p0, Object p1, Object p2, Object p3, Object p4) { - return filter(logger.getName()); - } - - @Override - public Result filter(Logger logger, Level level, Marker marker, String msg, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5) { - return filter(logger.getName()); - } - - @Override - public Result filter(Logger logger, Level level, Marker marker, String msg, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6) { - return filter(logger.getName()); - } - - @Override - public Result filter(Logger logger, Level level, Marker marker, String msg, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6, Object p7) { - return filter(logger.getName()); - } - - @Override - public Result filter(Logger logger, Level level, Marker marker, String msg, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6, Object p7, Object p8) { - return filter(logger.getName()); - } - - private Result filter(String loggerName){ - if (loggerName.startsWith(packageName)) { - return Result.ACCEPT; - } - return Result.DENY; - } - } - - - -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/logback/LogbackLogRegistry.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/logback/LogbackLogRegistry.java deleted file mode 100644 index d81d723cc387a62018d2ee52fd603bd6d596d79c..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/logback/LogbackLogRegistry.java +++ /dev/null @@ -1,185 +0,0 @@ -package com.gitee.starblues.extension.log.logback; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.encoder.PatternLayoutEncoder; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.Appender; -import ch.qos.logback.core.ConsoleAppender; -import ch.qos.logback.core.filter.Filter; -import ch.qos.logback.core.rolling.RollingFileAppender; -import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy; -import ch.qos.logback.core.spi.FilterReply; -import ch.qos.logback.core.util.FileSize; -import ch.qos.logback.core.util.OptionHelper; -import com.gitee.starblues.extension.log.LogRegistry; -import com.gitee.starblues.extension.log.config.LogConfig; -import com.gitee.starblues.extension.log.util.LogConfigUtil; -import com.gitee.starblues.factory.PluginRegistryInfo; -import org.pf4j.PluginWrapper; -import org.pf4j.util.StringUtils; -import org.slf4j.LoggerFactory; -import org.springframework.core.io.Resource; - -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -/** - * 日志配置处理者 - * @author sousouki - * @version 2.4.3 - */ -public class LogbackLogRegistry implements LogRegistry { - - private final org.slf4j.Logger log = LoggerFactory.getLogger(LogbackLogRegistry.class); - - private final Map>> pluginAppenderInfo = new ConcurrentHashMap<>(); - - @Override - public void registry(List resources, PluginRegistryInfo pluginRegistryInfo) throws Exception { - Set> appenderSet = new HashSet<>(); - PluginWrapper pluginWrapper = pluginRegistryInfo.getPluginWrapper(); - for (Resource resource : resources) { - if(resource == null){ - continue; - } - LogConfig logConfig; - try { - logConfig = LogConfigUtil.getLogConfig(resource, pluginRegistryInfo); - } catch (Exception e){ - log.error("Failed to read log configuration.", e); - continue; - } - Set> logAppenderSet = addAppender(pluginRegistryInfo, logConfig); - appenderSet.addAll(logAppenderSet); - } - pluginAppenderInfo.put(pluginWrapper.getPluginId(), appenderSet); - } - - @Override - public void unRegistry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - PluginWrapper pluginWrapper = pluginRegistryInfo.getPluginWrapper(); - Set> logAppenderSet = pluginAppenderInfo.get(pluginWrapper.getPluginId()); - if(logAppenderSet == null || logAppenderSet.isEmpty()){ - return; - } - String packageName = pluginRegistryInfo.getBasePlugin().scanPackage(); - LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); - Logger logger = context.getLogger(packageName); - for (Appender appender : logAppenderSet) { - logger.detachAppender(appender); - } - pluginAppenderInfo.remove(pluginWrapper.getPluginId()); - } - - private Set> addAppender(PluginRegistryInfo pluginRegistryInfo, LogConfig logConfig) { - LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); - String packageName = logConfig.getPackageName(); - Logger logger = context.getLogger(packageName); - logger.detachAndStopAllAppenders(); - - ConsoleAppender consoleAppender = createConsoleAppender(pluginRegistryInfo.getPluginWrapper(), - logConfig, packageName); - RollingFileAppender fileAppender = createFileAppender(pluginRegistryInfo, - logConfig, packageName); - - logger.setAdditive(false); - logger.setLevel(Level.toLevel(logConfig.getLevel())); - logger.addAppender(consoleAppender); - logger.addAppender(fileAppender); - - Set> appenderSet = new HashSet<>(); - appenderSet.add(consoleAppender); - appenderSet.add(fileAppender); - return appenderSet; - } - - private ConsoleAppender createConsoleAppender(PluginWrapper pluginWrapper, - LogConfig logConfig, - String packageName) { - LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); - ConsoleAppender appender = new ConsoleAppender<>(); - Filter filter = new LogFilter(packageName); - filter.start(); - appender.addFilter(filter); - appender.setContext(context); - appender.setName(pluginWrapper.getPluginId().concat("-console")); - - PatternLayoutEncoder encoder = new PatternLayoutEncoder(); - encoder.setContext(context); - encoder.setPattern(logConfig.getPattern()); - encoder.start(); - - appender.setEncoder(encoder); - appender.start(); - return appender; - } - - private RollingFileAppender createFileAppender(PluginRegistryInfo pluginRegistryInfo, - LogConfig logConfig, - String packageName) { - LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); - - RollingFileAppender appender = new RollingFileAppender<>(); - if(StringUtils.isNotNullOrEmpty(packageName)){ - Filter filter = new LogFilter(packageName); - filter.start(); - appender.addFilter(filter); - } - - PluginWrapper pluginWrapper = pluginRegistryInfo.getPluginWrapper(); - appender.setContext(context); - appender.setName(pluginWrapper.getPluginId()); - - String logFilePrefix = LogConfigUtil.getLogFile(pluginRegistryInfo, logConfig); - appender.setFile(OptionHelper.substVars(logFilePrefix.concat(".log"), context)); - - appender.setAppend(true); - appender.setPrudent(false); - - SizeAndTimeBasedRollingPolicy policy = new SizeAndTimeBasedRollingPolicy<>(); - - String fp = OptionHelper.substVars(logFilePrefix.concat(".%d{yyyy-MM-dd}-%i.log"), context); - policy.setMaxFileSize(FileSize.valueOf(logConfig.getMaxFileSize())); - policy.setFileNamePattern(fp); - policy.setMaxHistory(logConfig.getMaxHistory()); - policy.setTotalSizeCap(FileSize.valueOf(logConfig.getTotalFileSize())); - policy.setParent(appender); - policy.setContext(context); - policy.start(); - - PatternLayoutEncoder encoder = new PatternLayoutEncoder(); - encoder.setContext(context); - encoder.setPattern(logConfig.getPattern()); - encoder.start(); - - appender.setRollingPolicy(policy); - appender.setEncoder(encoder); - appender.start(); - return appender; - } - - private static class LogFilter extends Filter { - - private final String packageName; - private final LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); - - private LogFilter(String packageName) { - this.packageName = packageName; - } - - @Override - public FilterReply decide(ILoggingEvent event) { - Logger logger = loggerContext.getLogger(packageName); - if (event.getLoggerName().startsWith(packageName) && event.getLevel().isGreaterOrEqual(logger.getLevel())) { - return FilterReply.ACCEPT; - } - return FilterReply.DENY; - } - } - -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/util/LogConfigUtil.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/util/LogConfigUtil.java deleted file mode 100644 index f9b2e55456b10fbc02d1799f133cada2b740195b..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/util/LogConfigUtil.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.gitee.starblues.extension.log.util; - -import com.gitee.starblues.extension.log.annotation.ConfigItem; -import com.gitee.starblues.extension.log.config.LogConfig; -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.integration.IntegrationConfiguration; -import com.gitee.starblues.utils.CommonUtils; -import com.gitee.starblues.utils.ResourceUtils; -import org.pf4j.PluginWrapper; -import org.pf4j.RuntimeMode; -import org.pf4j.util.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.io.Resource; - -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Unmarshaller; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.StringReader; -import java.lang.reflect.Field; - -/** - * LogConfig 对象和 xml 映射的工具类 - * @author starBlues - * @version 2.4.3 - */ -public class LogConfigUtil { - - private static final Logger LOG = LoggerFactory.getLogger(LogConfigUtil.class); - public static final String ROOT_PLUGIN_SIGN = "~"; - - public static LogConfig getLogConfig(Resource xmlResource, PluginRegistryInfo pluginRegistryInfo) - throws Exception{ - PluginWrapper pluginWrapper = pluginRegistryInfo.getPluginWrapper(); - String configText = readConfigText(xmlResource); - LogConfig logConfig = (LogConfig) xml2object(configText); - checkLogConfig(logConfig, pluginWrapper.getPluginId()); - String packageName = logConfig.getPackageName(); - if(StringUtils.isNullOrEmpty(packageName)){ - logConfig.setPackageName(pluginRegistryInfo.getBasePlugin().scanPackage()); - } - return logConfig; - } - - - public static String getLogFile(PluginRegistryInfo pluginRegistryInfo, LogConfig logConfig){ - String rootDir = logConfig.getRootDir(); - String home; - PluginWrapper pluginWrapper = pluginRegistryInfo.getPluginWrapper(); - IntegrationConfiguration configuration = pluginRegistryInfo.getConfiguration(); - String pluginRootDir; - RuntimeMode runtimeMode = pluginWrapper.getRuntimeMode(); - if(runtimeMode == RuntimeMode.DEVELOPMENT){ - pluginRootDir = pluginWrapper.getPluginPath().toString(); - } else { - pluginRootDir = configuration.pluginPath().get(0); - } - if(StringUtils.isNullOrEmpty(rootDir)){ - home = CommonUtils.joiningFilePath(pluginRootDir, "logs"); - } else { - home = ResourceUtils.getAbsolutePath(pluginRegistryInfo, rootDir); - } - String fileName = logConfig.getFileName(); - if (StringUtils.isNullOrEmpty(fileName)) { - fileName = pluginWrapper.getPluginId(); - } - return CommonUtils.joiningFilePath(home, pluginWrapper.getPluginId(), fileName); - } - - private static void checkLogConfig(LogConfig logConfig, String pluginId) { - String fileName = logConfig.getFileName(); - if (StringUtils.isNullOrEmpty(fileName)) { - logConfig.setFileName(pluginId.concat("-log")); - } - Field[] fields = LogConfig.class.getDeclaredFields(); - for (Field field : fields) { - if (!field.isAccessible()) { - field.setAccessible(true); - } - ConfigItem configItem = field.getDeclaredAnnotation(ConfigItem.class); - if (configItem == null) { - continue; - } - try { - Object fieldValue = field.get(logConfig); - Class fieldType = field.getType(); - if (fieldValue == null || "".equals(fieldValue.toString()) || - ObjectUtil.isEmptyObject(fieldType, fieldValue)) { - String defaultValue = configItem.defaultValue(); - LOG.debug("Field {} is not config or invalid in log config of plugin {}, set it to default value {}.", field.getName(), defaultValue, pluginId); - Object fixedValue = ObjectUtil.parseBasicTypeValue(fieldType, defaultValue); - field.set(logConfig, fixedValue); - } - } catch (IllegalAccessException e) { - LOG.error("Failed to check config item {} in log config.", field.getName()); - } - } - } - - private static String readConfigText(Resource resource) throws IOException { - String fileContent; - try (InputStream inputStream = resource.getInputStream(); - ByteArrayOutputStream stream = new ByteArrayOutputStream()) { - byte[] buff = new byte[1024]; - int len; - while ((len = inputStream.read(buff)) != -1) { - stream.write(buff, 0, len); - } - byte[] data = stream.toByteArray(); - fileContent = new String(data); - } - return fileContent; - } - - private static Object xml2object(String xml) throws Exception { - Object object; - try { - JAXBContext context = JAXBContext.newInstance(LogConfig.class); - Unmarshaller unmarshaller = context.createUnmarshaller(); - StringReader stringReader = new StringReader(xml); - object = unmarshaller.unmarshal(stringReader); - } catch (JAXBException e) { - e.printStackTrace(); - throw new Exception("Invalid xml definition"); - } - return object; - } -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/util/ObjectUtil.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/util/ObjectUtil.java deleted file mode 100644 index 5cd95b61f46cb2f3317bb01426d3635bd0631100..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/util/ObjectUtil.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.gitee.starblues.extension.log.util; - -/** - * 对象工具类 - * @author sousouki - * @version 2.4.3 - */ -public class ObjectUtil { - - private ObjectUtil(){ - - } - - public static Object parseBasicTypeValue(Class fieldType, String defaultValue) { - Object fixedValue; - if (Byte.class == fieldType || byte.class == fieldType) { - fixedValue = Byte.parseByte(defaultValue); - } else if (Integer.class == fieldType || int.class == fieldType) { - fixedValue = Integer.parseInt(defaultValue); - } else if (Double.class == fieldType || double.class == fieldType) { - fixedValue = Double.parseDouble(defaultValue); - } else if (Short.class == fieldType || short.class == fieldType) { - fixedValue = Short.parseShort(defaultValue); - } else if (Long.class == fieldType || long.class == fieldType) { - fixedValue = Long.parseLong(defaultValue); - } else if (Float.class == fieldType || float.class == fieldType) { - fixedValue = Float.parseFloat(defaultValue); - } else if (Boolean.class == fieldType || boolean.class == fieldType) { - fixedValue = Boolean.parseBoolean(defaultValue); - } else if (Character.class == fieldType || char.class == fieldType) { - fixedValue = defaultValue.charAt(0); - } else { - fixedValue = defaultValue; - } - return fixedValue; - } - - public static boolean isEmptyObject(Class fieldType, Object fieldValue) { - if (Byte.class == fieldType || byte.class == fieldType) { - return (byte) fieldValue == 0; - } else if (Integer.class == fieldType || int.class == fieldType) { - return (int) fieldValue == 0; - } else if (Double.class == fieldType || double.class == fieldType) { - return (double) fieldValue == 0; - } else if (Short.class == fieldType || short.class == fieldType) { - return (short) fieldValue == 0; - } else if (Long.class == fieldType || long.class == fieldType) { - return (long) fieldValue == 0; - } else if (Float.class == fieldType || float.class == fieldType) { - return (float) fieldValue == 0; - } - return false; - } - -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/pom.xml b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/pom.xml deleted file mode 100644 index 8bbb50190a528fa3a094956e7f9feec93ff587c5..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/pom.xml +++ /dev/null @@ -1,221 +0,0 @@ - - - - 4.0.0 - - - org.sonatype.oss - oss-parent - 7 - - - com.gitee.starblues - springboot-plugin-framework-extension-mybatis - 2.4.6-RELEASE - jar - - 插件扩展-spring boot mybatis 集成扩展 - - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - repo - - - - - https://gitee.com/starblues/springboot-plugin-framework-parent - scm:https://gitee.com/starblues/springboot-plugin-framework-parent.git - scm:https://gitee.com/starblues/springboot-plugin-framework-parent.git - 1.0 - - - - - sonatype-nexus-snapshots - oss Snapshots Repository - https://oss.sonatype.org/content/repositories/snapshots - - - sonatype-nexus-staging - oss Staging Repository - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - - - StarBlues - starblues@foxmail.com - https://gitee.com/starblues/ - - - - - 1.8 - UTF-8 - - 3.8.1 - 3.1.0 - 3.1.0 - 3.1.0 - 1.6 - - 2.4.6-RELEASE - 2.0.1 - 3.4.1 - 2.1.5 - - - - - - com.gitee.starblues - springboot-plugin-framework - ${springboot-plugin-framework.version} - provided - - - - org.mybatis.spring.boot - mybatis-spring-boot-starter - ${mybatis-spring-boot-starter.version} - provided - - - - com.baomidou - mybatis-plus-boot-starter - ${mybatis-plus-boot-starter.version} - provided - - - - tk.mybatis - mapper-spring-boot-starter - ${tk-mybatis-spring-boot-starter.version} - provided - - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - ${java.version} - ${java.version} - - - - - org.apache.maven.plugins - maven-assembly-plugin - ${maven-assembly-plugin.version} - - - jar-with-dependencies - - ${project.artifactId}-${project.version} - false - false - - - true - true - - - - - - make-assembly - package - - single - - - - - - - org.apache.maven.plugins - maven-source-plugin - ${maven-source-plugin.version} - - - package - - jar-no-fork - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - ${maven-javadoc-plugin.version} - - ${plugin.skip} - - - - package - - jar - - - - - - - org.apache.maven.plugins - maven-gpg-plugin - ${maven-gpg-plugin.version} - - ${plugin.skip} - - - - sign-artifacts - verify - - sign - - - - - - - - - - - - - dev - - true - - - true - - - - - release - - false - - - - - - \ No newline at end of file diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/CommonRegister.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/CommonRegister.java deleted file mode 100644 index a9ef69e0b871b7a97ed8f5b251117bfb9ec5179b..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/CommonRegister.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.gitee.starblues.extension.mybatis; - -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.SpringBeanRegister; -import org.apache.ibatis.session.SqlSessionFactory; -import org.mybatis.spring.SqlSessionTemplate; - -/** - * 公共注册 - * @author starBlues - * @version 2.4.2 - */ -public class CommonRegister { - - private CommonRegister(){} - - - public static void commonRegister(PluginRegistryInfo pluginRegistryInfo, - SqlSessionFactory sqlSessionFactory, - SqlSessionTemplate sqlSessionTemplate){ - // 注册SqlSessionFactory - SpringBeanRegister springBeanRegister = pluginRegistryInfo.getSpringBeanRegister(); - springBeanRegister.registerSingleton("sqlSessionFactory", sqlSessionFactory); - springBeanRegister.registerSingleton("sqlSession", sqlSessionTemplate); - } - -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/MapperHandler.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/MapperHandler.java deleted file mode 100644 index 099758413da91450c02d95efd03d91660f2abfc7..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/MapperHandler.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.gitee.starblues.extension.mybatis; - -import com.gitee.starblues.extension.mybatis.group.PluginMapperGroup; -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.pipe.bean.name.PluginAnnotationBeanNameGenerator; -import org.apache.ibatis.session.SqlSessionFactory; -import org.mybatis.spring.SqlSessionTemplate; -import org.mybatis.spring.mapper.MapperFactoryBean; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition; -import org.springframework.beans.factory.config.BeanDefinitionHolder; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; -import org.springframework.beans.factory.support.BeanNameGenerator; -import org.springframework.beans.factory.support.GenericBeanDefinition; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.AnnotationConfigUtils; -import org.springframework.context.annotation.AnnotationScopeMetadataResolver; -import org.springframework.context.annotation.ScopeMetadata; -import org.springframework.context.annotation.ScopeMetadataResolver; -import org.springframework.context.support.GenericApplicationContext; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Mapper 接口处理者 - * @author starBlues - * @version 2.4.0 - */ -public class MapperHandler { - - private static final Logger LOGGER = LoggerFactory.getLogger(MapperHandler.class); - - private static final String MAPPER_INTERFACE_NAMES = "MybatisMapperInterfaceNames"; - - private final ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver(); - - - public MapperHandler() { - } - - /** - * 处理插件中的Mapper - * @param pluginRegistryInfo 插件信息 - * @param processMapper Mapper的具体处理者 - */ - public void processMapper(PluginRegistryInfo pluginRegistryInfo, - MapperHandler.ProcessMapper processMapper){ - GenericApplicationContext applicationContext = pluginRegistryInfo.getPluginApplicationContext(); - List> groupClasses = pluginRegistryInfo.getGroupClasses(PluginMapperGroup.GROUP_ID); - if(groupClasses == null || groupClasses.isEmpty()){ - return; - } - String pluginId = pluginRegistryInfo.getPluginWrapper().getPluginId(); - Set beanNames = new HashSet<>(); - for (Class groupClass : groupClasses) { - if (groupClass == null) { - continue; - } - BeanNameGenerator beanNameGenerator = new PluginAnnotationBeanNameGenerator(pluginId); - AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(groupClass); - ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(abd); - abd.setScope(scopeMetadata.getScopeName()); - String beanName = beanNameGenerator.generateBeanName(abd, applicationContext); - BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); - AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); - BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, applicationContext); - try { - processMapper.process(definitionHolder, groupClass); - beanNames.add(beanName); - } catch (Exception e) { - LOGGER.error("process mapper '{}' error. {}", groupClass.getName(), e.getMessage(), e); - } - } - pluginRegistryInfo.addExtension(MAPPER_INTERFACE_NAMES, beanNames); - } - - - /** - * 公共注册生成代理Mapper接口 - * @param holder ignore - * @param mapperClass ignore - * @param sqlSessionFactory ignore - * @param sqlSessionTemplate ignore - */ - public void commonProcessMapper(BeanDefinitionHolder holder, - Class mapperClass, - SqlSessionFactory sqlSessionFactory, - SqlSessionTemplate sqlSessionTemplate) { - GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition(); - definition.getConstructorArgumentValues().addGenericArgumentValue(mapperClass); - definition.setBeanClass(MapperFactoryBean.class); - definition.getPropertyValues().add("addToConfig", true); - definition.getPropertyValues().add("sqlSessionFactory", sqlSessionFactory); - definition.getPropertyValues().add("sqlSessionTemplate", sqlSessionTemplate); - definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); - } - - - - @FunctionalInterface - public interface ProcessMapper{ - void process(BeanDefinitionHolder holder, Class mapperClass) throws Exception; - } - - -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/MybatisCommonConfig.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/MybatisCommonConfig.java deleted file mode 100644 index 3cbdad79a62c2406b7e6af490bb9b53d9b75c55f..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/MybatisCommonConfig.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.gitee.starblues.extension.mybatis; - -import java.util.Set; - -/** - * Springboot mybatis 公用的配置 - * @author starBlues - * @version 2.3 - */ -public interface MybatisCommonConfig { - - /** - * 数据库表对应的实体类的包名集合。可配置多个 - * @return Set - */ - Set entityPackage(); - - /** - * mybatis xml mapper 匹配规则
- * ? 匹配一个字符
- * * 匹配零个或多个字符
- * ** 匹配路径中的零或多个目录
- * 例如:
- * 文件路径配置为

file:D://xml/*PluginMapper.xml


- * resources路径配置为

classpath:xml/mapper/*PluginMapper.xml


- * 包路径配置为

package:com.plugin.xml.mapper.*PluginMapper.xml


- * @return Set - */ - Set xmlLocationsMatch(); - - /** - * 插件是否自主启用配置. 默认进行禁用, 使用主程序的配置 - * @return 返回true, 表示进行插件自主进行Mybatis相关配置 - */ - default boolean enableOneselfConfig(){ - return false; - } - - -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/MybatisProcessor.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/MybatisProcessor.java deleted file mode 100644 index 1d2ddc078e3a2c83f967763a24572b80ae5e0f24..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/MybatisProcessor.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.gitee.starblues.extension.mybatis; - -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.pipe.bean.PluginBeanRegistrarExtend; -import com.gitee.starblues.utils.SpringBeanUtils; -import org.apache.ibatis.io.Resources; -import org.apache.ibatis.mapping.DatabaseIdProvider; -import org.apache.ibatis.plugin.Interceptor; -import org.apache.ibatis.scripting.LanguageDriver; -import org.apache.ibatis.session.Configuration; -import org.apache.ibatis.session.SqlSessionFactory; -import org.mybatis.spring.SqlSessionFactoryBean; -import org.mybatis.spring.SqlSessionTemplate; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.io.Resource; - -/** - * mybatis 处理者 - * @author starBlues - * @version 2.4.0 - */ -public class MybatisProcessor implements PluginBeanRegistrarExtend { - - private static final Logger LOGGER = LoggerFactory.getLogger(MybatisProcessor.class); - - public MybatisProcessor() { - } - - @Override - public String key() { - return "MybatisProcessor"; - } - - @Override - public void registry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - SpringBootMybatisConfig config = SpringBeanUtils.getObjectByInterfaceClass( - pluginRegistryInfo.getConfigSingletons(), - SpringBootMybatisConfig.class); - if(config == null){ - return; - } - - SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); - - if(config.enableOneselfConfig()){ - config.oneselfConfig(factory); - } else { - GenericApplicationContext mainApplicationContext = pluginRegistryInfo.getMainApplicationContext(); - PluginFollowCoreConfig followCoreConfig = new PluginFollowCoreConfig(mainApplicationContext); - factory.setDataSource(followCoreConfig.getDataSource()); - Configuration configuration = followCoreConfig.getConfiguration(SpringBootMybatisExtension.Type.MYBATIS); - factory.setConfiguration(configuration); - Interceptor[] interceptor = followCoreConfig.getInterceptor(); - if(interceptor != null && interceptor.length > 0){ - factory.setPlugins(interceptor); - } - DatabaseIdProvider databaseIdProvider = followCoreConfig.getDatabaseIdProvider(); - if(databaseIdProvider != null){ - factory.setDatabaseIdProvider(databaseIdProvider); - } - LanguageDriver[] languageDrivers = followCoreConfig.getLanguageDriver(); - if(languageDrivers != null){ - for (LanguageDriver languageDriver : languageDrivers) { - configuration.getLanguageRegistry().register(languageDriver); - } - } - - } - PluginResourceFinder pluginResourceFinder = new PluginResourceFinder(pluginRegistryInfo); - - Class[] aliasesClasses = pluginResourceFinder.getAliasesClasses(config.entityPackage()); - if(aliasesClasses != null && aliasesClasses.length > 0){ - factory.setTypeAliases(aliasesClasses); - } - - Resource[] xmlResource = pluginResourceFinder.getXmlResource(config.xmlLocationsMatch()); - if(xmlResource != null && xmlResource.length > 0){ - factory.setMapperLocations(xmlResource); - } - - ClassLoader defaultClassLoader = Resources.getDefaultClassLoader(); - try { - Resources.setDefaultClassLoader(pluginRegistryInfo.getPluginClassLoader()); - SqlSessionFactory sqlSessionFactory = factory.getObject(); - if(sqlSessionFactory == null){ - throw new Exception("Get mybatis sqlSessionFactory is null"); - } - SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory); - MapperHandler mapperHandler = new MapperHandler(); - mapperHandler.processMapper(pluginRegistryInfo, (holder, mapperClass) -> { - mapperHandler.commonProcessMapper(holder, mapperClass, sqlSessionFactory, sqlSessionTemplate); - }); - CommonRegister.commonRegister(pluginRegistryInfo, sqlSessionFactory, sqlSessionTemplate); - } finally { - Resources.setDefaultClassLoader(defaultClassLoader); - } - - } - - -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/PluginFollowCoreConfig.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/PluginFollowCoreConfig.java deleted file mode 100644 index 97cc0671ce734dd91f1aa4a85f2fda27c6c0c588..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/PluginFollowCoreConfig.java +++ /dev/null @@ -1,146 +0,0 @@ -package com.gitee.starblues.extension.mybatis; - -import com.baomidou.mybatisplus.core.MybatisConfiguration; -import org.apache.ibatis.mapping.DatabaseIdProvider; -import org.apache.ibatis.plugin.Interceptor; -import org.apache.ibatis.scripting.LanguageDriver; -import org.apache.ibatis.scripting.LanguageDriverRegistry; -import org.apache.ibatis.session.Configuration; -import org.apache.ibatis.session.SqlSessionFactory; -import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer; -import org.springframework.context.ApplicationContext; -import org.springframework.util.ReflectionUtils; - -import javax.sql.DataSource; -import java.lang.reflect.Field; -import java.util.*; - -/** - * 插件跟随主程序时, 获取主程序的Mybatis定义的一些配置 - * @author starBlues - * @version 2.3 - */ -public class PluginFollowCoreConfig { - - private final ApplicationContext mainApplicationContext; - - public PluginFollowCoreConfig(ApplicationContext mainApplicationContext) { - this.mainApplicationContext = mainApplicationContext; - } - - - public DataSource getDataSource(){ - return mainApplicationContext.getBean(DataSource.class); - } - - public Configuration getConfiguration(SpringBootMybatisExtension.Type type){ - Configuration configuration = new Configuration(); - if(type == SpringBootMybatisExtension.Type.MYBATIS){ - try { - Map customizerMap = mainApplicationContext.getBeansOfType(ConfigurationCustomizer.class); - if(!customizerMap.isEmpty()){ - for (ConfigurationCustomizer customizer : customizerMap.values()) { - customizer.customize(configuration); - } - } - } catch (Exception e){ - // ignore - } - } - return configuration; - } - - public MybatisConfiguration getMybatisPlusConfiguration(){ - MybatisConfiguration configuration = new MybatisConfiguration(); - try { - Map customizerMap = - mainApplicationContext.getBeansOfType(com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer.class); - if(!customizerMap.isEmpty()){ - for (com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer customizer : customizerMap.values()) { - customizer.customize(configuration); - } - } - } catch (Exception e){ - // ignore - } - return configuration; - } - - public Interceptor[] getInterceptor(){ - Map, Interceptor> interceptorMap = new HashMap<>(); - try { - SqlSessionFactory sqlSessionFactory = mainApplicationContext.getBean(SqlSessionFactory.class); - // 先从 SqlSessionFactory 工厂中获取拦截器 - List interceptors = sqlSessionFactory.getConfiguration().getInterceptors(); - if(interceptors != null){ - for (Interceptor interceptor : interceptors) { - if(interceptor == null){ - continue; - } - interceptorMap.put(interceptor.getClass(), interceptor); - } - } - } catch (Exception e){ - // ignore - } - // 再从定义Bean中获取拦截器 - Map beanInterceptorMap = mainApplicationContext.getBeansOfType(Interceptor.class); - if(!beanInterceptorMap.isEmpty()){ - beanInterceptorMap.forEach((k, v)->{ - // 如果Class一致, 则会覆盖 - interceptorMap.put(v.getClass(), v); - }); - } - if(interceptorMap.isEmpty()) { - return null; - } else { - return interceptorMap.values().toArray(new Interceptor[0]); - } - } - - public DatabaseIdProvider getDatabaseIdProvider(){ - String[] beanNamesForType = mainApplicationContext.getBeanNamesForType(DatabaseIdProvider.class, false, false); - if(beanNamesForType.length > 0){ - return mainApplicationContext.getBean(DatabaseIdProvider.class); - } - return null; - } - - @SuppressWarnings("unchecked") - public LanguageDriver[] getLanguageDriver(){ - Map, LanguageDriver> languageDriverMap = new HashMap<>(); - try { - SqlSessionFactory sqlSessionFactory = mainApplicationContext.getBean(SqlSessionFactory.class); - LanguageDriverRegistry languageRegistry = sqlSessionFactory.getConfiguration() - .getLanguageRegistry(); - // 先从 SqlSessionFactory 工厂中获取LanguageDriver - Field proxyTypesField = ReflectionUtils.findField(languageRegistry.getClass(), "LANGUAGE_DRIVER_MAP"); - Map, LanguageDriver> driverMap = null; - if(proxyTypesField != null){ - if (!proxyTypesField.isAccessible()) { - proxyTypesField.setAccessible(true); - } - driverMap = (Map, LanguageDriver>) proxyTypesField.get(languageRegistry); - } - if(driverMap != null){ - languageDriverMap.putAll(driverMap); - } - } catch (Exception e){ - // ignore - } - Map beansLanguageDriver = mainApplicationContext.getBeansOfType(LanguageDriver.class); - if(!beansLanguageDriver.isEmpty()){ - beansLanguageDriver.forEach((k, v)->{ - // 如果Class一致, 则会覆盖 - languageDriverMap.put(v.getClass(), v); - }); - } - if(languageDriverMap.isEmpty()){ - return null; - } - return languageDriverMap.values().toArray(new LanguageDriver[0]); - } - - - -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/PluginResourceFinder.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/PluginResourceFinder.java deleted file mode 100644 index 58e3c3cd619bbba126e3feb05b1bbdb2f6018237..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/PluginResourceFinder.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.gitee.starblues.extension.mybatis; - - -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.utils.ResourceUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.io.Resource; -import org.springframework.core.io.support.PathMatchingResourcePatternResolver; -import org.springframework.core.io.support.ResourcePatternResolver; -import org.springframework.core.type.ClassMetadata; -import org.springframework.core.type.classreading.MetadataReaderFactory; -import org.springframework.core.type.classreading.SimpleMetadataReaderFactory; -import org.springframework.util.ClassUtils; -import org.springframework.util.StringUtils; - -import java.io.IOException; -import java.util.*; - -/** - * 插件资源发现者 - * @author starBlues - * @version 2.4.0 - */ -public class PluginResourceFinder { - - private static final Logger LOGGER = LoggerFactory.getLogger(PluginResourceFinder.class); - - private final static String TYPE_FILE = "file"; - private final static String TYPE_CLASSPATH = "classpath"; - private final static String TYPE_PACKAGE = "package"; - - - private final ClassLoader classLoader; - private final ResourcePatternResolver resourcePatternResolver; - private final MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(); - - - public PluginResourceFinder(PluginRegistryInfo pluginRegistryInfo) { - this.classLoader = pluginRegistryInfo.getPluginClassLoader(); - this.resourcePatternResolver = new PathMatchingResourcePatternResolver(classLoader); - } - - /** - * 获取插件中xml资源 - * @param xmlLocationsMatchSet xml资源匹配集合 - * @return xml Resource 数组 - * @throws IOException 获取xml资源异常 - */ - public Resource[] getXmlResource(Set xmlLocationsMatchSet) throws IOException { - if(xmlLocationsMatchSet == null || xmlLocationsMatchSet.isEmpty()){ - return null; - } - List resources = new ArrayList<>(); - for (String xmlLocationsMatch : xmlLocationsMatchSet) { - if(StringUtils.isEmpty(xmlLocationsMatch)){ - continue; - } - List loadResources = getXmlResources(xmlLocationsMatch); - if(loadResources != null && !loadResources.isEmpty()){ - resources.addAll(loadResources); - } - } - - if(resources.isEmpty()){ - return null; - } - - return resources.toArray(new Resource[0]); - } - - - - /** - * 获取插件的实体类及其别名 - * @param packagePatterns 实体类包名 - * @return class 数组 - * @throws IOException 获取医院异常 - */ - public Class[] getAliasesClasses(Set packagePatterns) throws IOException { - if(packagePatterns == null || packagePatterns.isEmpty()){ - return null; - } - Set> aliasesClasses = new HashSet<>(); - for (String packagePattern : packagePatterns) { - Resource[] resources = resourcePatternResolver.getResources( - ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX - + ClassUtils.convertClassNameToResourcePath(packagePattern) + "/**/*.class"); - for (Resource resource : resources) { - try { - ClassMetadata classMetadata = metadataReaderFactory.getMetadataReader(resource).getClassMetadata(); - Class clazz = classLoader.loadClass(classMetadata.getClassName()); - aliasesClasses.add(clazz); - } catch (Throwable e) { - LOGGER.warn("Cannot load the '{}'. Cause by {}", resource, e.toString()); - } - } - } - return aliasesClasses.toArray(new Class[0]); - } - - /** - * 得到Xml资源 - * @param mybatisMapperXmlLocationMatch mybatis xml 批量规则 - * @return 匹配到的xml资源 - * @throws IOException IO 异常 - */ - private List getXmlResources(String mybatisMapperXmlLocationMatch) throws IOException { - String matchLocation = ResourceUtils.getMatchLocation(mybatisMapperXmlLocationMatch); - if(matchLocation == null){ - LOGGER.error("mybatisMapperXmlLocation {} illegal", mybatisMapperXmlLocationMatch); - return null; - } - try { - Resource[] resources = resourcePatternResolver.getResources(matchLocation); - if(resources.length > 0){ - return Arrays.asList(resources); - } else { - return null; - } - } catch (IOException e) { - LOGGER.error("mybatis xml resource '{}' match error : {}", mybatisMapperXmlLocationMatch, - e.getMessage(), e); - throw e; - } - } - - -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/SpringBootMybatisConfig.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/SpringBootMybatisConfig.java deleted file mode 100644 index 75ec57fe4b843fe174ba226cb29c56da4818f0fd..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/SpringBootMybatisConfig.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.gitee.starblues.extension.mybatis; - -import org.apache.ibatis.session.Configuration; -import org.mybatis.spring.SqlSessionFactoryBean; - -/** - * Springboot mybatis 的配置接口 - * @author starBlues - * @version 2.3 - */ -public interface SpringBootMybatisConfig extends MybatisCommonConfig{ - - - /** - * 插件自主配置Mybatis的SqlSessionFactoryBean - * SqlSessionFactoryBean 具体配置说明参考 Mybatis 官网 - * @param sqlSessionFactoryBean SqlSessionFactoryBean - */ - default void oneselfConfig(SqlSessionFactoryBean sqlSessionFactoryBean){ - } - - /** - * 重写配置当前跟随主程序的配置 - * 只有 enableOneselfConfig 返回 false, 实现该方法才生效 - * @param configuration Mybatis Configuration 的配置 - */ - default void reSetMainConfig(Configuration configuration){ - - } -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/SpringBootMybatisExtension.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/SpringBootMybatisExtension.java deleted file mode 100644 index fa73ffe9e8700f89fca968b6d10efc432c7ba99b..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/SpringBootMybatisExtension.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.gitee.starblues.extension.mybatis; - -import com.gitee.starblues.extension.AbstractExtension; -import com.gitee.starblues.extension.mybatis.group.MybatisConfigGroup; -import com.gitee.starblues.extension.mybatis.group.PluginEntityAliasesGroup; -import com.gitee.starblues.extension.mybatis.group.PluginMapperGroup; -import com.gitee.starblues.extension.mybatis.mybatisplus.MybatisPlusProcessor; -import com.gitee.starblues.extension.mybatis.tkmyabtis.TkMybatisProcessor; -import com.gitee.starblues.factory.process.pipe.PluginPipeProcessorExtend; -import com.gitee.starblues.factory.process.pipe.bean.PluginBeanRegistrarExtend; -import com.gitee.starblues.factory.process.pipe.classs.PluginClassGroupExtend; -import org.springframework.context.ApplicationContext; - -import java.util.ArrayList; -import java.util.List; - -/** - * mybatis 扩展 - * @author starBlues - * @version 2.4.0 - */ -public class SpringBootMybatisExtension extends AbstractExtension { - - private static final String KEY = "SpringBootMybatisPlusExtension"; - - private final Type type; - - /** - * 初始化扩展 - * @param type 根据当前环境所集成的框架来选择Type类型 - */ - public SpringBootMybatisExtension(Type type) { - if(type == null){ - this.type = Type.MYBATIS; - } else { - this.type = type; - } - } - - @Override - public String key() { - return KEY; - } - - @Override - public void initialize(ApplicationContext mainApplicationContext) throws Exception { - } - - @Override - public List getPluginClassGroup(ApplicationContext mainApplicationContext) { - final List pluginClassGroups = new ArrayList<>(); - pluginClassGroups.add(new MybatisConfigGroup()); - pluginClassGroups.add(new PluginEntityAliasesGroup()); - pluginClassGroups.add(new PluginMapperGroup()); - return pluginClassGroups; - } - - @Override - public List getPluginBeanRegistrar(ApplicationContext mainApplicationContext) { - final List pluginBeanRegistrarExtends = new ArrayList<>(3); - if(type == Type.MYBATIS_PLUS){ - pluginBeanRegistrarExtends.add(new MybatisPlusProcessor()); - } else if(type == Type.TK_MYBATIS){ - pluginBeanRegistrarExtends.add(new TkMybatisProcessor()); - } else { - pluginBeanRegistrarExtends.add(new MybatisProcessor()); - } - return pluginBeanRegistrarExtends; - } - - public enum Type{ - MYBATIS, - MYBATIS_PLUS, - TK_MYBATIS - } - -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/group/MybatisConfigGroup.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/group/MybatisConfigGroup.java deleted file mode 100644 index 87a6db4536e04c41f531dc505048f3bbb2efda4e..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/group/MybatisConfigGroup.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.gitee.starblues.extension.mybatis.group; - -import com.gitee.starblues.extension.mybatis.SpringBootMybatisConfig; -import com.gitee.starblues.extension.mybatis.mybatisplus.SpringBootMybatisPlusConfig; -import com.gitee.starblues.factory.process.pipe.classs.PluginClassGroupExtend; -import com.gitee.starblues.realize.BasePlugin; -import org.springframework.util.ClassUtils; - -import java.util.Set; - -/** - * mybatis 配置分组 - * @author starBlues - * @version 2.3 - */ -public class MybatisConfigGroup implements PluginClassGroupExtend { - - public static final String KEY = "plugin_mybatis_config"; - - @Override - public String key() { - return KEY; - } - - @Override - public String groupId() { - return "MybatisConfigGroup"; - } - - @Override - public void initialize(BasePlugin basePlugin) { - - } - - @Override - public boolean filter(Class aClass) { - if(aClass == null){ - return false; - } - Set> allInterfacesForClassAsSet = ClassUtils.getAllInterfacesForClassAsSet(aClass); - - return allInterfacesForClassAsSet.contains(SpringBootMybatisConfig.class) - || allInterfacesForClassAsSet.contains(SpringBootMybatisPlusConfig.class); - - } -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/group/PluginEntityAliasesGroup.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/group/PluginEntityAliasesGroup.java deleted file mode 100644 index 1fd08b5cb0b4814a8fb59c13d18d72f1439d8949..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/group/PluginEntityAliasesGroup.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.gitee.starblues.extension.mybatis.group; - -import com.gitee.starblues.extension.mybatis.mybatisplus.SpringBootMybatisPlusConfig; -import com.gitee.starblues.factory.process.pipe.classs.PluginClassGroupExtend; -import com.gitee.starblues.realize.BasePlugin; -import com.gitee.starblues.utils.AnnotationsUtils; -import org.apache.ibatis.type.Alias; - -import java.util.Set; - -/** - * 插件中的实体类别名 - * - * @author starBlues - * @version 2.3 - */ -public class PluginEntityAliasesGroup implements PluginClassGroupExtend { - - public static final String KEY = "plugin_mybatis_alias"; - - private Set typeAliasesPackage; - - public PluginEntityAliasesGroup() { - } - - @Override - public String groupId() { - return KEY; - } - - @Override - public void initialize(BasePlugin basePlugin) { - if(basePlugin instanceof SpringBootMybatisPlusConfig){ - SpringBootMybatisPlusConfig mybatisPlusConfig = (SpringBootMybatisPlusConfig) basePlugin; - typeAliasesPackage = null; - } - } - - - @Override - public boolean filter(Class aClass) { - if(AnnotationsUtils.haveAnnotations(aClass, false, Alias.class)){ - return true; - } - if(typeAliasesPackage == null || typeAliasesPackage.isEmpty()){ - return false; - } - for (String packageName : typeAliasesPackage) { - if(aClass.getPackage().getName().equals(packageName)){ - return true; - } - } - return false; - } - - @Override - public String key() { - return "PluginEntityAliasesGroup"; - } - - -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/group/PluginMapperGroup.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/group/PluginMapperGroup.java deleted file mode 100644 index f7d65dfe32e41fb45453e9e87aad2f96503e9937..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/group/PluginMapperGroup.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.gitee.starblues.extension.mybatis.group; - -import com.gitee.starblues.factory.process.pipe.classs.PluginClassGroupExtend; -import com.gitee.starblues.realize.BasePlugin; -import com.gitee.starblues.utils.AnnotationsUtils; -import org.apache.ibatis.annotations.Mapper; - -/** - * 插件中的Mapper接口分组 - * - * @author starBlues - * @version 2.3 - */ -public class PluginMapperGroup implements PluginClassGroupExtend { - - public static final String GROUP_ID = "plugin_mybatis_mapper"; - - @Override - public String groupId() { - return GROUP_ID; - } - - @Override - public void initialize(BasePlugin basePlugin) { - - } - - @Override - public boolean filter(Class aClass) { - return AnnotationsUtils.haveAnnotations(aClass, false, Mapper.class); - } - - @Override - public String key() { - return "PluginMybatisMapperGroup"; - } -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/mybatisplus/MybatisPlusProcessor.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/mybatisplus/MybatisPlusProcessor.java deleted file mode 100644 index e4b263eaf42a1ae3548322a4a52186d5ffe3b835..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/mybatisplus/MybatisPlusProcessor.java +++ /dev/null @@ -1,130 +0,0 @@ -package com.gitee.starblues.extension.mybatis.mybatisplus; - -import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties; -import com.baomidou.mybatisplus.core.MybatisConfiguration; -import com.baomidou.mybatisplus.core.config.GlobalConfig; -import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; -import com.gitee.starblues.extension.mybatis.CommonRegister; -import com.gitee.starblues.extension.mybatis.MapperHandler; -import com.gitee.starblues.extension.mybatis.PluginFollowCoreConfig; -import com.gitee.starblues.extension.mybatis.PluginResourceFinder; -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.pipe.bean.PluginBeanRegistrarExtend; -import com.gitee.starblues.utils.SpringBeanUtils; -import org.apache.ibatis.io.Resources; -import org.apache.ibatis.mapping.DatabaseIdProvider; -import org.apache.ibatis.plugin.Interceptor; -import org.apache.ibatis.scripting.LanguageDriver; -import org.apache.ibatis.session.SqlSessionFactory; -import org.mybatis.spring.SqlSessionTemplate; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.io.Resource; - - -/** - * springboot-mybatis plus 处理者 - * @author starBlues - * @version 2.4.1 - */ -public class MybatisPlusProcessor implements PluginBeanRegistrarExtend { - - private static final Logger LOGGER = LoggerFactory.getLogger(MybatisPlusProcessor.class); - - - public MybatisPlusProcessor() { - } - - @Override - public String key() { - return "MybatisPlusProcessor"; - } - - - @Override - public void registry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - SpringBootMybatisPlusConfig config = SpringBeanUtils.getObjectByInterfaceClass( - pluginRegistryInfo.getConfigSingletons(), - SpringBootMybatisPlusConfig.class); - if(config == null){ - return; - } - final MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean(); - - if(config.enableOneselfConfig()){ - config.oneselfConfig(factory); - } else { - PluginFollowCoreConfig followCoreConfig = new PluginFollowCoreConfig( - pluginRegistryInfo.getMainApplicationContext() - ); - MybatisConfiguration mybatisPlusConfiguration = followCoreConfig.getMybatisPlusConfiguration(); - factory.setDataSource(followCoreConfig.getDataSource()); - factory.setConfiguration(mybatisPlusConfiguration); - Interceptor[] interceptor = followCoreConfig.getInterceptor(); - if(interceptor != null && interceptor.length > 0){ - factory.setPlugins(interceptor); - } - DatabaseIdProvider databaseIdProvider = followCoreConfig.getDatabaseIdProvider(); - if(databaseIdProvider != null){ - factory.setDatabaseIdProvider(databaseIdProvider); - } - LanguageDriver[] languageDriver = followCoreConfig.getLanguageDriver(); - if(languageDriver != null){ - factory.setScriptingLanguageDrivers(languageDriver); - } - // 配置mybatis-plus私有的配置 - GlobalConfig globalConfig = mybatisPlusFollowCoreConfig(factory, pluginRegistryInfo.getMainApplicationContext()); - config.reSetMainConfig(mybatisPlusConfiguration, globalConfig); - } - - PluginResourceFinder pluginResourceFinder = new PluginResourceFinder(pluginRegistryInfo); - - Class[] aliasesClasses = pluginResourceFinder.getAliasesClasses(config.entityPackage()); - if(aliasesClasses != null && aliasesClasses.length > 0){ - factory.setTypeAliases(aliasesClasses); - } - - Resource[] xmlResource = pluginResourceFinder.getXmlResource(config.xmlLocationsMatch()); - if(xmlResource != null && xmlResource.length > 0){ - factory.setMapperLocations(xmlResource); - } - ClassLoader defaultClassLoader = Resources.getDefaultClassLoader(); - try { - Resources.setDefaultClassLoader(pluginRegistryInfo.getPluginClassLoader()); - SqlSessionFactory sqlSessionFactory = factory.getObject(); - if(sqlSessionFactory == null){ - throw new Exception("Get mybatis-plus sqlSessionFactory is null"); - } - SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory); - MapperHandler mapperHandler = new MapperHandler(); - mapperHandler.processMapper(pluginRegistryInfo, (holder, mapperClass) -> { - mapperHandler.commonProcessMapper(holder, mapperClass, sqlSessionFactory, sqlSessionTemplate); - }); - CommonRegister.commonRegister(pluginRegistryInfo, sqlSessionFactory, sqlSessionTemplate); - } finally { - Resources.setDefaultClassLoader(defaultClassLoader); - } - - } - - - - private GlobalConfig mybatisPlusFollowCoreConfig(MybatisSqlSessionFactoryBean factory, - GenericApplicationContext mainApplicationContext){ - MybatisPlusProperties plusProperties = mainApplicationContext.getBean(MybatisPlusProperties.class); - - GlobalConfig currentGlobalConfig = new GlobalConfig(); - currentGlobalConfig.setBanner(false); - GlobalConfig globalConfig = plusProperties.getGlobalConfig(); - if(globalConfig != null){ - currentGlobalConfig.setDbConfig(globalConfig.getDbConfig()); - currentGlobalConfig.setIdentifierGenerator(globalConfig.getIdentifierGenerator()); - currentGlobalConfig.setMetaObjectHandler(globalConfig.getMetaObjectHandler()); - currentGlobalConfig.setSqlInjector(globalConfig.getSqlInjector()); - } - factory.setGlobalConfig(currentGlobalConfig); - return currentGlobalConfig; - } - -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/mybatisplus/ServiceImplWrapper.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/mybatisplus/ServiceImplWrapper.java deleted file mode 100644 index 8b5414abe9acafa30f072c48b556bcec7f8770c7..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/mybatisplus/ServiceImplWrapper.java +++ /dev/null @@ -1,161 +0,0 @@ -package com.gitee.starblues.extension.mybatis.mybatisplus; - -import com.baomidou.mybatisplus.core.conditions.Wrapper; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.extension.service.IService; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; - -import java.io.Serializable; -import java.lang.reflect.Field; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.function.Function; - -/** - * mybatis plus ServiceImpl 的包装。解决原生mybatis plus 中ServiceImpl Mapper无法注入的问题 - * 升级mybatis-plus到3.4.1 - * @author starBlues - * @version 2.4.0 - */ -public class ServiceImplWrapper, T> implements IService { - - private final ServiceImpl serviceImpl; - - protected M baseMapper; - - - public ServiceImplWrapper(M baseMapper) { - this.baseMapper = Objects.requireNonNull(baseMapper); - this.serviceImpl = new ServiceImpl(); - setMapper(); - } - - /** - * 给ServiceImpl设置Mapper - */ - private void setMapper(){ - Class aClass = serviceImpl.getClass(); - Field[] fields = aClass.getDeclaredFields(); - for (Field field : fields) { - if(Objects.equals(field.getName(), "baseMapper") - || (baseMapper != null && baseMapper.getClass() == field.getType())){ - field.setAccessible(true); - try { - field.set(serviceImpl, baseMapper); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } - } - } - - - @Override - public boolean save(T entity) { - return serviceImpl.save(entity); - } - - @Override - public boolean saveBatch(Collection entityList, int batchSize) { - return serviceImpl.saveBatch(entityList, batchSize); - } - - @Override - public boolean saveOrUpdateBatch(Collection entityList, int batchSize) { - return serviceImpl.saveOrUpdateBatch(entityList, batchSize); - } - - @Override - public boolean removeById(Serializable id) { - return serviceImpl.removeById(id); - } - - @Override - public boolean removeByMap(Map columnMap) { - return serviceImpl.removeByMap(columnMap); - } - - @Override - public boolean remove(Wrapper queryWrapper) { - return serviceImpl.remove(queryWrapper); - } - - @Override - public boolean removeByIds(Collection idList) { - return serviceImpl.removeByIds(idList); - } - - @Override - public boolean updateById(T entity) { - return serviceImpl.updateById(entity); - } - - @Override - public boolean update(T entity, Wrapper updateWrapper) { - return serviceImpl.update(entity, updateWrapper); - } - - @Override - public boolean updateBatchById(Collection entityList, int batchSize) { - return serviceImpl.updateBatchById(entityList, batchSize); - } - - @Override - public boolean saveOrUpdate(T entity) { - return serviceImpl.saveOrUpdate(entity); - } - - @Override - public T getById(Serializable id) { - return serviceImpl.getById(id); - } - - @Override - public T getOne(Wrapper queryWrapper, boolean throwEx) { - return serviceImpl.getOne(queryWrapper, throwEx); - } - - @Override - public Map getMap(Wrapper queryWrapper) { - return serviceImpl.getMap(queryWrapper); - } - - @Override - public V getObj(Wrapper queryWrapper, Function mapper) { - return serviceImpl.getObj(queryWrapper, mapper); - } - - @Override - public int count(Wrapper queryWrapper) { - return serviceImpl.count(queryWrapper); - } - - @Override - public List list(Wrapper queryWrapper) { - return serviceImpl.list(queryWrapper); - } - - - @Override - public List> listMaps(Wrapper queryWrapper) { - return serviceImpl.listMaps(queryWrapper); - } - - @Override - public List listObjs(Wrapper queryWrapper, Function mapper) { - return serviceImpl.listObjs(queryWrapper, mapper); - } - - @Override - public M getBaseMapper() { - return this.baseMapper; - } - - @Override - public Class getEntityClass() { - return serviceImpl.getEntityClass(); - } - -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/mybatisplus/SpringBootMybatisPlusConfig.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/mybatisplus/SpringBootMybatisPlusConfig.java deleted file mode 100644 index 0eeac071ff3111451a8f759f607a751c156fa605..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/mybatisplus/SpringBootMybatisPlusConfig.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.gitee.starblues.extension.mybatis.mybatisplus; - -import com.baomidou.mybatisplus.core.MybatisConfiguration; -import com.baomidou.mybatisplus.core.config.GlobalConfig; -import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; -import com.gitee.starblues.extension.mybatis.MybatisCommonConfig; - -/** - * springboot mybatis plus 配置接口 - * @author starBlues - * @version 2.3 - */ -public interface SpringBootMybatisPlusConfig extends MybatisCommonConfig { - - - - /** - * 插件自主配置Mybatis-Plus的MybatisSqlSessionFactoryBean - * MybatisSqlSessionFactoryBean 具体配置说明参考 Mybatis-plus 官网 - * @param sqlSessionFactoryBean MybatisSqlSessionFactoryBean - */ - default void oneselfConfig(MybatisSqlSessionFactoryBean sqlSessionFactoryBean){ - } - - /** - * 重写设置配置 - * 只有 enableOneselfConfig 返回 false, 实现该方法才生效 - * @param configuration 当前 MybatisConfiguration - * @param globalConfig 当前全局配置GlobalConfig - */ - default void reSetMainConfig(MybatisConfiguration configuration, GlobalConfig globalConfig){ - - } - - -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/tkmyabtis/SpringBootTkMybatisConfig.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/tkmyabtis/SpringBootTkMybatisConfig.java deleted file mode 100644 index eeeb9ef541d6aa68cbe7e79869f2718334cd6a12..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/tkmyabtis/SpringBootTkMybatisConfig.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.gitee.starblues.extension.mybatis.tkmyabtis; - -import com.gitee.starblues.extension.mybatis.MybatisCommonConfig; -import org.apache.ibatis.session.Configuration; -import org.mybatis.spring.SqlSessionFactoryBean; -import tk.mybatis.mapper.entity.Config; - -/** - * springboot tk-mybatis 配置接口 - * @author starBlues - * @version 2.3 - */ -public interface SpringBootTkMybatisConfig extends MybatisCommonConfig { - - - - /** - * 插件自主配置Mybatis的 SqlSessionFactoryBean - * SqlSessionFactoryBean 具体配置说明参考 Mybatis 官网 - * @param sqlSessionFactoryBean SqlSessionFactoryBean - * @param config 插件自主配置tk的 Config 具体配置说明参考 https://gitee.com/free/Mapper/wikis/1.1-java?sort_id=208196 - */ - default void oneselfConfig(SqlSessionFactoryBean sqlSessionFactoryBean, Config config){ - } - - /** - * 重写配置当前跟随主程序的配置 - * 只有 enableOneselfConfig 返回 false, 实现该方法才生效 - * @param config 插件自主配置tk的 Config 具体配置说明参考 https://gitee.com/free/Mapper/wikis/1.1-java?sort_id=208196 - * @param configuration Mybatis Configuration 的配置 - */ - default void reSetMainConfig(Configuration configuration, Config config){ - - } - -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/tkmyabtis/TkMybatisProcessor.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/tkmyabtis/TkMybatisProcessor.java deleted file mode 100644 index 1005696a86edaee9159b5dcc433b5b956f6f8421..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-mybatis/src/main/java/com/gitee/starblues/extension/mybatis/tkmyabtis/TkMybatisProcessor.java +++ /dev/null @@ -1,146 +0,0 @@ -package com.gitee.starblues.extension.mybatis.tkmyabtis; - -import com.gitee.starblues.extension.mybatis.*; -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.pipe.bean.PluginBeanRegistrarExtend; -import com.gitee.starblues.utils.SpringBeanUtils; -import org.apache.ibatis.io.Resources; -import org.apache.ibatis.mapping.DatabaseIdProvider; -import org.apache.ibatis.plugin.Interceptor; -import org.apache.ibatis.session.Configuration; -import org.apache.ibatis.session.SqlSessionFactory; -import org.mybatis.spring.SqlSessionFactoryBean; -import org.mybatis.spring.SqlSessionTemplate; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.config.BeanDefinitionHolder; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.GenericBeanDefinition; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.io.Resource; -import tk.mybatis.mapper.entity.Config; -import tk.mybatis.mapper.mapperhelper.MapperHelper; -import tk.mybatis.spring.mapper.MapperFactoryBean; - -/** - * tk-mybatis处理者 - * @author starBlues - * @version 2.4.0 - */ -public class TkMybatisProcessor implements PluginBeanRegistrarExtend { - - private static final Logger LOGGER = LoggerFactory.getLogger(TkMybatisProcessor.class); - - private final MapperFactoryBean mapperFactoryBean = new MapperFactoryBean(); - - public TkMybatisProcessor() { - } - - @Override - public String key() { - return "TkMybatisProcessor"; - } - - - @Override - public void registry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - SpringBootTkMybatisConfig config = SpringBeanUtils.getObjectByInterfaceClass( - pluginRegistryInfo.getConfigSingletons(), - SpringBootTkMybatisConfig.class); - if(config == null){ - return; - } - - SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); - - Config tkConfig = null; - if(config.enableOneselfConfig()){ - tkConfig = new Config(); - config.oneselfConfig(factory, tkConfig); - } else { - GenericApplicationContext mainApplicationContext = pluginRegistryInfo.getMainApplicationContext(); - PluginFollowCoreConfig followCoreConfig = new PluginFollowCoreConfig(mainApplicationContext); - factory.setDataSource(followCoreConfig.getDataSource()); - Configuration configuration = followCoreConfig.getConfiguration(SpringBootMybatisExtension.Type.TK_MYBATIS); - factory.setConfiguration(configuration); - Interceptor[] interceptor = followCoreConfig.getInterceptor(); - if(interceptor != null && interceptor.length > 0){ - factory.setPlugins(interceptor); - } - DatabaseIdProvider databaseIdProvider = followCoreConfig.getDatabaseIdProvider(); - if(databaseIdProvider != null){ - factory.setDatabaseIdProvider(databaseIdProvider); - } - if(mainApplicationContext.getBeanNamesForType(Config.class, - false, false).length > 0){ - tkConfig = mainApplicationContext.getBean(Config.class); - } - config.reSetMainConfig(configuration, tkConfig); - } - - MapperHelper mapperHelper = new MapperHelper(); - if(tkConfig != null){ - mapperHelper.setConfig(tkConfig); - } - - - PluginResourceFinder pluginResourceFinder = new PluginResourceFinder(pluginRegistryInfo); - - Class[] aliasesClasses = pluginResourceFinder.getAliasesClasses(config.entityPackage()); - if(aliasesClasses != null && aliasesClasses.length > 0){ - factory.setTypeAliases(aliasesClasses); - } - - Resource[] xmlResource = pluginResourceFinder.getXmlResource(config.xmlLocationsMatch()); - if(xmlResource != null && xmlResource.length > 0){ - factory.setMapperLocations(xmlResource); - } - ClassLoader pluginClassLoader = pluginRegistryInfo.getPluginClassLoader(); - ClassLoader defaultClassLoader = Resources.getDefaultClassLoader(); - ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); - try { - Resources.setDefaultClassLoader(pluginClassLoader); - SqlSessionFactory sqlSessionFactory = factory.getObject(); - if(sqlSessionFactory == null){ - throw new Exception("Get tk-mybatis sqlSessionFactory is null"); - } - SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory); - // 用于解决Tk中MsUtil的ClassLoader的问题 - Thread.currentThread().setContextClassLoader(pluginClassLoader); - MapperHandler mapperHandler = new MapperHandler(); - mapperHandler.processMapper(pluginRegistryInfo, (holder, mapperClass) -> { - processMapper(holder, mapperClass, mapperHelper, sqlSessionFactory, sqlSessionTemplate); - }); - CommonRegister.commonRegister(pluginRegistryInfo, sqlSessionFactory, sqlSessionTemplate); - } finally { - Resources.setDefaultClassLoader(defaultClassLoader); - Thread.currentThread().setContextClassLoader(contextClassLoader); - } - } - - /** - * 处理Mapper接口 - * @param holder ignore - * @param mapperClass ignore - * @param mapperHelper ignore - * @param sqlSessionFactory ignore - * @param sqlSessionTemplate ignore - */ - private void processMapper(BeanDefinitionHolder holder, - Class mapperClass, - MapperHelper mapperHelper, - SqlSessionFactory sqlSessionFactory, - SqlSessionTemplate sqlSessionTemplate){ - GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition(); - definition.getConstructorArgumentValues().addGenericArgumentValue(mapperClass); - definition.setBeanClass(this.mapperFactoryBean.getClass()); - //设置通用 Mapper - definition.getPropertyValues().add("mapperHelper", mapperHelper); - definition.getPropertyValues().add("addToConfig", true); - definition.getPropertyValues().add("sqlSessionFactory", sqlSessionFactory); - definition.getPropertyValues().add("sqlSessionTemplate", sqlSessionTemplate); - definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); - } - - -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/pom.xml b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/pom.xml deleted file mode 100644 index 70af63ffa2d2985965b0b7260a5efa8391173098..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/pom.xml +++ /dev/null @@ -1,235 +0,0 @@ - - - 4.0.0 - - - org.sonatype.oss - oss-parent - 7 - - - - com.gitee.starblues - springboot-plugin-framework-extension-resources - 2.4.6-RELEASE - jar - - 插件扩展-通过url读取插件中的静态资源 - - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - repo - - - - - https://gitee.com/starblues/springboot-plugin-framework-parent - scm:https://gitee.com/starblues/springboot-plugin-framework-parent.git - scm:https://gitee.com/starblues/springboot-plugin-framework-parent.git - 1.0 - - - - - sonatype-nexus-snapshots - oss Snapshots Repository - https://oss.sonatype.org/content/repositories/snapshots - - - sonatype-nexus-staging - oss Staging Repository - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - - - StarBlues - starblues@foxmail.com - https://gitee.com/starblues/ - - - - - - - 1.8 - UTF-8 - - 3.8.1 - 3.1.0 - 3.1.0 - 3.1.0 - 1.6 - - 5.0.7.RELEASE - 4.0.1 - - 2.4.6-RELEASE - 2.0.3.RELEASE - 2.1.1.RELEASE - - 4.11 - - - - - - - com.gitee.starblues - springboot-plugin-framework - ${springboot-plugin-framework.version} - provided - - - - org.springframework.boot - spring-boot-starter-thymeleaf - ${spring-boot-starter-thymeleaf.version} - provided - - - - org.springframework - spring-webmvc - ${spring-version} - provided - - - - javax.servlet - javax.servlet-api - ${javax.servlet-api.version} - provided - true - - - - junit - junit - ${junit.version} - test - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - ${java.version} - ${java.version} - - - - - org.apache.maven.plugins - maven-assembly-plugin - ${maven-assembly-plugin.version} - - - jar-with-dependencies - - ${project.artifactId}-${project.version} - false - false - - - true - true - - - - - - make-assembly - package - - single - - - - - - - org.apache.maven.plugins - maven-source-plugin - ${maven-source-plugin.version} - - - package - - jar-no-fork - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - ${maven-javadoc-plugin.version} - - ${plugin.skip} - - - - package - - jar - - - - - - - org.apache.maven.plugins - maven-gpg-plugin - ${maven-gpg-plugin.version} - - ${plugin.skip} - - - - sign-artifacts - verify - - sign - - - - - - - - - - - - - dev - - true - - - true - - - - - release - - false - - - - - - \ No newline at end of file diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/PluginResourceResolverProcess.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/PluginResourceResolverProcess.java deleted file mode 100644 index c930a3a7603789c4bda971ca3496c8fbcb4c6ebd..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/PluginResourceResolverProcess.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.gitee.starblues.extension.resources; - -import com.gitee.starblues.extension.resources.resolver.PluginResourceResolver; -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.post.PluginPostProcessorExtend; -import com.gitee.starblues.utils.OrderPriority; -import com.gitee.starblues.utils.SpringBeanUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.context.properties.bind.Bindable; -import org.springframework.boot.context.properties.bind.Binder; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.util.ObjectUtils; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * 插件资源处理器 - * - * @author starBlues - * @version 2.4.0 - */ -public class PluginResourceResolverProcess implements PluginPostProcessorExtend { - - private static final Logger LOGGER = LoggerFactory.getLogger(PluginResourceResolverProcess.class); - private static final String KEY = "PluginResourceResolverProcess"; - - - PluginResourceResolverProcess() { - } - - @Override - public String key() { - return KEY; - } - - @Override - public OrderPriority order() { - return OrderPriority.getMiddlePriority(); - } - - @Override - public void initialize() throws Exception { - - } - - @Override - public synchronized void registry(List pluginRegistryInfos) throws Exception { - for (PluginRegistryInfo pluginRegistryInfo : pluginRegistryInfos) { - if(pluginRegistryInfo == null){ - continue; - } - String pluginId = pluginRegistryInfo.getPluginWrapper().getPluginId(); - try { - // 直接从配置文件获取, 后续版本移除从实现类中获取配置 - Set locations = pluginRegistryInfo.getPluginBinder() - .bind(PropertyKey.STATIC_LOCATIONS, Bindable.setOf(String.class)) - .orElseGet(()->null); - if(ObjectUtils.isEmpty(locations)){ - StaticResourceConfig config = SpringBeanUtils.getObjectByInterfaceClass( - pluginRegistryInfo.getConfigSingletons(), - StaticResourceConfig.class); - if(config != null){ - locations = config.locations(); - } - } - if(ObjectUtils.isEmpty(locations)){ - return; - } - PluginResourceResolver.parse(pluginRegistryInfo, locations); - } catch (Exception e){ - LOGGER.error("Parse plugin '{}' static resource failure.", pluginId, e); - } - } - } - - @Override - public void unRegistry(List pluginRegistryInfos) throws Exception { - for (PluginRegistryInfo pluginRegistryInfo : pluginRegistryInfos) { - try { - PluginResourceResolver.remove(pluginRegistryInfo.getPluginWrapper().getPluginId()); - } catch (Exception e){ - LOGGER.error("Remove plugin '{}' static resource failure.", - pluginRegistryInfo.getPluginWrapper().getPluginId(), - e); - } - } - } -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/PropertyKey.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/PropertyKey.java deleted file mode 100644 index 385f9873b7dd1e5b09fe36c40179a780b4eddbf6..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/PropertyKey.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.gitee.starblues.extension.resources; - -/** - * 配置文件key - * - * @author starBlues - * @version 2.4.3 - */ -public class PropertyKey { - - /** - * 静态文件路径 - * classpath: static/ - * file: D://path/test - */ - public final static String STATIC_LOCATIONS = "plugin.static.locations"; - - - /** - * Thymeleaf 配置前缀 - */ - public final static String THYMELEAF_CONFIG = "plugin.thymeleaf"; -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/StaticResourceConfig.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/StaticResourceConfig.java deleted file mode 100644 index 917a9f4002cab072fbe9465fc7c75ce4145d3fbe..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/StaticResourceConfig.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.gitee.starblues.extension.resources; - -import java.util.Set; - -/** - * 插件静态资源的配置 - * 建议从配置文件中进行配置: - * plugin: - * static: - * locations: - * - classpath: static/ - * - file: D://path/test - * @author starBlues - * @version 2.3 - */ -@Deprecated -public interface StaticResourceConfig { - - - /** - * 静态文件路径 - * classpath: static/ - * file: D://path/test - * @return 路径集合 - */ - Set locations(); - - - -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/StaticResourceExtension.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/StaticResourceExtension.java deleted file mode 100644 index 1f92529e42a4f32747e3e04cbc3381402f02f026..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/StaticResourceExtension.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.gitee.starblues.extension.resources; - -import com.gitee.starblues.extension.AbstractExtension; -import com.gitee.starblues.extension.resources.resolver.ResourceWebMvcConfigurer; -import com.gitee.starblues.extension.resources.thymeleaf.ThymeleafProcessor; -import com.gitee.starblues.factory.process.pipe.PluginPipeProcessorExtend; -import com.gitee.starblues.factory.process.post.PluginPostProcessorExtend; -import org.springframework.context.ApplicationContext; -import org.springframework.http.CacheControl; -import org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -import java.util.*; -import java.util.concurrent.TimeUnit; - -/** - * 插件静态资源访问的扩展插件 - * - * @author starBlues - * @version 2.4.3 - */ -public class StaticResourceExtension extends AbstractExtension { - - private final static String KEY = "StaticResourceExtension"; - - /** - * 访问插件静态资源前缀。默认为: static-plugin。 - */ - private static String pluginStaticResourcePathPrefix = "static-plugin"; - - /** - * 访问静态资源的缓存控制。默认最大1小时。主要针对http协议的缓存。 - */ - private static CacheControl pluginStaticResourcesCacheControl = - CacheControl.maxAge(1, TimeUnit.HOURS).cachePublic(); - - private final Set includes = new HashSet<>(1); - - public StaticResourceExtension(){ - } - - public StaticResourceExtension(Include... includes){ - if(includes != null){ - this.includes.addAll(Arrays.asList(includes)); - } - } - - @Override - public String key() { - return KEY; - } - - @Override - public void initialize(ApplicationContext mainApplicationContext) throws Exception{ - WebMvcConfigurer webMvcConfigurer = new ResourceWebMvcConfigurer(); - List webMvcConfigurers = new ArrayList<>(); - webMvcConfigurers.add(webMvcConfigurer); - DelegatingWebMvcConfiguration support = - mainApplicationContext.getBean(DelegatingWebMvcConfiguration.class); - support.setConfigurers(webMvcConfigurers); - } - - @Override - public List getPluginPipeProcessor(ApplicationContext mainApplicationContext) { - if(includes.contains(Include.THYMELEAF)){ - final List pluginPipeProcessorExtends = new ArrayList<>(1); - pluginPipeProcessorExtends.add(new ThymeleafProcessor()); - return pluginPipeProcessorExtends; - } - return null; - } - - @Override - public List getPluginPostProcessor(ApplicationContext mainApplicationContext) { - final List pluginPostProcessorExtends = new ArrayList<>(); - pluginPostProcessorExtends.add(new PluginResourceResolverProcess()); - return pluginPostProcessorExtends; - } - - /** - * 设置访问插件静态资源前缀 - * @param pluginStaticResourcePathPrefix 静态资源前缀。默认为: static-plugin。 - * @return StaticResourceExtension - */ - public StaticResourceExtension setPathPrefix(String pluginStaticResourcePathPrefix){ - if(pluginStaticResourcePathPrefix != null && !"".equals(pluginStaticResourcePathPrefix)){ - StaticResourceExtension.pluginStaticResourcePathPrefix = pluginStaticResourcePathPrefix; - } - return this; - } - - /** - * 设置缓存控制 - * @param pluginStaticResourcesCacheControl 访问静态资源的缓存控制。默认最大1小时。主要针对http协议的缓存。 - * @return StaticResourceExtension - */ - public StaticResourceExtension setCacheControl(CacheControl pluginStaticResourcesCacheControl){ - if(pluginStaticResourcesCacheControl == null){ - StaticResourceExtension.pluginStaticResourcesCacheControl = null; - } else { - StaticResourceExtension.pluginStaticResourcesCacheControl = pluginStaticResourcesCacheControl; - } - return this; - } - - public static String getPluginStaticResourcePathPrefix() { - return pluginStaticResourcePathPrefix; - } - - public static CacheControl getPluginStaticResourcesCacheControl() { - return pluginStaticResourcesCacheControl; - } - - - public enum Include{ - THYMELEAF - } -} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/thymeleaf/SpringBootThymeleafConfig.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/thymeleaf/SpringBootThymeleafConfig.java deleted file mode 100644 index 4001c58aafa1d016ebc69ba3c2038607a4f3d36d..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-resources/src/main/java/com/gitee/starblues/extension/resources/thymeleaf/SpringBootThymeleafConfig.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.gitee.starblues.extension.resources.thymeleaf; - - -/** - * @author starBlues - * @version 2.3 - */ -public interface SpringBootThymeleafConfig { - - - void config(ThymeleafConfig thymeleafConfig); - - -} diff --git a/springboot-plugin-framework/pom.xml b/springboot-plugin-framework/pom.xml deleted file mode 100644 index da29449ba45ac6cd072a2b2063df06fa1249b904..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/pom.xml +++ /dev/null @@ -1,276 +0,0 @@ - - - 4.0.0 - - - org.sonatype.oss - oss-parent - 7 - - - com.gitee.starblues - springboot-plugin-framework - jar - 2.4.6-RELEASE - - spring boot 插件式开发集成包 - - - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - repo - - - - - https://gitee.com/starblues/springboot-plugin-framework-parent - scm:https://gitee.com/starblues/springboot-plugin-framework-parent.git - scm:https://gitee.com/starblues/springboot-plugin-framework-parent.git - 1.0 - - - - - sonatype-nexus-snapshots - oss Snapshots Repository - https://oss.sonatype.org/content/repositories/snapshots - - - sonatype-nexus-staging - oss Staging Repository - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - - - StarBlues - starblues@foxmail.com - https://gitee.com/starblues/ - - - - - 1.8 - UTF-8 - - 3.8.1 - 3.1.0 - 3.1.0 - 3.1.0 - 1.6 - - 3.6.0 - 2.10.1 - 1.7.7 - - 5.0.7.RELEASE - 2.4.1 - 2.10.5 - 1.5.2 - 4.0.1 - 4.11 - - - - - - org.pf4j - pf4j - ${pf4j-version} - - - org.slf4j - slf4j-api - - - - - - com.fasterxml.jackson.core - jackson-databind - ${jackson.version} - - - - com.fasterxml.jackson.dataformat - jackson-dataformat-yaml - ${jackson.version} - - - - org.slf4j - slf4j-api - ${slf4j.version} - - - - org.springframework.boot - spring-boot - ${spring-boot-version} - provided - - - - org.springframework - spring-webmvc - ${spring-version} - provided - - - - io.springfox - springfox-spring-web - ${swagger-spring-web.version} - provided - - - - org.springdoc - springdoc-openapi-ui - ${springdoc.version} - provided - - - - javax.servlet - javax.servlet-api - ${javax.servlet-api.version} - provided - true - - - - junit - junit - ${junit.version} - test - - - - org.springframework.boot - spring-boot-starter-websocket - ${spring-boot-version} - provided - - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - ${java.version} - ${java.version} - - - - - org.apache.maven.plugins - maven-assembly-plugin - ${maven-assembly-plugin.version} - - - jar-with-dependencies - - ${project.artifactId}-${project.version} - false - false - - - true - true - - - - - - make-assembly - package - - single - - - - - - - org.apache.maven.plugins - maven-source-plugin - ${maven-source-plugin.version} - - - package - - jar-no-fork - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - ${maven-javadoc-plugin.version} - - ${plugin.skip} - - - - package - - jar - - - - - - - org.apache.maven.plugins - maven-gpg-plugin - ${maven-gpg-plugin.version} - - ${plugin.skip} - - - - sign-artifacts - verify - - sign - - - - - - - - - - dev - - true - - - true - - - - - release - - false - - - - - - \ No newline at end of file diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/annotation/ConfigDefinition.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/annotation/ConfigDefinition.java deleted file mode 100644 index 42109c7f245d021ae5ebcd2c8029a47145d2b8f2..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/annotation/ConfigDefinition.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.gitee.starblues.annotation; - -import java.lang.annotation.*; - -/** - * 插件配置对应的bean定义注解 - * 如果存在配置文件, 则进行属性自定义 - * 如果未依赖配置文件, 则直接定义注解即可 - * @author starBlues - * @version 2.4.0 - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface ConfigDefinition { - - - /** - * 插件中的配置文件的名称, 新版本替换 value 值 - * @return String - */ - String fileName() default ""; - - /** - * 开发环境下文件后缀 - * 如果文件名称为: xxx.yml, 根据当前配置(当前配置为-dev)在开发环境下文件后缀为: xxx-dev.yml - * @return 开发环境下文件名称后缀, 比如 dev - */ - String devSuffix() default ""; - - /** - * 生产环境下文件后缀 - * 如果文件名称为: xxx.yml, 根据当前配置(当前配置为-prod)在生产环境下文件后缀为: xxx-prod.yml - * @return 生产环境下文件名称后缀, 比如 -prod - */ - String prodSuffix() default ""; - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/extension/AbstractExtension.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/extension/AbstractExtension.java deleted file mode 100644 index c4cc71a91b36d5f63b28ee7d82b7b862e8eb702a..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/extension/AbstractExtension.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.gitee.starblues.extension; - -import com.gitee.starblues.factory.process.pipe.PluginPreProcessorExtend; -import com.gitee.starblues.factory.process.pipe.bean.PluginBeanRegistrarExtend; -import com.gitee.starblues.integration.application.PluginApplication; -import com.gitee.starblues.factory.process.pipe.loader.PluginResourceLoader; -import com.gitee.starblues.factory.process.pipe.PluginPipeProcessorExtend; -import com.gitee.starblues.factory.process.pipe.classs.PluginClassGroupExtend; -import com.gitee.starblues.factory.process.post.PluginPostProcessorExtend; -import org.springframework.context.ApplicationContext; - -import java.util.List; - -/** - * 抽象的扩展工厂 - * - * @author starBlues - * @version 2.4.0 - */ -public abstract class AbstractExtension { - - protected PluginApplication pluginApplication; - - public void setPluginApplication(PluginApplication pluginApplication) { - this.pluginApplication = pluginApplication; - } - - /** - * 扩展唯一的key - * @return String - */ - public abstract String key(); - - /** - * 该扩展初始化的操作 - * 主要是在插件初始化阶段被调用 - * @param mainApplicationContext 主程序ApplicationContext - * @throws Exception 初始化异常 - */ - public void initialize(ApplicationContext mainApplicationContext) throws Exception{ - } - - /** - * 返回插件的资源加载者。 - * 主要是加载插件中的某些资源,比如文件、图片等。 - * @return List PluginResourceLoader - */ - public List getPluginResourceLoader(){ - return null; - } - - /** - * 返回扩展的插件中的类分组器。 - * 该扩展主要是对插件中的Class文件分组,然后供 PluginPipeProcessor、PluginPostProcessor 阶段使用。 - * @param mainApplicationContext 主程序ApplicationContext - * @return List PluginPipeProcessorExtend - */ - public List getPluginClassGroup(ApplicationContext mainApplicationContext){ - return null; - } - - /** - * 返回扩展的插件前置处理者。 - * 该扩展主要是对每一个插件进行处理 - * @param mainApplicationContext 主程序ApplicationContext - * @return List PluginPipeProcessorExtend - */ - public List getPluginPreProcessor(ApplicationContext mainApplicationContext){ - return null; - } - - /** - * 返回扩展的bean定义注册者扩展 - * 该扩展主要是对每一个插件进行处理 - * @param mainApplicationContext 主程序ApplicationContext - * @return List PluginPipeProcessorExtend - */ - public List getPluginBeanRegistrar(ApplicationContext mainApplicationContext){ - return null; - } - - /** - * 返回扩展的流插件处理者。 - * 该扩展主要是对每一个插件进行处理 - * @param mainApplicationContext 主程序ApplicationContext - * @return List PluginPipeProcessorExtend - */ - public List getPluginPipeProcessor(ApplicationContext mainApplicationContext){ - return null; - } - - /** - * 返回扩展的插件后置处理者。 - * 该扩展主要是对全部插件进行处理。 - * @param mainApplicationContext 主程序ApplicationContext - * @return List PluginPostProcessorExtend - */ - public List getPluginPostProcessor(ApplicationContext mainApplicationContext){ - return null; - } - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/extension/ExtensionFactory.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/extension/ExtensionFactory.java deleted file mode 100644 index a3b6fd3d5d33391c5d14474ff34108116f197b3d..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/extension/ExtensionFactory.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.gitee.starblues.extension; - -import com.gitee.starblues.utils.SpringBeanUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.util.StringUtils; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -/** - * 静态的扩展工厂 - * - * @author starBlues - * @version 2.4.0 - */ -public class ExtensionFactory { - - private static final Logger LOG = LoggerFactory.getLogger(ExtensionFactory.class); - - /** - * 扩展工厂。key 扩展的key. 值为扩展的实现 - */ - private final static Map PLUGIN_EXTENSION_MAP = new ConcurrentHashMap<>(); - - private ExtensionFactory(){} - - - - public static void addExtension(AbstractExtension abstractExtension){ - if(abstractExtension == null){ - LOG.warn("add failure, abstractExtension is null"); - return; - } - String key = abstractExtension.key(); - if(StringUtils.isEmpty(key)){ - LOG.error("add failure, key is empty"); - } - PLUGIN_EXTENSION_MAP.put(key, abstractExtension); - } - - /** - * 得到PluginControllerProcessorExtend的实现 - * @param mainApplicationContext 主程序的 ApplicationContext - * @return PluginControllerProcessorExtend 的实现对象集合 - */ - public static List getPluginControllerProcessorExtend(ApplicationContext mainApplicationContext){ - return SpringBeanUtils.getBeans(mainApplicationContext, - PluginControllerProcessorExtend.class); - } - - - static Map getPluginExtension() { - return Collections.unmodifiableMap(PLUGIN_EXTENSION_MAP); - } - - - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/extension/ExtensionInitializer.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/extension/ExtensionInitializer.java deleted file mode 100644 index 9c5cc96b78f55dd577877c5d9fac88ee7b17939a..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/extension/ExtensionInitializer.java +++ /dev/null @@ -1,161 +0,0 @@ -package com.gitee.starblues.extension; - -import com.gitee.starblues.factory.process.pipe.PluginPipeProcessorExtend; -import com.gitee.starblues.factory.process.pipe.PluginPreProcessorExtend; -import com.gitee.starblues.factory.process.pipe.bean.PluginBeanRegistrarExtend; -import com.gitee.starblues.factory.process.pipe.classs.PluginClassGroupExtend; -import com.gitee.starblues.factory.process.post.PluginPostProcessorExtend; -import com.gitee.starblues.factory.process.pipe.loader.PluginResourceLoader; -import com.gitee.starblues.utils.CommonUtils; -import com.gitee.starblues.utils.OrderPriority; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationContext; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; -import java.util.function.Function; - -/** - * 静态的扩展初始化器 - * - * @author starBlues - * @version 2.4.0 - */ -public class ExtensionInitializer { - - - private static final Logger LOG = LoggerFactory.getLogger(ExtensionInitializer.class); - - private static final AtomicBoolean IS_INIT = new AtomicBoolean(false); - - private static final List RESOURCE_LOADERS_EXTENDS = new ArrayList<>(); - private static final List PIPE_PROCESSOR_EXTENDS = new ArrayList<>(); - private static final List BEAN_REGISTRAR_EXTEND = new ArrayList<>(); - private static final List CLASS_GROUP_EXTENDS = new ArrayList<>(); - private static final List PRE_PROCESSOR_EXTENDS = new ArrayList<>(); - private static final List POST_PROCESSOR_EXTENDS = new ArrayList<>(); - - private ExtensionInitializer(){ - - } - - - public static synchronized void initialize(ApplicationContext applicationContext){ - if(applicationContext == null){ - LOG.error("ApplicationContext is null, cannot initialize"); - return; - } - if(IS_INIT.get()){ - throw new RuntimeException("The extension has been initialized"); - } - Map pluginExtension = ExtensionFactory.getPluginExtension(); - for (Map.Entry entry : pluginExtension.entrySet()){ - AbstractExtension abstractExtension = entry.getValue(); - if(abstractExtension == null){ - continue; - } - try { - abstractExtension.initialize(applicationContext); - initialize(abstractExtension, applicationContext); - } catch (Exception e) { - LOG.error("Plugin extension '{}' initialize exception. {}", abstractExtension.key(), e.getMessage(), e); - } - } - IS_INIT.set(true); - } - - private static void initialize(AbstractExtension abstractExtension, ApplicationContext applicationContext){ - StringBuilder debug = new StringBuilder(); - debug.append("Plugin extension '").append(abstractExtension.key()).append("'") - .append(" are ["); - iteration(abstractExtension.getPluginResourceLoader(), extend->{ - RESOURCE_LOADERS_EXTENDS.add(extend); - debug.append(extend.key()).append("、"); - }, bean -> bean.order()); - - iteration(abstractExtension.getPluginPreProcessor(applicationContext), extend->{ - PRE_PROCESSOR_EXTENDS.add(extend); - debug.append(extend.key()).append("、"); - }, bean -> bean.order()); - - iteration(abstractExtension.getPluginBeanRegistrar(applicationContext), extend->{ - BEAN_REGISTRAR_EXTEND.add(extend); - debug.append(extend.key()).append("、"); - }, null); - - iteration(abstractExtension.getPluginPipeProcessor(applicationContext), extend->{ - PIPE_PROCESSOR_EXTENDS.add(extend); - debug.append(extend.key()).append("、"); - }, bean -> bean.order()); - - iteration(abstractExtension.getPluginClassGroup(applicationContext), extend->{ - CLASS_GROUP_EXTENDS.add(extend); - debug.append(extend.key()).append("、"); - }, null); - - iteration(abstractExtension.getPluginPostProcessor(applicationContext), extend->{ - POST_PROCESSOR_EXTENDS.add(extend); - debug.append(extend.key()); - }, bean -> bean.order()); - - debug.append("] is registered"); - LOG.info("Plugin extension '{}' is registered", abstractExtension.key()); - LOG.debug(debug.toString()); - } - - - public static List getResourceLoadersExtends() { - return RESOURCE_LOADERS_EXTENDS; - } - - public static List getPreProcessorExtends() { - return PRE_PROCESSOR_EXTENDS; - } - - public static List getPipeProcessorExtends() { - return PIPE_PROCESSOR_EXTENDS; - } - - public static List getPluginBeanRegistrarExtends() { - return BEAN_REGISTRAR_EXTEND; - } - - public static List getClassGroupExtends() { - return CLASS_GROUP_EXTENDS; - } - - public static List getPostProcessorExtends() { - return POST_PROCESSOR_EXTENDS; - } - - /** - * 迭代器 - * @param list 当前处理的集合 - * @param consumer 消费集合中的数据项 - * @param order 排序集合。传入 null 表示不需要排序 - */ - private static void iteration(List list, Consumer consumer, final Function order){ - if(list == null || list.isEmpty()){ - return; - } - if(order != null){ - list.stream() - .filter(t -> t != null) - .sorted(CommonUtils.orderPriority(order)) - .forEach(consumer); - ; - } else { - for (T t : list) { - if(t != null){ - consumer.accept(t); - } - } - } - } - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/extension/PluginControllerProcessorExtend.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/extension/PluginControllerProcessorExtend.java deleted file mode 100644 index a5738b25f39804b6ad0d530a1f03b08056088a07..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/extension/PluginControllerProcessorExtend.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.gitee.starblues.extension; - - -import com.gitee.starblues.factory.process.post.bean.model.ControllerWrapper; - -import java.util.List; - -/** - * 可扩展的 controller 处理者 - * - * @author starBlues - * @version 2.4.0 - */ -public interface PluginControllerProcessorExtend { - - /** - * 初始化 - */ - void initialize(); - - /** - * 注册 - * @param pluginId 插件id - * @param controllerWrappers controller 类集合 - * @throws Exception 异常 - */ - void registry(String pluginId, List controllerWrappers) throws Exception; - - /** - * 注册 - * @param pluginId 插件id - * @param controllerWrappers controller 类集合 - * @throws Exception 异常 - */ - void unRegistry(String pluginId, List controllerWrappers) throws Exception; -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/extension/support/SpringDocControllerProcessor.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/extension/support/SpringDocControllerProcessor.java deleted file mode 100644 index b8c962c7cb9808a0c054e25f1c551f708f64c082..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/extension/support/SpringDocControllerProcessor.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.gitee.starblues.extension.support; - -import com.gitee.starblues.extension.PluginControllerProcessorExtend; -import com.gitee.starblues.factory.process.post.bean.model.ControllerWrapper; -import com.gitee.starblues.utils.ClassUtils; -import com.gitee.starblues.utils.SpringBeanUtils; -import org.springdoc.api.AbstractOpenApiResource; -import org.springdoc.core.OpenAPIService; -import org.springframework.context.ApplicationContext; - -import java.util.List; - -/** - * @author starBlues - * @version 2.4.0 - */ -public class SpringDocControllerProcessor implements PluginControllerProcessorExtend { - - private final ApplicationContext applicationContext; - - private List> restControllers; - private OpenAPIService openAPIService; - - public SpringDocControllerProcessor(ApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } - - - @Override - public void initialize() { - AbstractOpenApiResource openApiResource = SpringBeanUtils.getExistBean(applicationContext, AbstractOpenApiResource.class); - if(openApiResource == null){ - return; - } - try { - restControllers = ClassUtils.getReflectionField(openApiResource, - "ADDITIONAL_REST_CONTROLLERS"); - } catch (IllegalAccessException e) { - restControllers = null; - } - openAPIService = SpringBeanUtils.getExistBean(applicationContext, OpenAPIService.class); - } - - @Override - public void registry(String pluginId, List controllerWrappers) throws Exception { - if(restControllers != null){ - for (ControllerWrapper controllerWrapper : controllerWrappers) { - restControllers.add(controllerWrapper.getBeanClass()); - } - refresh(); - } - } - - @Override - public void unRegistry(String pluginId, List controllerWrappers) throws Exception { - if(restControllers != null && !restControllers.isEmpty()){ - for (ControllerWrapper controllerWrapper : controllerWrappers) { - restControllers.remove(controllerWrapper.getBeanClass()); - } - refresh(); - } - } - - private void refresh(){ - if(openAPIService != null){ - openAPIService.setCachedOpenAPI(null); - openAPIService.resetCalculatedOpenAPI(); - } - } - -} 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 deleted file mode 100644 index 5651f397f30157f8cf0e66895be9b5fd75d0fc9a..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/DefaultPluginFactory.java +++ /dev/null @@ -1,202 +0,0 @@ -package com.gitee.starblues.factory; -import com.gitee.starblues.factory.process.pipe.PluginPipeProcessor; -import com.gitee.starblues.factory.process.pipe.PluginPipeProcessorFactory; -import com.gitee.starblues.factory.process.post.PluginPostProcessor; -import com.gitee.starblues.factory.process.post.PluginPostProcessorFactory; -import com.gitee.starblues.integration.IntegrationConfiguration; -import com.gitee.starblues.integration.listener.PluginListener; -import com.gitee.starblues.integration.listener.PluginListenerFactory; -import com.gitee.starblues.integration.listener.SwaggerListeningListener; -import org.pf4j.PluginWrapper; -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.GenericApplicationContext; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * 默认的插件处理者 - * - * @author starBlues - * @version 2.4.4 - */ -public class DefaultPluginFactory implements PluginFactory { - - - /** - * 注册的插件集合 - */ - private final Map registerPluginInfoMap = new HashMap<>(); - private final GenericApplicationContext applicationContext; - private final PluginPipeProcessor pluginPipeProcessor; - private final PluginPostProcessor pluginPostProcessor; - private final PluginListenerFactory pluginListenerFactory; - private final IntegrationConfiguration configuration; - - /** - * 0表示build、1 表示注册、2表示卸载 - */ - private Integer buildType = 0; - private final List buildContainer = new ArrayList<>(); - - public DefaultPluginFactory(ApplicationContext applicationContext) { - this(applicationContext, null); - } - - - public DefaultPluginFactory(ApplicationContext applicationContext, - PluginListenerFactory pluginListenerFactory) { - this.applicationContext = (GenericApplicationContext) applicationContext; - this.pluginPipeProcessor = new PluginPipeProcessorFactory(applicationContext); - this.pluginPostProcessor = new PluginPostProcessorFactory(applicationContext); - - if(pluginListenerFactory == null){ - this.pluginListenerFactory = new PluginListenerFactory(); - } else { - this.pluginListenerFactory = pluginListenerFactory; - } - configuration = applicationContext.getBean(IntegrationConfiguration.class); - } - - - @Override - public void initialize() throws Exception{ - // 新增默认监听者 - addDefaultPluginListener(); - pluginPipeProcessor.initialize(); - pluginPostProcessor.initialize(); - } - - @Override - public synchronized PluginFactory registry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - if(pluginRegistryInfo == null){ - throw new IllegalArgumentException("Parameter:pluginRegistryInfo cannot be null"); - } - PluginWrapper pluginWrapper = pluginRegistryInfo.getPluginWrapper(); - if(registerPluginInfoMap.containsKey(pluginWrapper.getPluginId())){ - throw new IllegalAccessException("The plugin '" - + pluginWrapper.getPluginId() +"' already exists, Can't register"); - } - if(!buildContainer.isEmpty() && buildType == 2){ - throw new IllegalAccessException("Unable to Registry operate. Because there's no build"); - } - try { - pluginPipeProcessor.registry(pluginRegistryInfo); - registerPluginInfoMap.put(pluginWrapper.getPluginId(), pluginRegistryInfo); - buildContainer.add(pluginRegistryInfo); - return this; - } catch (Exception e) { - pluginListenerFactory.registryFailure(pluginWrapper.getPluginId(), e); - throw e; - } finally { - buildType = 1; - } - } - - @Override - public synchronized PluginFactory unRegistry(String pluginId) throws Exception { - PluginRegistryInfo registerPluginInfo = registerPluginInfoMap.get(pluginId); - if(registerPluginInfo == null){ - throw new Exception("Not found plugin '" + pluginId + "' registered"); - } - if(!buildContainer.isEmpty() && buildType == 1){ - throw new Exception("Unable to UnRegistry operate. Because there's no build"); - } - try { - pluginPipeProcessor.unRegistry(registerPluginInfo); - buildContainer.add(registerPluginInfo); - return this; - } catch (Exception e) { - registerPluginInfo.destroy(); - pluginListenerFactory.unRegistryFailure(pluginId, e); - throw e; - } finally { - registerPluginInfoMap.remove(pluginId); - buildType = 2; - } - } - - - - @Override - public synchronized void build() throws Exception { - if(buildContainer.isEmpty()){ - return; - } - // 构建注册的Class插件监听者 - pluginListenerFactory.buildListenerClass(applicationContext); - try { - if(buildType == 1){ - registryBuild(); - } else { - unRegistryBuild(); - } - } finally { - if(buildType != 1){ - for (PluginRegistryInfo pluginRegistryInfo : buildContainer) { - pluginRegistryInfo.destroy(); - } - } - buildContainer.clear(); - buildType = 0; - } - } - - - - @Override - public void addListener(PluginListener pluginListener) { - pluginListenerFactory.addPluginListener(pluginListener); - } - - @Override - public void addListener(Class pluginListenerClass) { - pluginListenerFactory.addPluginListener(pluginListenerClass); - } - - @Override - public void addListener(List pluginListeners) { - if(pluginListeners != null){ - for (PluginListener pluginListener : pluginListeners) { - pluginListenerFactory.addPluginListener(pluginListener); - } - } - } - - /** - * 注册build - */ - private void registryBuild() throws Exception { - pluginPostProcessor.registry(buildContainer); - for (PluginRegistryInfo pluginRegistryInfo : buildContainer) { - pluginListenerFactory.registry( - pluginRegistryInfo.getPluginWrapper().getPluginId(), - pluginRegistryInfo.isFollowingInitial()); - } - } - - /** - * 卸载build - */ - private void unRegistryBuild() throws Exception { - pluginPostProcessor.unRegistry(buildContainer); - for (PluginRegistryInfo pluginRegistryInfo : buildContainer) { - pluginListenerFactory.unRegistry(pluginRegistryInfo.getPluginWrapper().getPluginId()); - } - } - - - - /** - * 添加默认插件监听者 - */ - private void addDefaultPluginListener(){ - if(configuration.enableSwaggerRefresh()){ - pluginListenerFactory.addPluginListener(new SwaggerListeningListener(applicationContext)); - } - } - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/PluginFactory.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/PluginFactory.java deleted file mode 100644 index 90b42122ef22f6115166744836ac31ac7fe90cd7..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/PluginFactory.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.gitee.starblues.factory; - -import com.gitee.starblues.integration.PluginListenerContext; -import org.pf4j.PluginWrapper; - - -/** - * 插件注册者接口 - * - * @author starBlues - * @version 2.4.0 - */ -public interface PluginFactory extends PluginListenerContext { - - /** - * 工厂初始化 - * @throws Exception 初始化异常 - */ - void initialize() throws Exception; - - - /** - * 注册插件。 - * @param pluginRegistryInfo 插件注册信息 - * @return 插件工厂 - * @throws Exception 插件工厂异常 - */ - PluginFactory registry(PluginRegistryInfo pluginRegistryInfo) throws Exception; - - - /** - * 注销插件。 - * @param pluginId 插件id - * @return 插件工厂 - * @throws Exception 插件工厂异常 - */ - PluginFactory unRegistry(String pluginId) throws Exception; - - - /** - * 注册或者注销后的构建调用 - * @throws Exception 插件工厂异常 - */ - void build() throws Exception; -} 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 deleted file mode 100644 index 95cbb0d4535ba8739f505ae3e6864e2ea6a282a3..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/PluginRegistryInfo.java +++ /dev/null @@ -1,346 +0,0 @@ -package com.gitee.starblues.factory; - -import com.gitee.starblues.factory.process.pipe.PluginInfoContainers; -import com.gitee.starblues.factory.process.pipe.loader.ResourceWrapper; -import com.gitee.starblues.integration.IntegrationConfiguration; -import com.gitee.starblues.realize.BasePlugin; -import org.pf4j.PluginManager; -import org.pf4j.PluginWrapper; -import org.pf4j.util.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.boot.context.properties.bind.Binder; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.support.GenericApplicationContext; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -/** - * 注册的插件信息 - * - * @author starBlues - * @version 2.4.1 - */ -public class PluginRegistryInfo { - - private final Logger logger = LoggerFactory.getLogger(this.getClass()); - - private final PluginWrapper pluginWrapper; - private final PluginManager pluginManager; - private final IntegrationConfiguration configuration; - private final GenericApplicationContext mainApplicationContext; - private final AnnotationConfigApplicationContext pluginApplicationContext; - private final Binder pluginBinder; - private final SpringBeanRegister springBeanRegister; - - /** - * 是否跟随主程序启动而初始化 - */ - private final boolean followingInitial; - private final BasePlugin basePlugin; - - - /** - * 扩展存储项 - */ - private final Map extensionMap = new ConcurrentHashMap<>(); - - /** - * 插件中的配置单例bean - */ - private final Set configSingletonObjects = new HashSet<>(4); - - /** - * 插件中的Class - */ - private final List> classes = new ArrayList<>(8); - - /** - * 插件加载的资源 - */ - private final Map pluginLoadResources = new ConcurrentHashMap<>(8); - - /** - * 插件中分类的Class - */ - private final Map>> groupClasses = new ConcurrentHashMap<>(8); - - /** - * 处理者信息 - */ - private final Map processorInfo = new ConcurrentHashMap<>(8); - - - private PluginRegistryInfo(PluginWrapper pluginWrapper, - PluginManager pluginManager, - GenericApplicationContext mainApplicationContext, - boolean followingInitial) { - this.pluginWrapper = pluginWrapper; - this.pluginManager = pluginManager; - this.basePlugin = (BasePlugin) pluginWrapper.getPlugin(); - this.mainApplicationContext = mainApplicationContext; - this.configuration = mainApplicationContext.getBean(IntegrationConfiguration.class); - this.followingInitial = followingInitial; - - ClassLoader pluginClassLoader = basePlugin.getWrapper().getPluginClassLoader(); - // 生成插件ApplicationContext-DefaultListableBeanFactory - DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory(); - this.pluginApplicationContext = new AnnotationConfigApplicationContext(defaultListableBeanFactory); - // 设置插件ApplicationContext的classLoader - this.pluginApplicationContext.setClassLoader(pluginClassLoader); - - this.pluginBinder = Binder.get(this.pluginApplicationContext.getEnvironment()); - this.springBeanRegister = new SpringBeanRegister(pluginApplicationContext); - } - - public static PluginRegistryInfo build(PluginWrapper pluginWrapper, - PluginManager pluginManager, - GenericApplicationContext parentApplicationContext, - boolean followingInitial){ - Objects.requireNonNull(pluginWrapper, "PluginWrapper can't is null"); - Objects.requireNonNull(pluginWrapper, "PluginManager can't is null"); - Objects.requireNonNull(pluginWrapper, "parentApplicationContext can't is null"); - return new PluginRegistryInfo(pluginWrapper, pluginManager, - parentApplicationContext, followingInitial); - } - - - public PluginWrapper getPluginWrapper() { - return pluginWrapper; - } - - public BasePlugin getBasePlugin() { - return basePlugin; - } - - /** - * 添加类到类集合容器 - * @param aClass 类 - */ - public void addClasses(Class aClass){ - if(aClass != null){ - classes.add(aClass); - } - } - - /** - * 清除类集合容器 - */ - public void cleanClasses(){ - classes.clear(); - } - - /** - * 得到类集合容器 - * @return 类集合容器 - */ - public List> getClasses(){ - return Collections.unmodifiableList(classes); - } - - /** - * 添加插件中加载的资源 - * @param key key - * @param resourceWrapper 资源包装者 - */ - public void addPluginLoadResource(String key, ResourceWrapper resourceWrapper){ - if(StringUtils.isNullOrEmpty(key)){ - return; - } - if(resourceWrapper == null){ - return; - } - pluginLoadResources.put(key, resourceWrapper); - } - - /** - * 得到插件中加载的资源 - * @param key 资源key - * @return ResourceWrapper - */ - public ResourceWrapper getPluginLoadResource(String key) { - return pluginLoadResources.get(key); - } - - /** - * 添加分组的类型 - * @param key 分组key - * @param aClass 类 - */ - public void addGroupClasses(String key, Class aClass){ - if(StringUtils.isNullOrEmpty(key)){ - return; - } - if(aClass == null){ - return; - } - List> classes = groupClasses.computeIfAbsent(key, k -> new ArrayList<>()); - classes.add(aClass); - } - - /** - * 通过分组key得到分组中的类类型 - * @param key 处理者key - * @return 类类型集合 - */ - public List> getGroupClasses(String key){ - List> classes = groupClasses.get(key); - List> result = new ArrayList<>(); - if(classes != null){ - result.addAll(classes); - } - return result; - } - - /** - * 得到插件bean注册者信息 - * @param key 扩展的key - * @param 处理者类型 - * @return 注册者信息 - */ - public T getProcessorInfo(String key){ - Object o = processorInfo.get(key); - if(o != null){ - return (T) o; - } - return null; - } - - /** - * 添加插件中的配置对象 - * @param singletonObject 单例对象 - */ - public void addConfigSingleton(Object singletonObject){ - configSingletonObjects.add(singletonObject); - } - - /** - * 添加插件中的配置对象 - * @return 配置的实现对象 - */ - public Set getConfigSingletons(){ - return Collections.unmodifiableSet(configSingletonObjects); - } - - /** - * 添加处理者信息 - * @param key key - * @param value value - */ - public void addProcessorInfo(String key, Object value){ - processorInfo.put(key, value); - } - - /** - * 添加扩展数据 - * @param key 扩展的key - * @param value 扩展值 - */ - public void addExtension(String key, Object value){ - if(extensionMap.containsKey(key)){ - throw new RuntimeException("The extension key ' " + key + " 'already exists"); - } - extensionMap.put(key, value); - } - - /** - * 得到主程序的ApplicationContext - * @return GenericApplicationContext - */ - public GenericApplicationContext getMainApplicationContext() { - return mainApplicationContext; - } - - /** - * 得到当前插件的ApplicationContext - * @return AnnotationConfigApplicationContext - */ - public GenericApplicationContext getPluginApplicationContext() { - return pluginApplicationContext; - } - - /** - * 得到当前插件的Binder - * @return Binder - */ - public Binder getPluginBinder() { - return pluginBinder; - } - - /** - * 得到当前插件Bean注册者 - * @return SpringBeanRegister - */ - public SpringBeanRegister getSpringBeanRegister() { - return springBeanRegister; - } - - /** - * 移除扩展数据 - * @param key 扩展的key - */ - public void removeExtension(String key){ - extensionMap.remove(key); - } - - /** - * 获取扩展值 - * @param key 扩展的key - * @param 返回值泛型 - * @return 扩展值 - */ - public T getExtension(String key){ - Object o = extensionMap.get(key); - if(o == null){ - return null; - } else { - return (T) o; - } - } - - public ClassLoader getPluginClassLoader(){ - return pluginWrapper.getPluginClassLoader(); - } - - public boolean isFollowingInitial() { - return followingInitial; - } - - public IntegrationConfiguration getConfiguration() { - return configuration; - } - - void destroy(){ - // 关闭ApplicationContext - try { - PluginInfoContainers.removePluginApplicationContext(getPluginWrapper().getPluginId()); - closePluginApplicationContext(); - } catch (Exception e){ - logger.error("Close plugin '{}'-ApplicationContext failure", getPluginWrapper().getPluginId(), e); - } - - // 清除数据集合 - try { - extensionMap.clear(); - classes.clear(); - groupClasses.clear(); - processorInfo.clear(); - pluginLoadResources.clear(); - configSingletonObjects.clear(); - } catch (Exception e){ - logger.error("Clear plugin '{}' failure", getPluginWrapper().getPluginId(), e); - } - } - - private void closePluginApplicationContext() { - try { - getSpringBeanRegister().destroySingletons(); - pluginApplicationContext.close(); - } catch (Exception e){ - logger.error("Close plugin '{}' ApplicationContext failure", getPluginWrapper().getPluginId(), e); - } - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/PropertyKey.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/PropertyKey.java deleted file mode 100644 index c96920872d342ed67c5b8e7602e28f3de37b3ca7..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/PropertyKey.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.gitee.starblues.factory; - -/** - * 配置文件key - * @author starBlues - * @version 2.4.3 - */ -public class PropertyKey { - - public final static String INSTALL_AUTO_CONFIG_CLASS = "plugin.auto-config-class"; - -} 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 deleted file mode 100644 index 2557254ec2090d501c4c03b64c5bc0820a695d8b..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/SpringBeanRegister.java +++ /dev/null @@ -1,224 +0,0 @@ -package com.gitee.starblues.factory; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition; -import org.springframework.beans.factory.config.BeanDefinitionHolder; -import org.springframework.beans.factory.support.BeanNameGenerator; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.context.annotation.AnnotationBeanNameGenerator; -import org.springframework.context.support.GenericApplicationContext; - -import java.text.MessageFormat; -import java.util.function.Consumer; - -/** - * Spring bean注册者, 向Spring注册Bean时, 必须使用该对象进行注册 - * - * @author starBlues - * @version 2.4.1 - */ -public class SpringBeanRegister { - - private static final Logger logger = LoggerFactory.getLogger(SpringBeanRegister.class); - - private final GenericApplicationContext applicationContext; - - public SpringBeanRegister(GenericApplicationContext applicationContext){ - this.applicationContext = applicationContext; - } - - - public boolean exist(String name){ - return applicationContext.containsBean(name); - } - - - /** - * 基于class注册一个bean - * - * @param aClass 类名 - * @return 注册的bean名称 - */ - public String register(Class aClass) { - return register(aClass, null); - } - - - /** - * 基于class注册一个bean - * - * @param pluginId 插件id - * @param aClass 类名 - * @return 注册的bean名称 - */ - @Deprecated - public String register(String pluginId, Class aClass) { - return register(pluginId, aClass, null); - } - - - /** - * 基于class注册一个bean, 可自定义 BeanDefinition - * - * @param aClass 注册的类 - * @param consumer 自定义处理AnnotatedGenericBeanDefinition - * @return 注册的bean名称 - */ - public String register(Class aClass, - Consumer consumer) { - AnnotatedGenericBeanDefinition beanDefinition = new AnnotatedGenericBeanDefinition(aClass); - beanDefinition.setBeanClass(aClass); - BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator(); - String beanName = beanNameGenerator.generateBeanName(beanDefinition, applicationContext); - - if(applicationContext.containsBean(beanName)){ - String error = MessageFormat.format("Bean name {0} already exist of {1}", - beanName, aClass.getName()); - logger.debug(error); - return beanName; - } - if(consumer != null){ - consumer.accept(beanDefinition); - } - applicationContext.registerBeanDefinition(beanName, beanDefinition); - return beanName; - } - - - /** - * 基于class注册一个bean, 可自定义 BeanDefinition - * - * @param pluginId 插件id - * @param aClass 注册的类 - * @param consumer 自定义处理AnnotatedGenericBeanDefinition - * @return 注册的bean名称 - */ - @Deprecated - public String register(String pluginId, Class aClass, - Consumer consumer) { - return register(aClass, consumer); - } - - /** - * 指定bean名称注册 - * @param beanName 指定的bean名称 - * @param aClass 注册的类 - */ - public void registerOfSpecifyName(String beanName, Class aClass){ - registerOfSpecifyName(beanName, aClass, null); - } - - /** - * 指定bean名称注册 - * @param pluginId 插件id - * @param beanName 指定的bean名称 - * @param aClass 注册的类 - */ - @Deprecated - public void registerOfSpecifyName(String pluginId, String beanName, Class aClass){ - registerOfSpecifyName(pluginId, beanName, aClass, null); - } - - - /** - * 指定bean名称注册, 可自定义 BeanDefinition - * @param beanName 指定的bean名称 - * @param aClass 注册的类 - * @param consumer 自定义处理AnnotatedGenericBeanDefinition - */ - public void registerOfSpecifyName(String beanName, - Class aClass, - Consumer consumer) { - AnnotatedGenericBeanDefinition beanDefinition = new - AnnotatedGenericBeanDefinition(aClass); - if(applicationContext.containsBean(beanName)){ - String error = MessageFormat.format("Bean name {0} already exist of {1}", - beanName, aClass.getName()); - throw new RuntimeException(error); - } - if(consumer != null){ - consumer.accept(beanDefinition); - } - applicationContext.registerBeanDefinition(beanName, beanDefinition); - } - - - /** - * 指定bean名称注册, 可自定义 BeanDefinition - * @param pluginId 插件id - * @param beanName 指定的bean名称 - * @param aClass 注册的类 - * @param consumer 自定义处理AnnotatedGenericBeanDefinition - */ - @Deprecated - public void registerOfSpecifyName(String pluginId, - String beanName, - Class aClass, - Consumer consumer) { - registerOfSpecifyName(beanName, aClass, consumer); - } - - /** - * 注册单例 - * @param name 单例名称 - * @param object 对象 - */ - public void registerSingleton(String name, Object object){ - DefaultListableBeanFactory listableBeanFactory = applicationContext.getDefaultListableBeanFactory(); - if(!listableBeanFactory.containsSingleton(name)){ - listableBeanFactory.registerSingleton(name, object); - } - } - - /** - * 注册BeanDefinitionHolder - * @param definitionHolder BeanDefinitionHolder - */ - public void registerBeanDefinition(BeanDefinitionHolder definitionHolder) { - - String beanName = definitionHolder.getBeanName(); - applicationContext.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); - - String[] aliases = definitionHolder.getAliases(); - if (aliases != null) { - for (String alias : aliases) { - applicationContext.registerAlias(beanName, alias); - } - } - } - - /** - * 销毁单例 - * @param name 单例名称 - */ - public void destroySingleton(String name){ - DefaultListableBeanFactory listableBeanFactory = applicationContext.getDefaultListableBeanFactory(); - if(listableBeanFactory.containsSingleton(name)){ - listableBeanFactory.destroySingleton(name); - } - } - - /** - * 销毁所有单例 - */ - public void destroySingletons(){ - DefaultListableBeanFactory listableBeanFactory = applicationContext.getDefaultListableBeanFactory(); - listableBeanFactory.destroySingletons(); - } - - /** - * 卸载bean - * @param pluginId 插件id - * @param beanName bean名称 - */ - public void unregister(String pluginId, String beanName){ - try { - applicationContext.removeBeanDefinition(beanName); - } catch (Exception e){ - logger.error("Remove plugin '{}' bean {} error. {}", pluginId, beanName, e.getMessage()); - } - } - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginConfigBeanPipeProcessor.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginConfigBeanPipeProcessor.java deleted file mode 100644 index 39a4646c3d1e5791d8700cb86a83942bdac41101..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginConfigBeanPipeProcessor.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.gitee.starblues.factory.process.pipe; - -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.realize.ConfigBean; -import com.gitee.starblues.utils.SpringBeanUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author starBlues - * @version 2.4.0 - */ -public class PluginConfigBeanPipeProcessor implements PluginPipeProcessor{ - - private final static String KEY = "ConfigBeans"; - - private final Logger logger = LoggerFactory.getLogger(this.getClass()); - - @Override - public void initialize() throws Exception { - - } - - @Override - public void registry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - List pluginBeans = SpringBeanUtils.getBeans(pluginRegistryInfo.getPluginApplicationContext(), - ConfigBean.class); - if(pluginBeans.isEmpty()){ - return; - } - List successConfigBeans = new ArrayList<>(pluginBeans.size()); - for (ConfigBean pluginBean : pluginBeans) { - try { - pluginBean.initialize(); - successConfigBeans.add(pluginBean); - } catch (Exception e){ - logger.error("Plugin '{}' configBean '{}' initialize exception.", - pluginRegistryInfo.getPluginWrapper().getPluginId(), - pluginBean.getClass().getName(), e); - } - } - pluginRegistryInfo.addExtension(KEY, successConfigBeans); - } - - @Override - public void unRegistry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - List pluginBeans = pluginRegistryInfo.getExtension(KEY); - if(pluginBeans == null || pluginBeans.isEmpty()){ - return; - } - for (ConfigBean pluginBean : pluginBeans) { - try { - pluginBean.destroy(); - } catch (Exception e){ - logger.error("Plugin '{}' configBean '{}' destroy exception.", - pluginRegistryInfo.getPluginWrapper().getPluginId(), - pluginBean.getClass().getName(), e); - } - } - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginInfoContainers.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginInfoContainers.java deleted file mode 100644 index 4c3d2b97d3619838a0860d09075062f47423a0d2..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginInfoContainers.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.gitee.starblues.factory.process.pipe; - -import org.springframework.context.support.GenericApplicationContext; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -/** - * 插件信息容器 - * @author starBlues - * @version 2.4.0 - */ -public class PluginInfoContainers { - - private final static Map PLUGIN_APPLICATION_CONTEXTS = - new ConcurrentHashMap<>(); - - public static void addPluginApplicationContext(String pluginId, GenericApplicationContext applicationContext){ - PLUGIN_APPLICATION_CONTEXTS.put(pluginId, applicationContext); - } - - public static void removePluginApplicationContext(String pluginId){ - PLUGIN_APPLICATION_CONTEXTS.remove(pluginId); - } - - static public GenericApplicationContext getPluginApplicationContext(String pluginId) { - GenericApplicationContext applicationContext = PLUGIN_APPLICATION_CONTEXTS.get(pluginId); - if(applicationContext == null){ - return null; - } - return applicationContext; - } - - static public List getPluginApplicationContexts() { - Collection values = PLUGIN_APPLICATION_CONTEXTS.values(); - if(values.isEmpty()){ - return new ArrayList<>(); - } - return new ArrayList<>(values); - } - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginOneselfStopEventProcessor.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginOneselfStopEventProcessor.java deleted file mode 100644 index e7e717d93da2535b32330f88432d167bdff9a9f3..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginOneselfStopEventProcessor.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.gitee.starblues.factory.process.pipe; - -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.post.bean.PluginOneselfStartEventProcessor; -import com.gitee.starblues.realize.BasePlugin; -import com.gitee.starblues.realize.OneselfListener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; - -/** - * 插件中 OneselfListener 监听者处理者。主要执行监听器的停止事件。 - * @author starBlues - * @version 1.0 - * @since 2021-05-30 - */ -public class PluginOneselfStopEventProcessor implements PluginPipeProcessor{ - - private final Logger log = LoggerFactory.getLogger(this.getClass()); - - @Override - public void initialize() throws Exception { - - } - - @Override - public void registry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - // 当前不执行启动事件 - // 由类 com.gitee.starblues.factory.process.post.bean.PluginOneselfStartEventProcessor 实现启动 - // 主要是资源全部加载注册完成后再触发启动事件 - } - - @Override - public void unRegistry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - List oneselfListeners = pluginRegistryInfo - .getExtension(PluginOneselfStartEventProcessor.KEY); - if(oneselfListeners == null || oneselfListeners.isEmpty()){ - return; - } - BasePlugin basePlugin = pluginRegistryInfo.getBasePlugin(); - for (OneselfListener oneselfListener : oneselfListeners) { - try { - oneselfListener.stopEvent(basePlugin); - } catch (Exception e){ - log.error("OneselfListener {} execute stopEvent exception. {}", - oneselfListener.getClass().getName(), e.getMessage(), e); - } - } - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginPipeApplicationContextProcessor.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginPipeApplicationContextProcessor.java deleted file mode 100644 index cfbca03b196888d3e46380d5f514e6a019cfb899..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginPipeApplicationContextProcessor.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.gitee.starblues.factory.process.pipe; - -import com.gitee.starblues.extension.ExtensionInitializer; -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.PropertyKey; -import com.gitee.starblues.factory.process.pipe.bean.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.autoconfigure.AutoConfigurationPackages; -import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor; -import org.springframework.boot.context.properties.bind.Bindable; -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.util.ObjectUtils; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * 插件的ApplicationContext 处理 - * 主要进行插件bean的扫描 - * @author starBlues - * @version 2.4.3 - */ -public class PluginPipeApplicationContextProcessor implements PluginPipeProcessor{ - - private final static Logger logger = LoggerFactory.getLogger(PluginPipeApplicationContextProcessor.class); - - private final List pluginBeanDefinitionRegistrars = new ArrayList<>(); - private final ApplicationContext mainApplicationContext; - - - public PluginPipeApplicationContextProcessor(ApplicationContext mainApplicationContext) { - this.mainApplicationContext = mainApplicationContext; - } - - @Override - public void initialize() throws Exception { - pluginBeanDefinitionRegistrars.add(new SpringBootConfigFileRegistrar(mainApplicationContext)); - pluginBeanDefinitionRegistrars.add(new PluginInsetBeanRegistrar()); - pluginBeanDefinitionRegistrars.add(new ConfigBeanRegistrar()); - pluginBeanDefinitionRegistrars.add(new ConfigFileBeanRegistrar(mainApplicationContext)); - pluginBeanDefinitionRegistrars.add(new BasicBeanRegistrar()); - pluginBeanDefinitionRegistrars.add(new InvokeBeanRegistrar()); - pluginBeanDefinitionRegistrars.addAll(ExtensionInitializer.getPluginBeanRegistrarExtends()); - } - - @Override - public void registry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - GenericApplicationContext pluginApplicationContext = pluginRegistryInfo.getPluginApplicationContext(); - // 进行bean注册 - for (PluginBeanRegistrar pluginBeanDefinitionRegistrar : pluginBeanDefinitionRegistrars) { - pluginBeanDefinitionRegistrar.registry(pluginRegistryInfo); - } - ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); - try { - registerMustDependencies(pluginApplicationContext, pluginRegistryInfo); - installPluginAutoConfiguration(pluginApplicationContext, pluginRegistryInfo); - pluginApplicationContext.refresh(); - } finally { - Thread.currentThread().setContextClassLoader(contextClassLoader); - } - - // 向插件静态容器中新增插件的ApplicationContext - String pluginId = pluginRegistryInfo.getPluginWrapper().getPluginId(); - PluginInfoContainers.addPluginApplicationContext(pluginId, pluginApplicationContext); - } - - /** - * 安装插件定义的自动装载配置类 - * @param pluginApplicationContext 插件ApplicationContext - * @param pluginRegistryInfo 插件注册信息 - */ - private void installPluginAutoConfiguration(GenericApplicationContext pluginApplicationContext, - PluginRegistryInfo pluginRegistryInfo) throws ClassNotFoundException { - Set installAutoConfigClassString = pluginRegistryInfo.getPluginBinder() - .bind(PropertyKey.INSTALL_AUTO_CONFIG_CLASS, Bindable.setOf(String.class)) - .orElseGet(()->null); - - if(ObjectUtils.isEmpty(installAutoConfigClassString)){ - return; - } - - // 注册AutoConfigurationPackages, 用于插件可自动配置 - AutoConfigurationPackages.register(pluginApplicationContext.getDefaultListableBeanFactory(), - pluginRegistryInfo.getBasePlugin().scanPackage()); - - Set> autoConfigurationClassSet = new HashSet<>(installAutoConfigClassString.size()); - ClassLoader pluginClassLoader = pluginRegistryInfo.getPluginClassLoader(); - for (String autoConfigClassPackage : installAutoConfigClassString) { - Class aClass = Class.forName(autoConfigClassPackage, false, pluginClassLoader); - autoConfigurationClassSet.add(aClass); - } - for (Class autoConfigurationClass : autoConfigurationClassSet) { - pluginApplicationContext.registerBean(autoConfigurationClass); - } - } - - /** - * 定义一些spring-boot中一些必要注册的依赖 - * @param pluginApplicationContext pluginApplicationContext - * @param pluginRegistryInfo pluginRegistryInfo - */ - private void registerMustDependencies(GenericApplicationContext pluginApplicationContext, - PluginRegistryInfo pluginRegistryInfo){ - // 注册AutoConfigurationPackages, 用于插件可自动配置 - AutoConfigurationPackages.register(pluginApplicationContext.getDefaultListableBeanFactory(), - pluginRegistryInfo.getBasePlugin().scanPackage()); - // 注册 ConfigurationPropertiesBindingPostProcessor - ConfigurationPropertiesBindingPostProcessor.register(pluginApplicationContext); - } - - - @Override - public void unRegistry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - for (PluginBeanRegistrar registrar : pluginBeanDefinitionRegistrars) { - try { - registrar.unRegistry(pluginRegistryInfo); - } catch (Exception e){ - logger.error("Plugin '{}'-'{}' unRegistry failure.", - pluginRegistryInfo.getPluginWrapper().getPluginId(), - registrar.getClass().getName()); - } - } - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginPipeProcessor.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginPipeProcessor.java deleted file mode 100644 index 7206178f10417d98a6d9e81f4d9324e06b72aa59..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginPipeProcessor.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.gitee.starblues.factory.process.pipe; - -import com.gitee.starblues.factory.PluginRegistryInfo; - -/** - * 插件管道处理者接口 - * - * @author starBlues - * @version 2.1.0 - */ -public interface PluginPipeProcessor { - - - /** - * 初始化 - * @throws Exception 初始化异常 - */ - void initialize() throws Exception; - - - /** - * 处理该插件的注册 - * @param pluginRegistryInfo 插件注册的信息 - * @throws Exception 处理异常 - */ - void registry(PluginRegistryInfo pluginRegistryInfo) throws Exception; - - - /** - * 处理该插件的卸载 - * @param pluginRegistryInfo 插件注册的信息 - * @throws Exception 处理异常 - */ - void unRegistry(PluginRegistryInfo pluginRegistryInfo) throws Exception; - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginPipeProcessorExtend.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginPipeProcessorExtend.java deleted file mode 100644 index cbb68c58b76716e496389720838cb7621c6299f1..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginPipeProcessorExtend.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.gitee.starblues.factory.process.pipe; - -import com.gitee.starblues.utils.OrderPriority; - -/** - * 单插件处理者扩展接口 - * - * @author starBlues - * @version 2.1.0 - */ -public interface PluginPipeProcessorExtend extends PluginPipeProcessor{ - - /** - * 扩展key - * @return String - */ - String key(); - - - /** - * 执行顺序 - * @return OrderPriority - */ - OrderPriority order(); - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginPipeProcessorFactory.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginPipeProcessorFactory.java deleted file mode 100644 index ec43b3234783a409d5685cb717e0a7063a9e5d1d..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginPipeProcessorFactory.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.gitee.starblues.factory.process.pipe; - -import com.gitee.starblues.extension.ExtensionInitializer; -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.pipe.classs.PluginClassProcess; -import com.gitee.starblues.factory.process.pipe.extract.PluginExtractPipeProcessor; -import com.gitee.starblues.factory.process.pipe.loader.PluginResourceLoadFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationContext; - -import java.util.ArrayList; -import java.util.List; - -/** - * 插件的pipe处理者工厂 - * - * @author starBlues - * @version 2.4.0 - */ -public class PluginPipeProcessorFactory implements PluginPipeProcessor { - - private final Logger logger = LoggerFactory.getLogger(this.getClass()); - - private final ApplicationContext mainApplicationContext; - private final List pluginPipeProcessors = new ArrayList<>(); - - public PluginPipeProcessorFactory(ApplicationContext mainApplicationContext){ - this.mainApplicationContext = mainApplicationContext; - } - - - - @Override - public void initialize() throws Exception{ - // 以下顺序不能更改 - // 停止事件放在第一位 - pluginPipeProcessors.add(new PluginOneselfStopEventProcessor()); - // 插件资源加载者, 必须放在第二位 - pluginPipeProcessors.add(new PluginResourceLoadFactory()); - // 插件类处理者 - pluginPipeProcessors.add(new PluginClassProcess()); - // 添加前置扩展 - pluginPipeProcessors.addAll(ExtensionInitializer.getPreProcessorExtends()); - // 插件的ApplicationContext处理者 - pluginPipeProcessors.add(new PluginPipeApplicationContextProcessor(mainApplicationContext)); - // 拦截器处理者 - pluginPipeProcessors.add(new PluginInterceptorsPipeProcessor(mainApplicationContext)); - // 插件ConfigBean处理者 - pluginPipeProcessors.add(new PluginConfigBeanPipeProcessor()); - // 插件扩展的流处理者 - pluginPipeProcessors.add(new PluginExtractPipeProcessor(mainApplicationContext)); - // 添加扩展 - pluginPipeProcessors.addAll(ExtensionInitializer.getPipeProcessorExtends()); - - // 进行初始化 - for (PluginPipeProcessor pluginPipeProcessor : pluginPipeProcessors) { - pluginPipeProcessor.initialize(); - } - } - - @Override - public void registry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - for (PluginPipeProcessor pluginPipeProcessor : pluginPipeProcessors) { - pluginPipeProcessor.registry(pluginRegistryInfo); - } - } - - @Override - public void unRegistry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - boolean findException = false; - for (PluginPipeProcessor pluginPipeProcessor : pluginPipeProcessors) { - try { - pluginPipeProcessor.unRegistry(pluginRegistryInfo); - } catch (Exception e){ - findException = true; - logger.error("unRegistry plugin '{}' failure by {}", pluginRegistryInfo.getPluginWrapper().getPluginId(), - pluginPipeProcessor.getClass().getName(), e); - } - } - if(findException){ - throw new Exception("UnRegistry plugin '" + pluginRegistryInfo.getPluginWrapper().getPluginId() + "'failure"); - } - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginPreProcessorExtend.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginPreProcessorExtend.java deleted file mode 100644 index 2c8030598b1be008b00050ab4c33b7654ef77643..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/PluginPreProcessorExtend.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.gitee.starblues.factory.process.pipe; - -import com.gitee.starblues.utils.OrderPriority; - -/** - * 单插件处理者扩展接口 - * - * @author starBlues - * @version 2.2.5 - */ -public interface PluginPreProcessorExtend extends PluginPipeProcessor { - - /** - * 扩展key - * @return String - */ - String key(); - - /** - * 执行顺序 - * @return OrderPriority - */ - OrderPriority order(); - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/BasicBeanRegistrar.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/BasicBeanRegistrar.java deleted file mode 100644 index 9291a78c99eecd4246355ba57b1ad8bcb9c6e74f..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/BasicBeanRegistrar.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.bean; - -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.SpringBeanRegister; -import com.gitee.starblues.factory.process.pipe.classs.group.ComponentGroup; -import com.gitee.starblues.factory.process.pipe.classs.group.OneselfListenerGroup; -import com.gitee.starblues.factory.process.pipe.classs.group.RepositoryGroup; - -import java.util.*; - -/** - * 基础bean注册 - * - * @author starBlues - * @version 2.4.0 - */ -public class BasicBeanRegistrar implements PluginBeanRegistrar { - - - public BasicBeanRegistrar(){ - } - - @Override - public void registry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - List> springComponents = pluginRegistryInfo - .getGroupClasses(ComponentGroup.GROUP_ID); - List> springRepository = pluginRegistryInfo - .getGroupClasses(RepositoryGroup.GROUP_ID); - List> oneselfListener = pluginRegistryInfo - .getGroupClasses(OneselfListenerGroup.GROUP_ID); - - register(pluginRegistryInfo, springComponents); - register(pluginRegistryInfo, springRepository); - register(pluginRegistryInfo, oneselfListener); - } - - /** - * 往Spring注册bean - * @param pluginRegistryInfo 插件注册的信息 - * @param classes 要注册的类集合 - */ - private void register(PluginRegistryInfo pluginRegistryInfo, - List> classes){ - if(classes == null || classes.isEmpty()){ - return; - } - String pluginId = pluginRegistryInfo.getPluginWrapper().getPluginId(); - SpringBeanRegister springBeanRegister = pluginRegistryInfo.getSpringBeanRegister(); - for (Class aClass : classes) { - if(aClass == null){ - continue; - } - springBeanRegister.register(aClass); - } - } - - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/ConfigBeanRegistrar.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/ConfigBeanRegistrar.java deleted file mode 100644 index b874aaea8a7a28ed463f1bc0cd291a7886762309..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/ConfigBeanRegistrar.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.bean; - -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.SpringBeanRegister; -import com.gitee.starblues.factory.process.pipe.classs.group.ConfigBeanGroup; -import com.gitee.starblues.realize.ConfigBean; - -import java.util.*; - -/** - * 插件中实现 ConfigBean 接口的的处理者 - * @see ConfigBean - * - * @author starBlues - * @version 2.4.0 - */ -public class ConfigBeanRegistrar implements PluginBeanRegistrar { - - public final static String KEY = "ConfigBeanNames"; - - public ConfigBeanRegistrar() { - } - - @Override - public void registry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - List> configBeans = - pluginRegistryInfo.getGroupClasses(ConfigBeanGroup.GROUP_ID); - if(configBeans == null || configBeans.isEmpty()){ - return; - } - SpringBeanRegister springBeanRegister = pluginRegistryInfo.getSpringBeanRegister(); - for (Class aClass : configBeans) { - if(aClass == null){ - continue; - } - springBeanRegister.register(aClass); - } - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/ConfigFileBeanRegistrar.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/ConfigFileBeanRegistrar.java deleted file mode 100644 index 912334d2dd00c417ce326a9e75b8cbb2241add28..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/ConfigFileBeanRegistrar.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.bean; - -import com.gitee.starblues.annotation.ConfigDefinition; -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.SpringBeanRegister; -import com.gitee.starblues.factory.process.pipe.bean.configuration.ConfigurationParser; -import com.gitee.starblues.factory.process.pipe.bean.configuration.PluginConfigDefinition; -import com.gitee.starblues.factory.process.pipe.bean.configuration.YamlConfigurationParser; -import com.gitee.starblues.factory.process.pipe.classs.group.ConfigDefinitionGroup; -import com.gitee.starblues.integration.IntegrationConfiguration; -import com.gitee.starblues.realize.ConfigDefinitionTip; -import com.gitee.starblues.utils.ClassUtils; -import com.gitee.starblues.utils.PluginConfigUtils; -import org.pf4j.util.StringUtils; -import org.springframework.context.ApplicationContext; -import org.springframework.util.ReflectionUtils; - -import java.lang.reflect.Field; -import java.util.List; - -/** - * 插件中配置文件 bean 的处理者。包括配置文件 - * @author starBlues - * @version 2.4.0 - */ -public class ConfigFileBeanRegistrar implements PluginBeanRegistrar { - - private final ConfigurationParser configurationParser; - private final IntegrationConfiguration integrationConfiguration; - - public ConfigFileBeanRegistrar(ApplicationContext mainApplicationContext) { - integrationConfiguration = - mainApplicationContext.getBean(IntegrationConfiguration.class); - this.configurationParser = new YamlConfigurationParser(integrationConfiguration); - } - - - @Override - public void registry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - List> configDefinitions = - pluginRegistryInfo.getGroupClasses(ConfigDefinitionGroup.GROUP_ID); - if(configDefinitions == null || configDefinitions.isEmpty()){ - return; - } - for (Class aClass : configDefinitions) { - registry(pluginRegistryInfo, aClass); - } - } - - /** - * 注册配置文件 - * @param pluginRegistryInfo 插件注册的信息 - * @param aClass 配置文件类 - * @throws Exception Exception - */ - private void registry(PluginRegistryInfo pluginRegistryInfo, Class aClass) throws Exception{ - ConfigDefinition configDefinition = aClass.getAnnotation(ConfigDefinition.class); - if(configDefinition == null){ - return; - } - PluginConfigUtils.FileNamePack fileNamePack = PluginConfigUtils.getConfigFileName( - configDefinition.fileName(), - configDefinition.prodSuffix(), - configDefinition.devSuffix(), - integrationConfiguration.environment()); - String fileName = PluginConfigUtils.joinConfigFileName(fileNamePack); - Object parseObject = null; - if(!StringUtils.isNullOrEmpty(fileName)){ - PluginConfigDefinition pluginConfigDefinition = - new PluginConfigDefinition(fileName, aClass); - parseObject = configurationParser.parse(pluginRegistryInfo, - pluginConfigDefinition); - } else { - parseObject = aClass.newInstance(); - } - - String name = aClass.getName(); - SpringBeanRegister springBeanRegister = pluginRegistryInfo.getSpringBeanRegister(); - setConfigDefinitionTip(pluginRegistryInfo, parseObject); - springBeanRegister.registerSingleton(name, parseObject); - pluginRegistryInfo.addConfigSingleton(parseObject); - } - - /** - * 设置小工具类 - * @param parseObject 当前的配置对象 - */ - private void setConfigDefinitionTip(PluginRegistryInfo pluginRegistryInfo, Object parseObject) { - Class aClass = parseObject.getClass(); - List fields = ClassUtils.getAllFields(aClass); - ConfigDefinitionTip configDefinitionTip = new ConfigDefinitionTip(pluginRegistryInfo); - for (Field field : fields) { - if(field.getType() == ConfigDefinitionTip.class){ - field.setAccessible(true); - ReflectionUtils.setField(field, parseObject, configDefinitionTip); - } - } - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/InvokeBeanRegistrar.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/InvokeBeanRegistrar.java deleted file mode 100644 index ceca253eb2c98c6695faed749b22fd700a4e8794..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/InvokeBeanRegistrar.java +++ /dev/null @@ -1,308 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.bean; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.gitee.starblues.annotation.Caller; -import com.gitee.starblues.annotation.Supplier; -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.SpringBeanRegister; -import com.gitee.starblues.factory.process.pipe.PluginInfoContainers; -import com.gitee.starblues.factory.process.pipe.classs.group.CallerGroup; -import com.gitee.starblues.factory.process.pipe.classs.group.SupplierGroup; -import com.gitee.starblues.factory.process.post.bean.PluginInvokePostProcessor; -import org.pf4j.util.StringUtils; -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.support.GenericBeanDefinition; -import org.springframework.util.ClassUtils; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.text.MessageFormat; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -/** - * 插件互相调用的bean注册者 - * @author starBlues - * @version 2.4.0 - */ -public class InvokeBeanRegistrar implements PluginBeanRegistrar{ - - private final static Map> PLUGIN_SUPPER_MAP = new ConcurrentHashMap<>(4); - - public static final String SUPPLIER_KEY = "Invoke_Supplier"; - - @Override - public void registry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - registrySupper(pluginRegistryInfo); - registryCall(pluginRegistryInfo); - } - - @Override - public void unRegistry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - PLUGIN_SUPPER_MAP.remove(pluginRegistryInfo.getPluginWrapper().getPluginId()); - } - - /** - * 处理被调用者 - * @param pluginRegistryInfo 插件注册的信息 - * @throws Exception 处理异常 - */ - private void registrySupper(PluginRegistryInfo pluginRegistryInfo) throws Exception { - List> supperClasses = pluginRegistryInfo.getGroupClasses(SupplierGroup.GROUP_ID); - if(supperClasses.isEmpty()){ - return; - } - SpringBeanRegister springBeanRegister = pluginRegistryInfo.getSpringBeanRegister(); - Set beanNames = new HashSet<>(supperClasses.size()); - for (Class supperClass : supperClasses) { - if(supperClass == null){ - continue; - } - Supplier supplier = supperClass.getAnnotation(Supplier.class); - if(supplier == null){ - continue; - } - String beanName = supplier.value(); - if(springBeanRegister.exist(beanName)){ - String error = MessageFormat.format( - "Plugin {0} : Bean @Supplier name {1} already exist of {2}", - pluginRegistryInfo.getPluginWrapper().getPluginId(), beanName, supperClass.getName()); - throw new Exception(error); - } - springBeanRegister.registerOfSpecifyName(beanName, supperClass); - beanNames.add(beanName); - } - pluginRegistryInfo.addExtension(SUPPLIER_KEY, beanNames); - } - - - private void registryCall(PluginRegistryInfo pluginRegistryInfo) { - List> callerClasses = pluginRegistryInfo.getGroupClasses(CallerGroup.GROUP_ID); - if(callerClasses == null || callerClasses.isEmpty()){ - return; - } - SpringBeanRegister springBeanRegister = pluginRegistryInfo.getSpringBeanRegister(); - for (Class callerClass : callerClasses) { - Caller caller = callerClass.getAnnotation(Caller.class); - if(caller == null){ - continue; - } - springBeanRegister.register(callerClass, (beanDefinition) ->{ - beanDefinition.getPropertyValues().add("callerInterface", callerClass); - beanDefinition.getPropertyValues().add("callerAnnotation", caller); - beanDefinition.setBeanClass(CallerInterfaceFactory.class); - beanDefinition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE); - }); - } - - } - - public static void addSupper(String pluginId, String name, Object o){ - Map superMap = PLUGIN_SUPPER_MAP.computeIfAbsent(pluginId, k -> new HashMap<>(4)); - superMap.put(name, o); - } - - public static Object getSupper(String name){ - for (Map superMap : PLUGIN_SUPPER_MAP.values()) { - Object o = superMap.get(name); - if(o != null){ - return o; - } - } - return null; - } - - public static Object getSupper(String pluginId, String name){ - Map superMap = PLUGIN_SUPPER_MAP.get(pluginId); - if(superMap == null || superMap.isEmpty()){ - return null; - } - return superMap.get(name); - } - - - /** - * 调用者的接口工厂 - * @param 接口泛型 - */ - private static class CallerInterfaceFactory implements FactoryBean { - - private Class callerInterface; - private Caller callerAnnotation; - - @Override - public T getObject() throws Exception { - ClassLoader classLoader = callerInterface.getClassLoader(); - Class[] interfaces = new Class[]{callerInterface}; - ProxyHandler proxy = new ProxyHandler(callerAnnotation); - return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy); - } - - @Override - public Class getObjectType() { - return callerInterface; - } - - @Override - public boolean isSingleton() { - return true; - } - - public Class getCallerInterface() { - return callerInterface; - } - - public void setCallerInterface(Class callerInterface) { - this.callerInterface = callerInterface; - } - - public Caller getCallerAnnotation() { - return callerAnnotation; - } - - public void setCallerAnnotation(Caller callerAnnotation) { - this.callerAnnotation = callerAnnotation; - } - } - - - - /** - * 代理类 - */ - private static class ProxyHandler implements InvocationHandler { - - private final Caller callerAnnotation; - - private final static ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - - private ProxyHandler(Caller callerAnnotation) { - this.callerAnnotation = callerAnnotation; - } - - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - Object supplierObject = null; - String pluginId = callerAnnotation.pluginId(); - if(StringUtils.isNullOrEmpty(pluginId)){ - supplierObject = getSupper(callerAnnotation.value()); - } else { - supplierObject = getSupper(pluginId, callerAnnotation.value()); - } - if(supplierObject == null){ - if(StringUtils.isNullOrEmpty(pluginId)){ - throw new Exception("Not found '" + callerAnnotation.value() + "' supplier object"); - } else { - throw new Exception("Not found '" + callerAnnotation.value() + "' supplier object in plugin '" + - pluginId + "'"); - } - } - - Caller.Method callerMethod = method.getAnnotation(Caller.Method.class); - if(args == null){ - args = new Object[]{}; - } - if(callerMethod == null){ - return notAnnotationInvoke(method, supplierObject, args); - } else { - return annotationInvoke(method, callerMethod, supplierObject, args); - } - } - - /** - * 有注解的调用 - * @param method 调用接口的方法 - * @param callerMethod 调用者方法注解 - * @param supplierObject 调用者对象 - * @param args 传入参数 - * @return 返回值 - * @throws Throwable 异常 - */ - private Object annotationInvoke(Method method, Caller.Method callerMethod, - Object supplierObject, Object[] args) throws Throwable{ - - String callerMethodName = callerMethod.value(); - Class supplierClass = supplierObject.getClass(); - Method[] methods = supplierClass.getMethods(); - Method supplierMethod = null; - for (Method m : methods) { - Supplier.Method supplierMethodAnnotation = m.getAnnotation(Supplier.Method.class); - if(supplierMethodAnnotation == null){ - continue; - } - if(Objects.equals(supplierMethodAnnotation.value(), callerMethodName)){ - supplierMethod = m; - break; - } - } - if(supplierMethod == null){ - // 如果为空, 说明没有找到被调用者的注解, 则走没有注解的代理调用。 - return notAnnotationInvoke(method, supplierObject, args); - } - Class[] parameterTypes = supplierMethod.getParameterTypes(); - if(parameterTypes.length != args.length){ - // 参数不匹配 - return notAnnotationInvoke(method, supplierObject, args); - } - Object[] supplierArgs = new Object[args.length]; - for (int i = 0; i < parameterTypes.length; i++) { - Class parameterType = parameterTypes[i]; - Object arg = args[i]; - if(parameterType == arg.getClass()){ - supplierArgs[i] = arg; - } else { - // 类型不匹配, 尝试使用json序列化 - String json = OBJECT_MAPPER.writeValueAsString(arg); - Object serializeObject = OBJECT_MAPPER.readValue(json, parameterType); - supplierArgs[i] = serializeObject; - } - } - Object invokeReturn = supplierMethod.invoke(supplierObject, supplierArgs); - return getReturnObject(invokeReturn, method); - } - - /** - * 没有注解调用 - * @param method 调用接口的方法 - * @param supplierObject 提供者对象 - * @param args 传入参数 - * @return 返回值 - * @throws Throwable 异常 - */ - private Object notAnnotationInvoke(Method method, Object supplierObject, Object[] args) throws Throwable{ - String name = method.getName(); - Class[] argClasses = new Class[args.length]; - for (int i = 0; i < args.length; i++) { - argClasses[i] = args[i].getClass(); - } - Class supplierClass = supplierObject.getClass(); - Method supplierMethod = supplierClass.getMethod(name, argClasses); - Object invokeReturn = supplierMethod.invoke(supplierObject, args); - return getReturnObject(invokeReturn, method); - } - - - /** - * 得到返回值对象 - * @param invokeReturn 反射调用后返回的对象 - * @param method 调用接口的方法 - * @return 返回值对象 - * @throws Throwable Throwable - */ - private Object getReturnObject(Object invokeReturn, Method method) throws Throwable{ - if(invokeReturn == null){ - return null; - } - Class returnType = method.getReturnType(); - if(ClassUtils.isAssignable(invokeReturn.getClass(),returnType)){ - return invokeReturn; - } else { - String json = OBJECT_MAPPER.writeValueAsString(invokeReturn); - return OBJECT_MAPPER.readValue(json, OBJECT_MAPPER.getTypeFactory().constructType(method.getGenericReturnType()) ); - } - } - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/PluginBeanRegistrar.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/PluginBeanRegistrar.java deleted file mode 100644 index 97a8071944b95743947e4651d6e770e84812cd45..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/PluginBeanRegistrar.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.bean; - -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.pipe.PluginInfoContainers; - -/** - * @author starBlues - * @version 1.0 - */ -public interface PluginBeanRegistrar { - - - /** - * 处理该插件的注册 - * @param pluginRegistryInfo 插件注册的信息 - * @throws Exception 处理异常 - */ - void registry(PluginRegistryInfo pluginRegistryInfo) throws Exception; - - /** - * 处理该插件的卸载 - * @param pluginRegistryInfo 插件注册的信息 - * @throws Exception 处理异常 - */ - default void unRegistry(PluginRegistryInfo pluginRegistryInfo) throws Exception{} - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/PluginBeanRegistrarExtend.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/PluginBeanRegistrarExtend.java deleted file mode 100644 index d9446b1627ead0bd618ee62cd204dfab1dcb5ff4..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/PluginBeanRegistrarExtend.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.bean; - -/** - * @author starBlues - * @version 1.0 - */ -public interface PluginBeanRegistrarExtend extends PluginBeanRegistrar { - - /** - * 扩展key - * @return String - */ - String key(); - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/PluginInsetBeanRegistrar.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/PluginInsetBeanRegistrar.java deleted file mode 100644 index 8b5e07b31d593337c67135c76f91356077f4a1c5..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/PluginInsetBeanRegistrar.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.bean; - -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.SpringBeanRegister; -import com.gitee.starblues.factory.process.pipe.bean.inset.ExtractFactoryInset; -import com.gitee.starblues.factory.process.pipe.bean.inset.PluginInsetBean; -import com.gitee.starblues.factory.process.pipe.bean.inset.PluginUtilsInset; -import org.pf4j.util.StringUtils; - -import java.util.ArrayList; -import java.util.List; - -/** - * 系统内嵌的Bean注册者 - * @author starBlues - * @version 1.0 - */ -public class PluginInsetBeanRegistrar implements PluginBeanRegistrar{ - - private final List pluginInsetBeans = new ArrayList<>(2); - - public PluginInsetBeanRegistrar() { - this.pluginInsetBeans.add(new PluginUtilsInset()); - this.pluginInsetBeans.add(new ExtractFactoryInset()); - } - - - @Override - public void registry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - SpringBeanRegister springBeanRegister = pluginRegistryInfo.getSpringBeanRegister(); - for (PluginInsetBean pluginInsetBean : this.pluginInsetBeans) { - String beanName = pluginInsetBean.getBeanName(); - Object bean = pluginInsetBean.getBean(pluginRegistryInfo); - if(bean == null){ - continue; - } - if(StringUtils.isNullOrEmpty(beanName)){ - beanName = bean.getClass().getName(); - } - springBeanRegister.registerSingleton(beanName, bean); - } - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/SpringBootConfigFileRegistrar.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/SpringBootConfigFileRegistrar.java deleted file mode 100644 index cb8d65a671d5ae3ae4d0cd9c69af054991ac267a..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/SpringBootConfigFileRegistrar.java +++ /dev/null @@ -1,310 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.bean; - -import com.gitee.starblues.annotation.ConfigDefinition; -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.pipe.loader.ResourceWrapper; -import com.gitee.starblues.factory.process.pipe.loader.load.PluginConfigFileLoader; -import com.gitee.starblues.integration.IntegrationConfiguration; -import com.gitee.starblues.integration.pf4j.descriptor.DefaultPluginDescriptorExtend; -import com.gitee.starblues.realize.BasePlugin; -import com.gitee.starblues.utils.PluginConfigUtils; -import org.pf4j.PluginDescriptor; -import org.pf4j.RuntimeMode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.context.properties.bind.Binder; -import org.springframework.boot.env.PropertiesPropertySourceLoader; -import org.springframework.boot.env.PropertySourceLoader; -import org.springframework.boot.env.YamlPropertySourceLoader; -import org.springframework.context.ApplicationContext; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.Environment; -import org.springframework.core.env.PropertySource; -import org.springframework.core.io.Resource; -import org.springframework.util.Assert; -import org.springframework.util.ObjectUtils; -import org.springframework.util.StringUtils; - -import java.io.IOException; -import java.util.*; -import java.util.stream.Collectors; - -/** - * 解析springboot中的插件中的配置文件。目前支持: prop、yaml - * @author starBlues - * @version 2.4.5 - */ -public class SpringBootConfigFileRegistrar implements PluginBeanRegistrar{ - - /** - * The "active profiles" property name. - */ - private static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active"; - - /** - * The "includes profiles" property name. - */ - private static final String INCLUDE_PROFILES_PROPERTY = "spring.profiles.include"; - - private final Logger logger = LoggerFactory.getLogger(SpringBootConfigFileRegistrar.class); - - private final IntegrationConfiguration integrationConfiguration; - private final List propertySourceLoaders; - - public SpringBootConfigFileRegistrar(ApplicationContext mainApplicationContext){ - integrationConfiguration = - mainApplicationContext.getBean(IntegrationConfiguration.class); - this.propertySourceLoaders = new ArrayList<>(); - addPropertySourceLoader(); - } - - protected void addPropertySourceLoader(){ - this.propertySourceLoaders.add(new YamlPropertySourceLoader()); - this.propertySourceLoaders.add(new PropertiesPropertySourceLoader()); - } - - @Override - public void registry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - ConfigurableEnvironment environment = pluginRegistryInfo.getPluginApplicationContext().getEnvironment(); - - PluginConfigUtils.FileNamePack fileNamePack = getConfigFileName(pluginRegistryInfo); - if(fileNamePack == null){ - return; - } - String configFileName = PluginConfigUtils.joinConfigFileName(fileNamePack); - - PluginConfigFileLoader pluginConfigFileLoader = new PluginConfigFileLoader( - integrationConfiguration.pluginConfigFilePath(), configFileName - ); - - ResourceWrapper resourceWrapper = pluginConfigFileLoader.load(pluginRegistryInfo); - List resources = resourceWrapper.getResources(); - if(ObjectUtils.isEmpty(resources)){ - return; - } - // 获取注解中原始文件配置 - List> propProfiles = getPropProfiles(resources); - if(ObjectUtils.isEmpty(propProfiles)){ - return; - } - for (PropertySource propertySource : propProfiles) { - environment.getPropertySources().addLast(propertySource); - } - // 发现原始文件中配置的 profiles - List profiles = getProfiles(environment); - if(!ObjectUtils.isEmpty(profiles)){ - loadProfilesConfig(pluginRegistryInfo, fileNamePack, environment, profiles); - } - } - - /** - * 获取插件中定义的配置文件名称信息 - * @param pluginRegistryInfo PluginRegistryInfo - * @return 配置文件信息 - */ - private PluginConfigUtils.FileNamePack getConfigFileName(PluginRegistryInfo pluginRegistryInfo){ - // 先从插件引导配置获取配置文件 - PluginDescriptor descriptor = pluginRegistryInfo.getPluginWrapper().getDescriptor(); - if(descriptor instanceof DefaultPluginDescriptorExtend){ - DefaultPluginDescriptorExtend descriptorExtend = (DefaultPluginDescriptorExtend) descriptor; - String configFileName = descriptorExtend.getConfigFileName(); - if(!ObjectUtils.isEmpty(configFileName)){ - return new PluginConfigUtils.FileNamePack(configFileName, descriptorExtend.getConfigFileProfile()); - } - } - - // 如果插件引导文件未设置, 则从引导类的注解添加 ConfigDefinition - //加载成PropertySource对象,并添加到Environment环境中 - BasePlugin basePlugin = pluginRegistryInfo.getBasePlugin(); - ConfigDefinition configDefinition = basePlugin.getClass().getAnnotation(ConfigDefinition.class); - if(configDefinition == null){ - return null; - } - RuntimeMode runtimeMode = pluginRegistryInfo.getPluginWrapper().getRuntimeMode(); - return PluginConfigUtils.getConfigFileName( - configDefinition.fileName(), - configDefinition.prodSuffix(), - configDefinition.devSuffix(), - runtimeMode); - } - - /** - * 从 Resource 中解析出 PropertySource - * @param resources resources - * @return List - * @throws IOException 加载文件 IOException 异常 - */ - private List> getPropProfiles(List resources) throws IOException { - List> propProfiles = new ArrayList<>(); - if(resources == null || resources.isEmpty()){ - return propProfiles; - } - for (Resource resource : resources) { - if(resource == null || !resource.exists()){ - continue; - } - String filename = resource.getFilename(); - if(ObjectUtils.isEmpty(filename)){ - logger.error("File name is empty!"); - return null; - } - for (PropertySourceLoader propertySourceLoader : propertySourceLoaders) { - if(!canLoadFileExtension(propertySourceLoader, filename)){ - continue; - } - List> propertySources = propertySourceLoader.load(filename, resource); - if(ObjectUtils.isEmpty(propertySources)){ - continue; - } - propProfiles.addAll(propertySources); - } - } - return propProfiles; - } - - /** - * 加载 spring.profiles.active/spring.profiles.include 定义的配置 - * @param pluginRegistryInfo 插件注册信息 - * @param fileNamePack 配置文件包装 - * @param environment ConfigurableEnvironment - * @param profiles 主配置文件中定义的值 - * @throws Exception Exception - */ - private void loadProfilesConfig(PluginRegistryInfo pluginRegistryInfo, - PluginConfigUtils.FileNamePack fileNamePack, - ConfigurableEnvironment environment, List profiles) throws Exception { - // 解析当前文件名称 - for (Profile profile : profiles) { - String name = profile.getName(); - String fileName = PluginConfigUtils.joinConfigFileName(fileNamePack.getSourceFileName(), name); - PluginConfigFileLoader pluginConfigFileLoader = new PluginConfigFileLoader( - integrationConfiguration.pluginConfigFilePath(), fileName - ); - - ResourceWrapper resourceWrapper = pluginConfigFileLoader.load(pluginRegistryInfo); - List resources = resourceWrapper.getResources(); - if(ObjectUtils.isEmpty(resources)){ - continue; - } - List> propProfiles = getPropProfiles(resources); - if(ObjectUtils.isEmpty(propProfiles)){ - return; - } - for (PropertySource propertySource : propProfiles) { - environment.getPropertySources().addLast(propertySource); - } - } - // 重新设置 ActiveProfiles - String[] names = profiles.stream() - .filter((profile) -> profile != null && !profile.isDefaultProfile()) - .map(Profile::getName).toArray(String[]::new); - environment.setActiveProfiles(names); - } - - /** - * 根据文件后缀判断是否可解析 - * @param loader PropertySourceLoader - * @param name 文件名称 - * @return boolean - */ - private boolean canLoadFileExtension(PropertySourceLoader loader, String name) { - return Arrays.stream(loader.getFileExtensions()) - .anyMatch((fileExtension) -> StringUtils.endsWithIgnoreCase(name, - fileExtension)); - } - - - /** - * 得到主配置文件中的 Profile - * @param environment Environment - * @return List - */ - private List getProfiles(Environment environment) { - List profiles = new ArrayList<>(); - Set activatedViaProperty = getProfilesActivatedViaProperty(environment); - profiles.addAll(getOtherActiveProfiles(environment, activatedViaProperty)); - profiles.addAll(activatedViaProperty); - profiles.removeIf( - (profile) -> (profile != null && profile.isDefaultProfile())); - return profiles; - } - - private Set getProfilesActivatedViaProperty(Environment environment) { - if (!environment.containsProperty(ACTIVE_PROFILES_PROPERTY) - && !environment.containsProperty(INCLUDE_PROFILES_PROPERTY)) { - return Collections.emptySet(); - } - Binder binder = Binder.get(environment); - Set activeProfiles = new LinkedHashSet<>(); - activeProfiles.addAll(getProfiles(binder, INCLUDE_PROFILES_PROPERTY)); - activeProfiles.addAll(getProfiles(binder, ACTIVE_PROFILES_PROPERTY)); - return activeProfiles; - } - - private List getOtherActiveProfiles(Environment environment, Set activatedViaProperty) { - return Arrays.stream(environment.getActiveProfiles()).map(Profile::new) - .filter((profile) -> !activatedViaProperty.contains(profile)) - .collect(Collectors.toList()); - } - - private Set getProfiles(Binder binder, String name) { - return binder.bind(name, String[].class).map(this::asProfileSet) - .orElse(Collections.emptySet()); - } - - private Set asProfileSet(String[] profileNames) { - List profiles = new ArrayList<>(); - for (String profileName : profileNames) { - profiles.add(new Profile(profileName)); - } - return new LinkedHashSet<>(profiles); - } - - private static class Profile { - - private final String name; - - private final boolean defaultProfile; - - Profile(String name) { - this(name, false); - } - - Profile(String name, boolean defaultProfile) { - Assert.notNull(name, "Name must not be null"); - this.name = name; - this.defaultProfile = defaultProfile; - } - - public String getName() { - return this.name; - } - - public boolean isDefaultProfile() { - return this.defaultProfile; - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (obj == null || obj.getClass() != getClass()) { - return false; - } - return ((Profile) obj).name.equals(this.name); - } - - @Override - public int hashCode() { - return this.name.hashCode(); - } - - @Override - public String toString() { - return this.name; - } - - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/configuration/AbstractConfigurationParser.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/configuration/AbstractConfigurationParser.java deleted file mode 100644 index 89065fa20348f118dd6f7fd96ef05ea207add70b..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/configuration/AbstractConfigurationParser.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.bean.configuration; - -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.integration.IntegrationConfiguration; -import com.gitee.starblues.factory.process.pipe.loader.PluginResourceLoader; -import com.gitee.starblues.factory.process.pipe.loader.ResourceWrapper; -import com.gitee.starblues.factory.process.pipe.loader.load.PluginConfigFileLoader; -import org.springframework.core.io.Resource; - -import java.util.List; -import java.util.Objects; - -/** - * 抽象的插件配置文件解析者 - * @author starBlues - * @version 1.0 - */ -public abstract class AbstractConfigurationParser implements ConfigurationParser { - - private final IntegrationConfiguration configuration; - - public AbstractConfigurationParser(IntegrationConfiguration configuration) { - Objects.requireNonNull(configuration); - this.configuration = configuration; - } - - @Override - public Object parse(PluginRegistryInfo pluginRegistryInfo, PluginConfigDefinition pluginConfigDefinition) throws Exception { - Class configClass = pluginConfigDefinition.getConfigClass(); - if(pluginConfigDefinition.getConfigClass() == null){ - throw new IllegalArgumentException("pluginConfigDefinition : " + pluginConfigDefinition + " " + - "configClass can not be null"); - } - String fileName = pluginConfigDefinition.getFileName(); - if(pluginConfigDefinition.getFileName() == null || "".equals(pluginConfigDefinition.getFileName())){ - throw new IllegalArgumentException("pluginConfigDefinition : " + pluginConfigDefinition + " " + - "fileName can not be empty"); - } - - PluginResourceLoader resourceLoader = new PluginConfigFileLoader( - configuration.pluginConfigFilePath(), - fileName - ); - ResourceWrapper resourceWrapper = resourceLoader.load(pluginRegistryInfo); - if(resourceWrapper == null){ - return null; - } - List resources = resourceWrapper.getResources(); - if(resources.size() != 1){ - return null; - } - Object o = parse(resources.get(0), configClass); - if(o == null){ - return configClass.newInstance(); - } - return o; - } - - - /** - * 子类实现解析 - * @param resource 配置文件的资源信息 - * @param pluginConfigClass 配置文件class - * @return 返回映射后的存在值得对象 - * @throws Exception 配置文件解析异常 - */ - protected abstract Object parse(Resource resource, - Class pluginConfigClass) throws Exception; - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/configuration/ConfigurationParser.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/configuration/ConfigurationParser.java deleted file mode 100644 index cf53ae3a377ea5f67de58268bcf04e49356319e4..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/configuration/ConfigurationParser.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.bean.configuration; - -import com.gitee.starblues.factory.PluginRegistryInfo; - -/** - * 配置解析者 - * @author starBlues - * @version 1.0 - */ -public interface ConfigurationParser { - - /** - * 配置解析 - * @param pluginRegistryInfo 插件信息 - * @param pluginConfigDefinition 插件配置定义 - * @return 解析后映射值的对象 - * @throws Exception 抛出配置解析异常 - */ - Object parse(PluginRegistryInfo pluginRegistryInfo, PluginConfigDefinition pluginConfigDefinition) throws Exception; - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/configuration/PluginConfigDefinition.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/configuration/PluginConfigDefinition.java deleted file mode 100644 index c15045377c66fb623dbe05302fca68eaed1b9b15..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/configuration/PluginConfigDefinition.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.bean.configuration; - -import java.util.Objects; - -/** - * 插件配置的参数定义 - * @author starBlues - * @version 1.0 - */ -public class PluginConfigDefinition { - - /** - * 插件中的配置文件名称 - */ - private final String fileName; - - /** - * 配置文件实现类的Class定义 - */ - private final Class configClass; - - - - public PluginConfigDefinition(String fileName, Class configClass) { - this.fileName = fileName; - this.configClass = configClass; - } - - public String getFileName() { - return fileName; - } - - public Class getConfigClass() { - return configClass; - } - - - @Override - public boolean equals(Object o) { - if (this == o){ - return true; - } - if (!(o instanceof PluginConfigDefinition)){ - return false; - } - PluginConfigDefinition that = (PluginConfigDefinition) o; - return getFileName().equals(that.getFileName()) && - getConfigClass().equals(that.getConfigClass()); - } - - @Override - public int hashCode() { - return Objects.hash(getFileName(), getConfigClass()); - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/configuration/YamlConfigurationParser.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/configuration/YamlConfigurationParser.java deleted file mode 100644 index b2e00cdefae7c64b4f926c7288717d125d5360ab..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/configuration/YamlConfigurationParser.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.bean.configuration; - -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.TreeTraversingParser; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import com.fasterxml.jackson.dataformat.yaml.YAMLParser; -import com.gitee.starblues.integration.IntegrationConfiguration; -import org.springframework.core.io.Resource; - -import java.io.InputStream; - -/** - * yaml 配置解析者 - * @author starBlues - * @version 1.0 - */ -public class YamlConfigurationParser extends AbstractConfigurationParser { - - private final YAMLFactory yamlFactory; - private final ObjectMapper objectMapper; - - public YamlConfigurationParser(IntegrationConfiguration configuration) { - super(configuration); - this.yamlFactory = new YAMLFactory(); - this.objectMapper = new ObjectMapper(); - objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - } - - - @Override - protected Object parse(Resource resource, Class pluginConfigClass) - throws Exception{ - InputStream inputStream = null; - YAMLParser yamlParser = null; - TreeTraversingParser treeTraversingParser = null; - try { - inputStream = resource.getInputStream(); - yamlParser = yamlFactory.createParser(inputStream); - final JsonNode node = objectMapper.readTree(yamlParser); - if(node == null){ - return pluginConfigClass.newInstance(); - } - treeTraversingParser = new TreeTraversingParser(node); - return objectMapper.readValue(treeTraversingParser, pluginConfigClass); - } finally { - if(treeTraversingParser != null){ - treeTraversingParser.close(); - } - if(yamlParser != null){ - yamlParser.close(); - } - if(inputStream != null){ - inputStream.close(); - } - } - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/inset/ExtractFactoryInset.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/inset/ExtractFactoryInset.java deleted file mode 100644 index 26db0afce34003ff921f9473d1b775754f7f9436..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/inset/ExtractFactoryInset.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.bean.inset; - -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.pipe.extract.ExtractFactory; - -/** - * ExtractFactory 扩展工厂注册者 - * @author starBlues - * @version 2.4.1 - */ -public class ExtractFactoryInset implements PluginInsetBean{ - - @Override - public String getBeanName() { - return ExtractFactoryInset.class.getName(); - } - - @Override - public Object getBean(PluginRegistryInfo pluginRegistryInfo) { - return ExtractFactory.getInstant(); - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/inset/PluginInsetBean.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/inset/PluginInsetBean.java deleted file mode 100644 index a745d3f4225dc3b5f186aa782a189003a60c696a..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/inset/PluginInsetBean.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.bean.inset; - -import com.gitee.starblues.factory.PluginRegistryInfo; - -/** - * @author starBlues - * @version 2.4.1 - */ -public interface PluginInsetBean { - - /** - * 得到bean名称 - * @return bean 名称 - */ - String getBeanName(); - - /** - * 得到bean对象 - * @param pluginRegistryInfo pluginRegistryInfo - * @return 对象 - */ - Object getBean(PluginRegistryInfo pluginRegistryInfo); - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/inset/PluginUtilsInset.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/inset/PluginUtilsInset.java deleted file mode 100644 index 5d720d9fd446c8131a25adc58d343b58d7d0bfe2..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/inset/PluginUtilsInset.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.bean.inset; - -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.realize.PluginUtils; -import org.springframework.context.support.GenericApplicationContext; - -/** - * PluginUtils 对象注册者 - * @author starBlues - * @version 2.4.1 - */ -public class PluginUtilsInset implements PluginInsetBean{ - @Override - public String getBeanName() { - return "PluginUtilsName"; - } - - @Override - public Object getBean(PluginRegistryInfo pluginRegistryInfo) { - GenericApplicationContext parentApplicationContext = pluginRegistryInfo.getMainApplicationContext(); - GenericApplicationContext pluginApplicationContext = pluginRegistryInfo.getPluginApplicationContext(); - return new PluginUtils(parentApplicationContext, - pluginApplicationContext, - pluginRegistryInfo.getPluginWrapper().getDescriptor()); - } -} 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 deleted file mode 100644 index 70a13bc4bdb9c8a5db1961b55d8d00f439ea7fe6..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/bean/name/PluginAnnotationBeanNameGenerator.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.bean.name; - -import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.context.annotation.AnnotationBeanNameGenerator; -import org.springframework.util.StringUtils; - -/** - * 插件注解名称生成者 - * - * @author starBlues - * @version 1.0 - */ -@Deprecated -public class PluginAnnotationBeanNameGenerator extends AnnotationBeanNameGenerator { - - /** - * 插件id - */ - private final String pluginId; - - public PluginAnnotationBeanNameGenerator(String pluginId) { - this.pluginId = pluginId; - } - - @Override - public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { - if (definition instanceof AnnotatedBeanDefinition) { - String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition); - if (StringUtils.hasText(beanName)) { - return beanName.concat("@").concat(pluginId); - } - } - return buildDefaultBeanName(definition, registry).concat("@").concat(pluginId); - } - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/PluginClassGroup.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/PluginClassGroup.java deleted file mode 100644 index 99dbf3cfb371411735fd93d88f93ecdf40a20078..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/PluginClassGroup.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.classs; - -import com.gitee.starblues.realize.BasePlugin; - -/** - * 插件类分组器 - * - * @author starBlues - * @version 2.1.0 - */ -public interface PluginClassGroup { - - /** - * 组id - * @return 组id - */ - String groupId(); - - /** - * 初始化。每处理一个插件, 该方法调用一次。 - * @param basePlugin 当前插件信息 - */ - void initialize(BasePlugin basePlugin); - - - /** - * 过滤类。 - * @param aClass 类 - * @return 返回true.说明符合该分组器。false不符合该分组器 - */ - boolean filter(Class aClass); - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/PluginClassGroupExtend.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/PluginClassGroupExtend.java deleted file mode 100644 index c9326d7c437fd41516e55ee0c4f96283447ddd6b..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/PluginClassGroupExtend.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.classs; - -/** - * 插件类分组器扩展 - * - * @author starBlues - * @version 2.1.0 - */ -public interface PluginClassGroupExtend extends PluginClassGroup{ - - /** - * 扩展key - * @return String - */ - String key(); - - -} 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 deleted file mode 100644 index 6e6c03b466b19fd19a7b2591e0b1f56cc24ce288..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/PluginClassProcess.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.classs; - -import com.gitee.starblues.extension.ExtensionInitializer; -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.pipe.PluginPipeProcessor; -import com.gitee.starblues.factory.process.pipe.classs.group.*; -import com.gitee.starblues.factory.process.pipe.loader.ResourceWrapper; -import com.gitee.starblues.factory.process.pipe.loader.load.PluginClassLoader; -import com.gitee.starblues.realize.BasePlugin; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.io.Resource; -import org.springframework.util.StringUtils; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -/** - * 插件类加载处理者 - * - * @author starBlues - * @version 2.4.0 - */ -public class PluginClassProcess implements PluginPipeProcessor { - - - private final Logger log = LoggerFactory.getLogger(this.getClass()); - - /** - * 其他类 - */ - public static final String OTHER = "other"; - - - private final List pluginClassGroups = new ArrayList<>(); - - - public PluginClassProcess(){} - - - @Override - public void initialize() { - pluginClassGroups.add(new ComponentGroup()); - pluginClassGroups.add(new ControllerGroup()); - pluginClassGroups.add(new RepositoryGroup()); - pluginClassGroups.add(new ConfigDefinitionGroup()); - pluginClassGroups.add(new ConfigBeanGroup()); - pluginClassGroups.add(new SupplierGroup()); - pluginClassGroups.add(new CallerGroup()); - pluginClassGroups.add(new OneselfListenerGroup()); - pluginClassGroups.add(new WebSocketGroup()); - // 添加扩展 - pluginClassGroups.addAll(ExtensionInitializer.getClassGroupExtends()); - } - - @Override - public void registry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - BasePlugin basePlugin = pluginRegistryInfo.getBasePlugin(); - ResourceWrapper resourceWrapper = pluginRegistryInfo.getPluginLoadResource(PluginClassLoader.KEY); - if(resourceWrapper == null){ - return; - } - List pluginResources = resourceWrapper.getResources(); - if(pluginResources == null){ - return; - } - for (PluginClassGroup pluginClassGroup : pluginClassGroups) { - try { - pluginClassGroup.initialize(basePlugin); - } catch (Exception e){ - log.error("PluginClassGroup {} initialize exception. {}", pluginClassGroup.getClass(), - e.getMessage(), e); - } - } - Set classPackageNames = resourceWrapper.getClassPackageNames(); - ClassLoader classLoader = basePlugin.getWrapper().getPluginClassLoader(); - for (String classPackageName : classPackageNames) { - Class aClass = Class.forName(classPackageName, false, classLoader); - if(aClass == null){ - continue; - } - boolean findGroup = false; - for (PluginClassGroup pluginClassGroup : pluginClassGroups) { - if(pluginClassGroup == null || StringUtils.isEmpty(pluginClassGroup.groupId())){ - continue; - } - if(pluginClassGroup.filter(aClass)){ - pluginRegistryInfo.addGroupClasses(pluginClassGroup.groupId(), aClass); - findGroup = true; - } - } - if(!findGroup){ - pluginRegistryInfo.addGroupClasses(OTHER, aClass); - } - pluginRegistryInfo.addClasses(aClass); - } - } - - @Override - public void unRegistry(PluginRegistryInfo registerPluginInfo) throws Exception { - registerPluginInfo.cleanClasses(); - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/CallerGroup.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/CallerGroup.java deleted file mode 100644 index 11cee41c6fd1f406d5340429cb1e7deec4fdc78c..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/CallerGroup.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.classs.group; - -import com.gitee.starblues.annotation.Caller; -import com.gitee.starblues.factory.process.pipe.classs.PluginClassGroup; -import com.gitee.starblues.realize.BasePlugin; -import com.gitee.starblues.utils.AnnotationsUtils; - - -/** - * 分组存在注解: @Caller - * - * @author starBlues - * @version 2.1.0 - */ -public class CallerGroup implements PluginClassGroup { - - - /** - * 自定义 @Caller - */ - public static final String GROUP_ID = "caller"; - - - @Override - public String groupId() { - return GROUP_ID; - } - - @Override - public void initialize(BasePlugin basePlugin) { - - } - - @Override - public boolean filter(Class aClass) { - return AnnotationsUtils.haveAnnotations(aClass, false, Caller.class); - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/ComponentGroup.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/ComponentGroup.java deleted file mode 100644 index 99eaed96abb4ff0a54baebbafcb1c74de2828bd0..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/ComponentGroup.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.classs.group; - -import com.gitee.starblues.annotation.Extract; -import com.gitee.starblues.factory.process.pipe.classs.PluginClassGroup; -import com.gitee.starblues.realize.BasePlugin; -import com.gitee.starblues.utils.AnnotationsUtils; -import org.springframework.context.annotation.Configuration; -import org.springframework.stereotype.Component; -import org.springframework.stereotype.Controller; -import org.springframework.stereotype.Service; -import org.springframework.web.bind.annotation.RestController; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.List; - -/** - * 分组存在注解: Component、Service - * - * @author starBlues - * @version 2.4.0 - */ -public class ComponentGroup implements PluginClassGroup { - - /** - * spring 组件bean. - * 包括Component、Service - */ - public static final String GROUP_ID = "spring_component"; - - - private final List filters = new ArrayList<>(); - - public ComponentGroup(){ - filters.add(new ConfigDefinitionGroup()); - filters.add(new ConfigBeanGroup()); - filters.add(new OneselfListenerGroup()); - filters.add(new CallerGroup()); - filters.add(new SupplierGroup()); - } - - - @Override - public String groupId() { - return GROUP_ID; - } - - @Override - public void initialize(BasePlugin basePlugin) { - - } - - @Override - public boolean filter(Class aClass) { - boolean have = AnnotationsUtils.haveAnnotations(aClass, false, - Component.class, Service.class, - Controller.class, RestController.class, Configuration.class, - Extract.class); - if(!have){ - return false; - } - // 进行基本组件Bean的过滤 - for (PluginClassGroup filter : filters) { - if(filter.filter(aClass)){ - return false; - } - } - return true; - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/ConfigBeanGroup.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/ConfigBeanGroup.java deleted file mode 100644 index d837d8965ead04844712c52c75dd0fabf84f3ef0..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/ConfigBeanGroup.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.classs.group; - -import com.gitee.starblues.factory.process.pipe.classs.PluginClassGroup; -import com.gitee.starblues.realize.BasePlugin; -import com.gitee.starblues.realize.ConfigBean; -import org.springframework.util.ClassUtils; - -import java.util.Set; - -/** - * 对接口ConfigBean实现的类分组 - * @see ConfigBean - * - * @author starBlues - * @version 2.2.2 - */ -public class ConfigBeanGroup implements PluginClassGroup { - - - public static final String GROUP_ID = "config_bean"; - - - @Override - public String groupId() { - return GROUP_ID; - } - - @Override - public void initialize(BasePlugin basePlugin) { - - } - - @Override - public boolean filter(Class aClass) { - if(aClass == null){ - return false; - } - Set> allInterfacesForClassAsSet = ClassUtils.getAllInterfacesForClassAsSet(aClass); - return allInterfacesForClassAsSet.contains(ConfigBean.class); - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/ConfigDefinitionGroup.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/ConfigDefinitionGroup.java deleted file mode 100644 index 953a8adf325cd945a55a40086e20185a68f38574..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/ConfigDefinitionGroup.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.classs.group; - -import com.gitee.starblues.annotation.ConfigDefinition; -import com.gitee.starblues.factory.process.pipe.classs.PluginClassGroup; -import com.gitee.starblues.realize.BasePlugin; -import com.gitee.starblues.utils.AnnotationsUtils; -import org.springframework.util.ObjectUtils; - -/** - * 分组存在注解: @ConfigDefinition - * - * @author starBlues - * @version 2.1.0 - */ -public class ConfigDefinitionGroup implements PluginClassGroup { - - /** - * 自定义插件配置文件bean @ConfigDefinition - */ - public static final String GROUP_ID= "config_definition"; - - - @Override - public String groupId() { - return GROUP_ID; - } - - @Override - public void initialize(BasePlugin basePlugin) { - - } - - @Override - public boolean filter(Class aClass) { - if(BasePlugin.class.isAssignableFrom(aClass)){ - return false; - } - return AnnotationsUtils.haveAnnotations(aClass, false, ConfigDefinition.class); - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/ControllerGroup.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/ControllerGroup.java deleted file mode 100644 index 4a9bb31e836f140b3061617bb747ee468ce4dba2..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/ControllerGroup.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.classs.group; - -import com.gitee.starblues.factory.process.pipe.classs.PluginClassGroup; -import com.gitee.starblues.realize.BasePlugin; -import com.gitee.starblues.utils.AnnotationsUtils; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RestController; - -/** - * 分组存在注解: @Controller、@RestController - * - * @author starBlues - * @version 2.1.0 - */ -public class ControllerGroup implements PluginClassGroup { - - - /** - * spring @Controller @RestController 注解bean - */ - public static final String GROUP_ID = "spring_controller"; - - - @Override - public String groupId() { - return GROUP_ID; - } - - @Override - public void initialize(BasePlugin basePlugin) { - - } - - @Override - public boolean filter(Class aClass) { - return AnnotationsUtils.haveAnnotations(aClass, false, RestController.class, Controller.class); - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/OneselfListenerGroup.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/OneselfListenerGroup.java deleted file mode 100644 index 282ee8447337816f549efca163272a5d7d9a883b..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/OneselfListenerGroup.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.classs.group; - -import com.gitee.starblues.factory.process.pipe.classs.PluginClassGroup; -import com.gitee.starblues.realize.BasePlugin; -import com.gitee.starblues.realize.OneselfListener; -import org.springframework.util.ClassUtils; - -import java.util.Set; - -/** - * 插件自己的监听器分组 - * - * @author starBlues - * @version 2.2.1 - */ -public class OneselfListenerGroup implements PluginClassGroup { - - public static final String GROUP_ID = "oneself_listener"; - - @Override - public String groupId() { - return GROUP_ID; - } - - @Override - public void initialize(BasePlugin basePlugin) { - - } - - @Override - public boolean filter(Class aClass) { - if(aClass == null){ - return false; - } - Set> allInterfacesForClassAsSet = ClassUtils.getAllInterfacesForClassAsSet(aClass); - return allInterfacesForClassAsSet.contains(OneselfListener.class); - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/RepositoryGroup.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/RepositoryGroup.java deleted file mode 100644 index 5efb442743e8d453a2e43abe36259f5c57667173..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/RepositoryGroup.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.classs.group; - -import com.gitee.starblues.factory.process.pipe.classs.PluginClassGroup; -import com.gitee.starblues.realize.BasePlugin; -import com.gitee.starblues.utils.AnnotationsUtils; -import org.springframework.stereotype.Repository; - -/** - * 分组存在注解: @Repository - * - * @author starBlues - * @version 2.1.0 - */ -public class RepositoryGroup implements PluginClassGroup { - - /** - * spring @Repository 注解bean - */ - public static final String GROUP_ID = "spring_repository"; - - - @Override - public String groupId() { - return GROUP_ID; - } - - @Override - public void initialize(BasePlugin basePlugin) { - - } - - @Override - public boolean filter(Class aClass) { - return AnnotationsUtils.haveAnnotations(aClass, false, Repository.class); - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/SupplierGroup.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/SupplierGroup.java deleted file mode 100644 index b673e7d665646a4b616c2dd79d03384dea347bb7..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/SupplierGroup.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.classs.group; - -import com.gitee.starblues.annotation.Supplier; -import com.gitee.starblues.factory.process.pipe.classs.PluginClassGroup; -import com.gitee.starblues.realize.BasePlugin; -import com.gitee.starblues.utils.AnnotationsUtils; - - -/** - * 分组存在注解: @Supplier - * - * @author starBlues - * @version 2.1.0 - */ -public class SupplierGroup implements PluginClassGroup { - - /** - * 自定义 @Supplier - */ - public static final String GROUP_ID = "supplier"; - - - @Override - public String groupId() { - return GROUP_ID; - } - - @Override - public void initialize(BasePlugin basePlugin) { - - } - - @Override - public boolean filter(Class aClass) { - return AnnotationsUtils.haveAnnotations(aClass, false, Supplier.class); - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/WebSocketGroup.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/WebSocketGroup.java deleted file mode 100644 index cdf3d7b9012e8dc7be930e0161667c062922be22..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/classs/group/WebSocketGroup.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.classs.group; - -import com.gitee.starblues.factory.process.pipe.classs.PluginClassGroup; -import com.gitee.starblues.realize.BasePlugin; -import com.gitee.starblues.utils.AnnotationsUtils; -import javax.websocket.server.ServerEndpoint; - -/** - * 分组存在注解: @ServerEndpoint - * - * @author sousouki - */ -public class WebSocketGroup implements PluginClassGroup { - - public static final String GROUP_ID = "websocket"; - - @Override - public String groupId() { - return GROUP_ID; - } - - @Override - public void initialize(BasePlugin basePlugin) { - - } - - @Override - public boolean filter(Class aClass) { - return AnnotationsUtils.haveAnnotations(aClass, false, ServerEndpoint.class); - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/extract/PluginExtractPipeProcessor.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/extract/PluginExtractPipeProcessor.java deleted file mode 100644 index a7c3f812d8055ccc28aa093131e9369c863c235f..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/extract/PluginExtractPipeProcessor.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.extract; - -import com.gitee.starblues.annotation.Extract; -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.SpringBeanRegister; -import com.gitee.starblues.factory.process.pipe.PluginPipeProcessor; -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.GenericApplicationContext; - -import java.util.Map; - -/** - * 插件扩展处理者 - * @author starBlues - * @version 2.4.1 - */ -public class PluginExtractPipeProcessor implements PluginPipeProcessor { - - private final ApplicationContext mainApplicationContext; - private final SpringBeanRegister springBeanRegister; - private final ExtractFactory extractFactory; - - public PluginExtractPipeProcessor(ApplicationContext mainApplicationContext) { - this.mainApplicationContext = mainApplicationContext; - this.springBeanRegister = new SpringBeanRegister((GenericApplicationContext) mainApplicationContext); - this.extractFactory = ExtractFactory.getInstant(); - } - - @Override - public void initialize() throws Exception { - springBeanRegister.registerSingleton(ExtractFactory.class.getName(), extractFactory); - // 获取主程序的扩展 - Map extractMap = mainApplicationContext.getBeansWithAnnotation(Extract.class); - if(!extractMap.isEmpty()){ - for (Object extract : extractMap.values()) { - extractFactory.addOfMain(extract); - } - } - } - - @Override - public void registry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - GenericApplicationContext pluginApplicationContext = pluginRegistryInfo.getPluginApplicationContext(); - Map extractMap = pluginApplicationContext.getBeansWithAnnotation(Extract.class); - if(extractMap.isEmpty()){ - return; - } - String pluginId = pluginRegistryInfo.getPluginWrapper().getPluginId(); - for (Object extract : extractMap.values()) { - extractFactory.add(pluginId, extract); - } - pluginRegistryInfo.getSpringBeanRegister().registerSingleton( - ExtractFactory.class.getName(), extractFactory - ); - } - - @Override - public void unRegistry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - String pluginId = pluginRegistryInfo.getPluginWrapper().getPluginId(); - extractFactory.remove(pluginId); - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/interceptor/PluginInterceptorRegister.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/interceptor/PluginInterceptorRegister.java deleted file mode 100644 index 56fcba37c58a77614a9a352a1c5a121e6f16a857..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/interceptor/PluginInterceptorRegister.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.interceptor; - -/** - * 插件拦截器注册者 - * @author starBlues - * @version 2.4.1 - */ -public interface PluginInterceptorRegister { - - /** - * 拦截器注册者 - * @param registry 注册对象 - */ - void registry(PluginInterceptorRegistry registry); - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/loader/PluginResourceLoadFactory.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/loader/PluginResourceLoadFactory.java deleted file mode 100644 index 2646be039caff3f22207dfa8b6f38420a3957532..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/loader/PluginResourceLoadFactory.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.loader; - -import com.gitee.starblues.extension.ExtensionInitializer; -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.pipe.PluginPipeProcessor; -import com.gitee.starblues.factory.process.pipe.loader.load.PluginClassLoader; -import com.gitee.starblues.realize.BasePlugin; -import com.gitee.starblues.utils.CommonUtils; -import com.gitee.starblues.utils.OrderPriority; -import org.pf4j.util.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * 插件资源加载者 - * - * @author starBlues - * @version 2.4.0 - */ -public class PluginResourceLoadFactory implements PluginPipeProcessor { - - private static final Logger LOG = LoggerFactory.getLogger(PluginResourceLoadFactory.class); - - - private final List pluginResourceLoaders = new ArrayList<>(5); - - - public PluginResourceLoadFactory() { - this.pluginResourceLoaders.add(new PluginClassLoader()); - // 添加扩展 - this.pluginResourceLoaders.addAll(ExtensionInitializer.getResourceLoadersExtends()); - CommonUtils.order(pluginResourceLoaders, (pluginResourceLoader -> { - OrderPriority order = pluginResourceLoader.order(); - if (order == null) { - order = OrderPriority.getMiddlePriority(); - } - return order.getPriority(); - })); - } - - @Override - public void initialize() throws Exception { - - } - - @Override - public void registry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - for (PluginResourceLoader pluginResourceLoader : pluginResourceLoaders) { - if(pluginResourceLoader == null){ - continue; - } - String key = pluginResourceLoader.key(); - if(StringUtils.isNullOrEmpty(key)){ - LOG.error("pluginResourceLoader {} key is empty, skip!", - pluginResourceLoader.getClass().getName()); - continue; - } - try { - ResourceWrapper resourceWrapper = pluginResourceLoader.load(pluginRegistryInfo); - if(resourceWrapper != null){ - pluginRegistryInfo.addPluginLoadResource(key, resourceWrapper); - } - } catch (Exception e){ - LOG.error("Plugin resource loader '{}' load error. {}", key, e.getMessage(), e); - } - } - } - - @Override - public void unRegistry(PluginRegistryInfo pluginRegistryInfo) throws Exception { - for (PluginResourceLoader pluginResourceLoader : pluginResourceLoaders) { - if(pluginResourceLoader == null){ - continue; - } - String key = pluginResourceLoader.key(); - try { - ResourceWrapper resourceWrapper = pluginRegistryInfo.getPluginLoadResource(key); - if(resourceWrapper == null){ - continue; - } - pluginResourceLoader.unload(pluginRegistryInfo, resourceWrapper); - } catch (Exception e){ - LOG.error("Plugin resource loader '{}' unload error. {}", key, e.getMessage(), e); - } - } - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/loader/PluginResourceLoader.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/loader/PluginResourceLoader.java deleted file mode 100644 index ed45d45e15d7bd68ac08cd40b885ae77fa8a699e..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/loader/PluginResourceLoader.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.loader; - -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.realize.BasePlugin; -import com.gitee.starblues.utils.OrderPriority; - -/** - * 插件资源加载者统一定义的接口 - * - * @author starBlues - * @version 2.4.0 - */ -public interface PluginResourceLoader { - - /** - * 加载者的key - * @return String - */ - String key(); - - - /** - * 加载资源 - * @param pluginRegistryInfo 插件注册者信息 - * @return 资源包装对象 - * @throws Exception Exception - */ - ResourceWrapper load(PluginRegistryInfo pluginRegistryInfo) throws Exception; - - /** - * 卸载时的操作 - * @param pluginRegistryInfo 插件对象 - * @param resourceWrapper 资源包装者 - * @throws Exception 卸载异常 - */ - void unload(PluginRegistryInfo pluginRegistryInfo, ResourceWrapper resourceWrapper) throws Exception; - - - /** - * 执行顺序 - * @return OrderPriority - */ - OrderPriority order(); - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/loader/ResourceWrapper.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/loader/ResourceWrapper.java deleted file mode 100644 index d2d7b89dd29e139f834fbaff520b6a8aa3b91146..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/loader/ResourceWrapper.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.loader; - -import org.springframework.core.io.Resource; -import org.springframework.util.StringUtils; - -import java.util.*; - -/** - * 资源包装类 - * - * @author starBlues - * @version 2.4.0 - */ -public class ResourceWrapper { - - private final List resources = new ArrayList<>(); - private final Set classPackageNames = new HashSet<>(); - private final Map extensions = new HashMap<>(); - - public void addResource(Resource resource){ - if(resource == null){ - return; - } - resources.add(resource); - } - - public void addResources(List resources){ - if(resources == null || resources.isEmpty()){ - return; - } - this.resources.addAll(resources); - } - - public List getResources(){ - return Collections.unmodifiableList(resources); - } - - public void addClassPackageName(String classFullName){ - if(StringUtils.isEmpty(classFullName)){ - return; - } - classPackageNames.add(classFullName); - } - - public void addClassPackageNames(Set classPackageNames){ - if(classPackageNames == null || classPackageNames.isEmpty()){ - return; - } - this.classPackageNames.addAll(classPackageNames); - } - - public Set getClassPackageNames(){ - return Collections.unmodifiableSet(classPackageNames); - } - - - public void addExtension(String key, Object value) { - extensions.put(key, value); - } - - public Object getExtension(String key){ - return extensions.get(key); - } - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/loader/load/PluginClassLoader.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/loader/load/PluginClassLoader.java deleted file mode 100644 index 7e26a58effa7ec85e81f10a9a98bfb662720a74f..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/loader/load/PluginClassLoader.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.loader.load; - -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.pipe.loader.PluginResourceLoader; -import com.gitee.starblues.factory.process.pipe.loader.ResourceWrapper; -import com.gitee.starblues.realize.BasePlugin; -import com.gitee.starblues.utils.OrderPriority; -import com.gitee.starblues.utils.ScanUtils; -import org.pf4j.RuntimeMode; - -import java.util.Set; - -/** - * 插件类文件加载者 - * - * @author starBlues - * @version 2.4.0 - */ -public class PluginClassLoader implements PluginResourceLoader { - - public static final String KEY = "PluginClassProcess"; - - @Override - public String key() { - return KEY; - } - - @Override - public ResourceWrapper load(PluginRegistryInfo pluginRegistryInfo) throws Exception{ - RuntimeMode runtimeMode = pluginRegistryInfo.getPluginWrapper().getRuntimeMode(); - BasePlugin basePlugin = pluginRegistryInfo.getBasePlugin(); - Set classPackageName = null; - if(runtimeMode == RuntimeMode.DEPLOYMENT){ - // 生产环境 - classPackageName = ScanUtils.scanClassPackageName( - basePlugin.scanPackage(), basePlugin.getWrapper()); - - } else if(runtimeMode == RuntimeMode.DEVELOPMENT){ - // 开发环境 - classPackageName = ScanUtils.scanClassPackageName( - basePlugin.scanPackage(), basePlugin.getClass()); - } - ResourceWrapper resourceWrapper = new ResourceWrapper(); - resourceWrapper.addClassPackageNames(classPackageName); - return resourceWrapper; - } - - @Override - public void unload(PluginRegistryInfo pluginRegistryInfo, ResourceWrapper resourceWrapper) throws Exception { - // Do nothing - } - - @Override - public OrderPriority order() { - return OrderPriority.getHighPriority(); - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/loader/load/PluginConfigFileLoader.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/loader/load/PluginConfigFileLoader.java deleted file mode 100644 index 1628b808810df19879526497656a0a906b84daf0..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/pipe/loader/load/PluginConfigFileLoader.java +++ /dev/null @@ -1,140 +0,0 @@ -package com.gitee.starblues.factory.process.pipe.loader.load; - -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.pipe.loader.PluginResourceLoader; -import com.gitee.starblues.factory.process.pipe.loader.ResourceWrapper; -import com.gitee.starblues.realize.BasePlugin; -import com.gitee.starblues.utils.OrderPriority; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.FileSystemResource; -import org.springframework.core.io.Resource; - -import java.io.File; -import java.io.FileNotFoundException; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Supplier; - -/** - * 插件配置文件加载者 - * - * @author starBlues - * @version 2.4.0 - */ -public class PluginConfigFileLoader implements PluginResourceLoader { - - private final static Logger log = LoggerFactory.getLogger(PluginConfigFileLoader.class); - - private final String configFilePath; - private final String fileName; - - public PluginConfigFileLoader(String configFilePath, - String fileName) { - this.configFilePath = configFilePath; - this.fileName = fileName; - } - - - @Override - public String key() { - return null; - } - - @Override - public ResourceWrapper load(PluginRegistryInfo pluginRegistryInfo) throws Exception { - List> suppliers = new ArrayList<>(); - BasePlugin basePlugin = pluginRegistryInfo.getBasePlugin(); - suppliers.add(findConfigRoot()); - suppliers.add(findPluginRoot(basePlugin)); - suppliers.add(findClassPath(pluginRegistryInfo.getPluginClassLoader())); - - for (Supplier supplier : suppliers) { - SupplierBean supplierBean = supplier.get(); - Resource resource = supplierBean.getResource(); - if(resource.exists()){ - List resources = new ArrayList<>(); - resources.add(resource); - ResourceWrapper resourceWrapper = new ResourceWrapper(); - resourceWrapper.addResources(resources); - log.info("Load the plugin '{}' config file '{}' from '{}'", - basePlugin.getWrapper().getPluginId(), fileName, supplierBean.getPath()); - return resourceWrapper; - } - } - throw new FileNotFoundException("Not found plugin '" + basePlugin.getWrapper().getPluginId() + "' " + - "config file : " + fileName); - } - - @Override - public void unload(PluginRegistryInfo pluginRegistryInfo, ResourceWrapper resourceWrapper) throws Exception { - // Do nothing - } - - @Override - public OrderPriority order() { - return OrderPriority.getHighPriority().down(20); - } - - /** - * 从插件文件的根目录查找配置文件 - * @param basePlugin basePlugin - * @return 返回resource - */ - private Supplier findPluginRoot(BasePlugin basePlugin){ - return ()->{ - Path pluginPath = basePlugin.getWrapper().getPluginPath(); - String rootPath = pluginPath.getParent().toString(); - String configPath = rootPath + File.separatorChar + fileName; - Resource resource = new FileSystemResource(configPath); - return new SupplierBean(rootPath, resource); - }; - } - - - /** - * 从插件配置文件 pluginConfigFilePath 的路径下查找配置文件 - * @return 返回resource - */ - private Supplier findConfigRoot(){ - return ()->{ - String filePath = configFilePath + File.separatorChar + fileName; - Resource resource = new FileSystemResource(filePath); - return new SupplierBean(configFilePath, resource); - }; - } - - /** - * 从ClassPath 中查找配置文件 - * @param classLoader classLoader - * @return 返回resource - */ - private Supplier findClassPath(ClassLoader classLoader){ - return ()->{ - Resource resource = new ClassPathResource("/" + fileName, classLoader); - return new SupplierBean("classPath", resource); - }; - } - - private class SupplierBean{ - private String path; - private Resource resource; - - public SupplierBean(String path, Resource resource) { - this.path = path; - this.resource = resource; - } - - public String getPath() { - return path; - } - - public Resource getResource() { - return resource; - } - } - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/PluginPostProcessor.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/PluginPostProcessor.java deleted file mode 100644 index 9436192e2ec784980485c798ce5a0b14c3abffdd..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/PluginPostProcessor.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.gitee.starblues.factory.process.post; - -import com.gitee.starblues.factory.PluginRegistryInfo; - -import java.util.List; - -/** - * 插件后置处理者接口 - * - * @author starBlues - * @version 2.1.0 - */ -public interface PluginPostProcessor { - - /** - * 初始化 - * @throws Exception 初始化异常 - */ - void initialize() throws Exception; - - - /** - * 处理该插件的注册 - * @param pluginRegistryInfos 插件注册的信息 - * @throws Exception 处理异常 - */ - void registry(List pluginRegistryInfos) throws Exception; - - - /** - * 处理该插件的卸载 - * @param pluginRegistryInfos 插件注册的信息 - * @throws Exception 处理异常 - */ - void unRegistry(List pluginRegistryInfos) throws Exception; - - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/PluginPostProcessorExtend.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/PluginPostProcessorExtend.java deleted file mode 100644 index 3c7a58f2469feb3cdee021b515242c4a8079f984..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/PluginPostProcessorExtend.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.gitee.starblues.factory.process.post; - -import com.gitee.starblues.utils.OrderPriority; - -/** - * 后置插件处理者 - * - * @author starBlues - * @version 2.1.0 - */ -public interface PluginPostProcessorExtend extends PluginPostProcessor{ - - /** - * 扩展key - * @return String - */ - String key(); - - /** - * 执行顺序 - * @return OrderPriority - */ - OrderPriority order(); - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/PluginPostProcessorFactory.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/PluginPostProcessorFactory.java deleted file mode 100644 index 778b29edb89029ea2ecc46114c44f6cd558a7755..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/PluginPostProcessorFactory.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.gitee.starblues.factory.process.post; - -import com.gitee.starblues.extension.ExtensionInitializer; -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.post.bean.*; -import com.gitee.starblues.integration.IntegrationConfiguration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationContext; - -import java.util.ArrayList; -import java.util.List; - -/** - * 插件后置处理工厂 - * - * @author starBlues - * @version 2.4.0 - */ -public class PluginPostProcessorFactory implements PluginPostProcessor { - - private final Logger LOGGER = LoggerFactory.getLogger(this.getClass()); - - private final List pluginPostProcessors = new ArrayList<>(); - private final ApplicationContext mainApplicationContext; - private final IntegrationConfiguration integrationConfiguration; - - public PluginPostProcessorFactory(ApplicationContext mainApplicationContext){ - this.mainApplicationContext = mainApplicationContext; - this.integrationConfiguration = mainApplicationContext.getBean(IntegrationConfiguration.class); - } - - @Override - public void initialize() throws Exception{ - // 以下顺序不能更改 - pluginPostProcessors.add(new PluginInvokePostProcessor(mainApplicationContext)); - pluginPostProcessors.add(new PluginControllerPostProcessor(mainApplicationContext)); - if(integrationConfiguration.enableWebSocket()){ - // 如果配置启用webSocket的功能, 则进行引入 - pluginPostProcessors.add(new PluginWebSocketProcessor(mainApplicationContext)); - } - // 添加扩展 - pluginPostProcessors.addAll(ExtensionInitializer.getPostProcessorExtends()); - - // 主要触发启动监听事件,因此在最后一个执行。配合 OneselfListenerStopEventProcessor 该类触发启动、停止事件。 - pluginPostProcessors.add(new PluginOneselfStartEventProcessor()); - // 进行初始化 - for (PluginPostProcessor pluginPostProcessor : pluginPostProcessors) { - pluginPostProcessor.initialize(); - } - } - - - @Override - public void registry(List pluginRegistryInfos) throws Exception{ - for (PluginPostProcessor pluginPostProcessor : pluginPostProcessors) { - pluginPostProcessor.registry(pluginRegistryInfos); - } - } - - @Override - public void unRegistry(List pluginRegistryInfos) throws Exception{ - boolean findException = false; - for (PluginPostProcessor pluginPostProcessor : pluginPostProcessors) { - try { - pluginPostProcessor.unRegistry(pluginRegistryInfos); - } catch (Exception e){ - findException = true; - LOGGER.error("PluginPostProcessor '{}' unRegistry process exception", - pluginPostProcessor.getClass().getName(), e); - } - } - if(findException){ - throw new Exception("UnRegistry plugin failure"); - } - } -} 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 deleted file mode 100644 index 3afb71a8ae99a3d3df376be9a5924fda172f4e3f..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/bean/PluginControllerPostProcessor.java +++ /dev/null @@ -1,249 +0,0 @@ -package com.gitee.starblues.factory.process.post.bean; - -import com.gitee.starblues.extension.ExtensionFactory; -import com.gitee.starblues.extension.PluginControllerProcessorExtend; -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.pipe.classs.group.ControllerGroup; -import com.gitee.starblues.factory.process.post.PluginPostProcessor; -import com.gitee.starblues.factory.process.post.bean.model.ControllerWrapper; -import com.gitee.starblues.integration.IntegrationConfiguration; -import com.gitee.starblues.utils.ClassUtils; -import com.gitee.starblues.utils.CommonUtils; -import org.pf4j.util.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.util.ReflectionUtils; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.servlet.mvc.method.RequestMappingInfo; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; -import java.lang.reflect.Method; -import java.util.*; -import java.util.function.Consumer; - -/** - * 插件中controller处理者 - * - * @author starBlues - * @version 2.4.0 - */ -public class PluginControllerPostProcessor implements PluginPostProcessor { - - private final Logger log = LoggerFactory.getLogger(this.getClass()); - - private static final String KEY = "PluginControllerPostProcessor"; - - private final RequestMappingHandlerMapping requestMappingHandlerMapping; - private final IntegrationConfiguration configuration; - - private final List pluginControllerProcessors; - - public PluginControllerPostProcessor(ApplicationContext mainApplicationContext){ - Objects.requireNonNull(mainApplicationContext); - this.requestMappingHandlerMapping = mainApplicationContext.getBean(RequestMappingHandlerMapping.class); - this.configuration = mainApplicationContext.getBean(IntegrationConfiguration.class); - this.pluginControllerProcessors = ExtensionFactory - .getPluginControllerProcessorExtend(mainApplicationContext); - } - - - @Override - public void initialize() throws Exception { - resolveProcessExtend(extend->{ - try { - extend.initialize(); - }catch (Exception e){ - log.error("'{}' initialize error", - extend.getClass().getName(), - e); - } - }); - } - - @Override - public void registry(List pluginRegistryInfos) throws Exception { - for (PluginRegistryInfo pluginRegistryInfo : pluginRegistryInfos) { - List> groupClasses = pluginRegistryInfo.getGroupClasses(ControllerGroup.GROUP_ID); - if(groupClasses == null || groupClasses.isEmpty()){ - continue; - } - String pluginId = pluginRegistryInfo.getPluginWrapper().getPluginId(); - List controllerBeanWrappers = new ArrayList<>(); - for (Class groupClass : groupClasses) { - if(groupClass == null){ - continue; - } - try { - ControllerWrapper controllerBeanWrapper = registry(pluginRegistryInfo, groupClass); - controllerBeanWrappers.add(controllerBeanWrapper); - } catch (Exception e){ - pluginRegistryInfo.addProcessorInfo(getKey(pluginRegistryInfo), controllerBeanWrappers); - throw e; - } - } - resolveProcessExtend(extend->{ - try { - extend.registry(pluginId, controllerBeanWrappers); - }catch (Exception e){ - log.error("'{}' process plugin[{}] error in registry", - extend.getClass().getName(), - pluginId, e); - } - }); - pluginRegistryInfo.addProcessorInfo(getKey(pluginRegistryInfo), controllerBeanWrappers); - } - } - - @Override - public void unRegistry(List pluginRegistryInfos) { - for (PluginRegistryInfo pluginRegistryInfo : pluginRegistryInfos) { - List controllerBeanWrappers = - pluginRegistryInfo.getProcessorInfo(getKey(pluginRegistryInfo)); - if(controllerBeanWrappers == null || controllerBeanWrappers.isEmpty()){ - continue; - } - String pluginId = pluginRegistryInfo.getPluginWrapper().getPluginId(); - for (ControllerWrapper controllerBeanWrapper : controllerBeanWrappers) { - if(controllerBeanWrapper == null){ - continue; - } - unregister(controllerBeanWrapper); - } - resolveProcessExtend(extend->{ - try { - extend.unRegistry(pluginId, controllerBeanWrappers); - }catch (Exception e){ - log.error("'{}' process plugin[{}] error in unRegistry", - extend.getClass().getName(), - pluginId, e); - } - }); - } - } - - /** - * 注册单一插件 - * @param pluginRegistryInfo 注册的插件信息 - * @param aClass controller 类 - * @return ControllerBeanWrapper - * @throws Exception Exception - */ - private ControllerWrapper registry(PluginRegistryInfo pluginRegistryInfo, Class aClass) - throws Exception { - String pluginId = pluginRegistryInfo.getPluginWrapper().getPluginId(); - GenericApplicationContext pluginApplicationContext = pluginRegistryInfo.getPluginApplicationContext(); - try { - Object object = pluginApplicationContext.getBean(aClass); - ControllerWrapper controllerBeanWrapper = new ControllerWrapper(); - 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) { - if (isHaveRequestMapping(method)) { - RequestMappingInfo requestMappingInfo = (RequestMappingInfo) - getMappingForMethod.invoke(requestMappingHandlerMapping, method, aClass); - requestMappingHandlerMapping.registerMapping(requestMappingInfo, object, method); - requestMappingInfos.add(requestMappingInfo); - } - } - controllerBeanWrapper.setRequestMappingInfos(requestMappingInfos); - controllerBeanWrapper.setBeanClass(aClass); - return controllerBeanWrapper; - } catch (Exception e){ - // 出现异常, 卸载该 controller bean - throw e; - } - } - - /** - * 卸载具体的Controller操作 - * @param controllerBeanWrapper controllerBean包装 - */ - private void unregister(ControllerWrapper controllerBeanWrapper) { - Set requestMappingInfos = controllerBeanWrapper.getRequestMappingInfos(); - if(requestMappingInfos != null && !requestMappingInfos.isEmpty()){ - for (RequestMappingInfo requestMappingInfo : requestMappingInfos) { - requestMappingHandlerMapping.unregisterMapping(requestMappingInfo); - } - } - } - - /** - * 调用扩展出的接口控制器 - * @param extendConsumer 扩展消费者 - */ - private void resolveProcessExtend(Consumer extendConsumer){ - if(pluginControllerProcessors == null || pluginControllerProcessors.isEmpty()){ - return; - } - for (PluginControllerProcessorExtend pluginControllerProcessor : pluginControllerProcessors) { - extendConsumer.accept(pluginControllerProcessor); - } - } - - /** - * 得到往RegisterPluginInfo->processorInfo 保存的key - * @param registerPluginInfo 注册的插件信息 - * @return String - */ - private String getKey(PluginRegistryInfo registerPluginInfo){ - return KEY + "_" + registerPluginInfo.getPluginWrapper().getPluginId(); - } - - /** - * 设置请求路径前缀 - * @param aClass controller 类 - */ - private void setPathPrefix(String pluginId, - Class aClass) { - RequestMapping requestMapping = aClass.getAnnotation(RequestMapping.class); - if(requestMapping == null){ - return; - } - String pathPrefix = CommonUtils.getPluginRestPrefix(configuration, pluginId); - if(StringUtils.isNullOrEmpty(pathPrefix)){ - return; - } - Set definePaths = new HashSet<>(); - definePaths.addAll(Arrays.asList(requestMapping.path())); - definePaths.addAll(Arrays.asList(requestMapping.value())); - try { - Map memberValues = ClassUtils.getAnnotationsUpdater(requestMapping); - String[] newPath = new String[definePaths.size()]; - int i = 0; - for (String definePath : definePaths) { - // 解决插件启用、禁用后, 路径前缀重复的问题。 - if(definePath.contains(pathPrefix)){ - newPath[i++] = definePath; - } else { - newPath[i++] = CommonUtils.restJoiningPath(pathPrefix, definePath); - } - } - if(newPath.length == 0){ - newPath = new String[]{ pathPrefix }; - } - memberValues.put("path", newPath); - memberValues.put("value", new String[]{}); - } catch (Exception e) { - log.error("Define Plugin RestController pathPrefix error : {}", e.getMessage(), e); - } - } - - /** - * 方法上是否存在 @RequestMapping 注解 - * @param method method - * @return boolean - */ - private boolean isHaveRequestMapping(Method method){ - if (AnnotationUtils.findAnnotation(method, RequestMapping.class) != null) { - return true; - } else { - return false; - } - } -} 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 deleted file mode 100644 index ccfaa18cfe0be649d74f52a9af01462d967530f3..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/bean/PluginInvokePostProcessor.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.gitee.starblues.factory.process.post.bean; - -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.pipe.bean.InvokeBeanRegistrar; -import com.gitee.starblues.factory.process.post.PluginPostProcessor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.aop.framework.AdvisedSupport; -import org.springframework.aop.framework.AopProxy; -import org.springframework.aop.support.AopUtils; -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.GenericApplicationContext; - -import java.lang.reflect.Field; -import java.util.List; -import java.util.Objects; -import java.util.Set; - -/** - * 处理插件中类之间相互调用的的功能. 主要获取被调用者的对象, 然后存储到被调用者容器中 - * - * @author starBlues - * @version 2.4.0 - */ -public class PluginInvokePostProcessor implements PluginPostProcessor { - - private final Logger log = LoggerFactory.getLogger(this.getClass()); - - public PluginInvokePostProcessor(ApplicationContext applicationContext){ - Objects.requireNonNull(applicationContext); - } - - - @Override - public void initialize() throws Exception { - - } - - @Override - public void registry(List pluginRegistryInfos) throws Exception { - for (PluginRegistryInfo pluginRegistryInfo : pluginRegistryInfos) { - String pluginId = pluginRegistryInfo.getPluginWrapper().getPluginId(); - try { - Set supperBeanNames = pluginRegistryInfo.getExtension(InvokeBeanRegistrar.SUPPLIER_KEY); - if(supperBeanNames == null || supperBeanNames.isEmpty()){ - continue; - } - GenericApplicationContext pluginApplicationContext = pluginRegistryInfo.getPluginApplicationContext(); - for (String supperBeanName : supperBeanNames) { - if(pluginApplicationContext.containsBean(supperBeanName)){ - Object bean = pluginApplicationContext.getBean(supperBeanName); - InvokeBeanRegistrar.addSupper(pluginId, supperBeanName, getTarget(bean)); - } - } - } catch (Exception e){ - log.error("Process plugin '{}' supper bean exception.", pluginId, e); - } - } - } - - - @Override - public void unRegistry(List pluginRegistryInfos) throws Exception{ - // 什么也不做 - } - - /** - * 获取 目标对象. 解决@Supplier中开启事务,@Caller中调用异常 - * fix: https://gitee.com/starblues/springboot-plugin-framework-parent/issues/I4BOSK - * @param proxy 代理对象 - * @return 目标对象 - * @throws Exception Exception - */ - public Object getTarget(Object proxy) throws Exception { - //不是代理对象 - if(!AopUtils.isAopProxy(proxy)) { - return proxy; - } - if(AopUtils.isJdkDynamicProxy(proxy)) { - return getJdkDynamicProxyTargetObject(proxy); - } else { - //cglib - return getCglibProxyTargetObject(proxy); - } - } - - private Object getCglibProxyTargetObject(Object proxy) throws Exception { - Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0"); - h.setAccessible(true); - Object dynamicAdvisedInterceptor = h.get(proxy); - Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised"); - advised.setAccessible(true); - return ((AdvisedSupport)advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget(); - } - - private Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception { - Field h = proxy.getClass().getSuperclass().getDeclaredField("h"); - h.setAccessible(true); - AopProxy aopProxy = (AopProxy) h.get(proxy); - Field advised = aopProxy.getClass().getDeclaredField("advised"); - advised.setAccessible(true); - return ((AdvisedSupport)advised.get(aopProxy)).getTargetSource().getTarget(); - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/bean/PluginOneselfStartEventProcessor.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/bean/PluginOneselfStartEventProcessor.java deleted file mode 100644 index 94854ed2eb6cd0ada954d78f8cae1badae60fd2b..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/bean/PluginOneselfStartEventProcessor.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.gitee.starblues.factory.process.post.bean; - -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.post.PluginPostProcessor; -import com.gitee.starblues.realize.BasePlugin; -import com.gitee.starblues.realize.OneselfListener; -import com.gitee.starblues.utils.CommonUtils; -import com.gitee.starblues.utils.SpringBeanUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.support.GenericApplicationContext; - -import java.util.ArrayList; -import java.util.List; - -/** - * 插件中 OneselfListener 监听者处理者。主要执行监听器的启动事件。 - * @author starBlues - * @version 2.4.0 - */ -public class PluginOneselfStartEventProcessor implements PluginPostProcessor { - - private final Logger log = LoggerFactory.getLogger(this.getClass()); - public final static String KEY = "OneselfListeners"; - - public PluginOneselfStartEventProcessor(){ - } - - - @Override - public void initialize() throws Exception { - - } - - @Override - public void registry(List pluginRegistryInfos) throws Exception { - for (PluginRegistryInfo pluginRegistryInfo : pluginRegistryInfos) { - BasePlugin basePlugin = pluginRegistryInfo.getBasePlugin(); - try { - GenericApplicationContext pluginApplicationContext = pluginRegistryInfo.getPluginApplicationContext(); - List oneselfListeners = SpringBeanUtils.getBeans(pluginApplicationContext, OneselfListener.class); - List saveOneselfListeners = new ArrayList<>(oneselfListeners.size()); - oneselfListeners.stream() - .filter(oneselfListener -> oneselfListener != null) - .sorted(CommonUtils.orderPriority(oneselfListener -> oneselfListener.order())) - .forEach(oneselfListener -> { - try { - oneselfListener.startEvent(basePlugin); - } catch (Exception e){ - log.error("OneselfListener {} execute stopEvent exception. {}", - oneselfListener.getClass().getName(), e.getMessage(), e); - } finally { - saveOneselfListeners.add(oneselfListener); - } - }); - if(!saveOneselfListeners.isEmpty()){ - pluginRegistryInfo.addExtension(KEY, saveOneselfListeners); - } - } catch (Exception e){ - log.error("Plugin '{}' OneselfListener process exception.", basePlugin.getWrapper().getPluginId(), e); - } - } - } - - - @Override - public void unRegistry(List pluginRegistryInfos) { - // 当前不执行停止事件 - // 由类 com.gitee.starblues.factory.process.pipe.PluginOneselfStopEventProcessor 实现停止 - // 主要考虑到停止时, 需要先执行, 自主来关闭当前插件某些资源 - } - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/bean/PluginWebSocketProcessor.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/bean/PluginWebSocketProcessor.java deleted file mode 100644 index f8db4c92e0441a76aa85615fbe909c04dcc4ca44..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/bean/PluginWebSocketProcessor.java +++ /dev/null @@ -1,254 +0,0 @@ -package com.gitee.starblues.factory.process.post.bean; - -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.factory.process.pipe.classs.group.WebSocketGroup; -import com.gitee.starblues.factory.process.post.PluginPostProcessor; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentSkipListMap; -import javax.servlet.ServletContext; -import javax.websocket.DeploymentException; -import javax.websocket.EndpointConfig; -import javax.websocket.Session; -import javax.websocket.server.ServerContainer; -import javax.websocket.server.ServerEndpoint; -import javax.websocket.server.ServerEndpointConfig; - -import com.gitee.starblues.utils.ClassUtils; -import org.pf4j.util.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.web.context.WebApplicationContext; -import org.springframework.web.socket.server.standard.ServerEndpointExporter; - -/** - * 插件中websocket处理者 - * - * @author sousouki - * @version 2.4.2 - */ -public class PluginWebSocketProcessor implements PluginPostProcessor { - - private static final Logger log = LoggerFactory.getLogger(PluginWebSocketProcessor.class); - - public static final String KEY = "PluginWsConfigProcessor"; - private static final String WEB_SOCKET_PATH = "WEB_SOCKET_PATH"; - - private final ApplicationContext applicationContext; - - public PluginWebSocketProcessor(ApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } - - @Override - public void initialize() throws Exception { - - } - - @Override - public void registry(List pluginRegistryInfos) throws Exception { - ServerContainer serverContainer = getServerContainer(); - if (serverContainer == null) return; - - for (PluginRegistryInfo pluginRegistryInfo : pluginRegistryInfos) { - Map webSocketPathMap = new HashMap<>(); - List> websocketClasses = pluginRegistryInfo.getGroupClasses(WebSocketGroup.GROUP_ID); - String pluginId = pluginRegistryInfo.getPluginWrapper().getPluginId(); - websocketClasses.forEach(websocketClass -> { - ServerEndpoint serverEndpoint = websocketClass.getDeclaredAnnotation(ServerEndpoint.class); - if (serverEndpoint == null) { - log.warn("WebSocket class {} doesn't has annotation {}", websocketClass.getName(), ServerEndpoint.class.getName()); - return; - } - String sourcePath = serverEndpoint.value(); - if (StringUtils.isNullOrEmpty(sourcePath)) { - return; - } - String processPath = sourcePath; - if(!processPath.startsWith("/")){ - processPath = "/".concat(processPath); - } - UriTemplate uriTemplate; - - try { - uriTemplate = new UriTemplate(processPath); - } catch (DeploymentException e) { - log.error("Websocket path validate failed.", e); - return; - } - String newWebsocketPath = "/".concat(pluginId).concat(processPath); - String newWebsocketTemplatePath = "/".concat(pluginId).concat(uriTemplate.getPath()); - Map annotationsUpdater = null; - try { - annotationsUpdater = ClassUtils.getAnnotationsUpdater(serverEndpoint); - } catch (Exception e) { - log.error("Process and update websocket path '{}' annotation exception.", sourcePath, e); - return; - } - try { - annotationsUpdater.put("value", newWebsocketPath); - serverContainer.addEndpoint(websocketClass); - webSocketPathMap.put(newWebsocketPath, newWebsocketTemplatePath); - log.info("Succeed to create websocket service for path {}", newWebsocketPath); - } catch (Exception e) { - log.error("Create websocket service for websocket class " + websocketClass.getName() + " failed.", e); - } finally { - annotationsUpdater.put("value", sourcePath); - } - }); - pluginRegistryInfo.addExtension(WEB_SOCKET_PATH, webSocketPathMap); - } - } - - @Override - public void unRegistry(List pluginRegistryInfos) throws Exception { - ServerContainer serverContainer = getServerContainer(); - if (serverContainer == null) { - log.warn("Not found ServerContainer, So websocket can't used!"); - return; - } - Map configExactMatchMap = ClassUtils.getReflectionField(serverContainer, "configExactMatchMap"); - Map> configTemplateMatchMap = - ClassUtils.getReflectionField(serverContainer, "configTemplateMatchMap"); - Map endpointSessionMap = ClassUtils.getReflectionField(serverContainer, "endpointSessionMap"); - Map sessions = ClassUtils.getReflectionField(serverContainer, "sessions"); - pluginRegistryInfos.forEach(pluginRegistryInfo -> { - Map webSocketPathMap = pluginRegistryInfo.getExtension(WEB_SOCKET_PATH); - webSocketPathMap.forEach((webSocketPath,newWebsocketTemplatePath)->{ - configExactMatchMap.remove(webSocketPath); - log.debug("Removed websocket config for path {}", webSocketPath); - configTemplateMatchMap.forEach((key, value) -> { - value.remove(newWebsocketTemplatePath); - }); - endpointSessionMap.remove(webSocketPath); - log.debug("Removed websocket session for path {}", webSocketPath); - - for (Map.Entry entry : sessions.entrySet()) { - Session session = entry.getKey(); - try { - if(closeSession(session, webSocketPath)){ - sessions.remove(session); - log.debug("Removed websocket session {} for path {}", session.getId(), webSocketPath); - } - } catch (Exception e) { - log.debug("Close websocket session {} for path {} failure", session.getId(), webSocketPath, e); - } - } - log.info("Remove websocket for path {} success.", webSocketPath); - }); - }); - } - - /** - * 得到 Tomcat ServerContainer - * @return ServerContainer - */ - private ServerContainer getServerContainer() { - try { - applicationContext.getBean(ServerEndpointExporter.class); - } catch (BeansException e) { - log.debug("The required bean of {} not found, if you want to use plugin websocket, please create it.", ServerEndpointExporter.class.getName()); - return null; - } - if (!(applicationContext instanceof WebApplicationContext)) { - return null; - } - WebApplicationContext webApplicationContext = (WebApplicationContext) applicationContext; - ServletContext servletContext = webApplicationContext.getServletContext(); - if (servletContext == null) { - log.warn("Servlet context is null."); - return null; - } - Object obj = servletContext.getAttribute("javax.websocket.server.ServerContainer"); - if (!(obj instanceof ServerContainer)) { - return null; - } - return (ServerContainer) obj; - } - - /** - * 关闭session - * @param session session - * @param websocketPath websocketPath 路径 - * @return 如果需要关闭并且关闭成功, 则返回true。 否则返回false - * @throws Exception 关闭异常 - */ - private boolean closeSession(Session session, String websocketPath) throws Exception{ - EndpointConfig endpointConfig = ClassUtils.getReflectionField(session, "endpointConfig"); - ServerEndpointConfig perEndpointConfig = ClassUtils.getReflectionField(endpointConfig, "perEndpointConfig"); - String path = ClassUtils.getReflectionField(perEndpointConfig, "path"); - if (path.equals(websocketPath)) { - session.close(); - log.info("Closed websocket session {} for path {}", session.getId(), websocketPath); - return true; - } - return false; - } - - /** - * websocket路径解析类,主要用于处理参数 - */ - private static class UriTemplate { - - private final Map paramMap = new ConcurrentHashMap<>(); - private final String path; - - private UriTemplate(String path) throws DeploymentException { - if (path == null || path.length() == 0 || !path.startsWith("/") || path.contains("/../") || path.contains("/./") || path.contains("//")) { - throw new DeploymentException(String.format("The path [%s] is not valid.", path)); - } - StringBuilder normalized = new StringBuilder(path.length()); - Set paramNames = new HashSet<>(); - - // Include empty segments. - String[] segments = path.split("/", -1); - int paramCount = 0; - - for (int i = 0; i < segments.length; i++) { - String segment = segments[i]; - if (segment.length() == 0) { - if (i == 0 || (i == segments.length - 1 && paramCount == 0)) { - // Ignore the first empty segment as the path must always - // start with '/' - // Ending with a '/' is also OK for instances used for - // matches but not for parameterised templates. - continue; - } else { - // As per EG discussion, all other empty segments are - // invalid - throw new DeploymentException(String.format("The path [%s] contains one or more empty segments which is not permitted", path)); - } - } - normalized.append('/'); - if (segment.startsWith("{") && segment.endsWith("}")) { - segment = segment.substring(1, segment.length() - 1); - normalized.append('{'); - normalized.append(paramCount++); - normalized.append('}'); - if (!paramNames.add(segment)) { - throw new DeploymentException(String.format("The parameter [%s] appears more than once in the path which is not permitted", segment)); - } - paramMap.put(segment, paramCount - 1); - } else { - if (segment.contains("{") || segment.contains("}")) { - throw new DeploymentException(String.format("The segment [%s] is not valid in the provided path [%s]", segment, path)); - } - normalized.append(segment); - } - } - this.path = normalized.toString(); - } - - public String getPath() { - return path; - } - - public Map getParamMap() { - return paramMap; - } - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/bean/model/ControllerWrapper.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/bean/model/ControllerWrapper.java deleted file mode 100644 index ffe7f9b7593ede41aa8e8567f38c1cb4e299ec60..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/factory/process/post/bean/model/ControllerWrapper.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.gitee.starblues.factory.process.post.bean.model; - -import org.springframework.web.servlet.mvc.method.RequestMappingInfo; - -import java.util.Set; - -/** - * controller 包装 - * @author starBlues - * @version 2.4.0 - */ -public class ControllerWrapper { - - /** - * controller bean 名称 - */ - private String beanName; - - /** - * controller bean 类型 - */ - private Class beanClass; - - /** - * controller 的 RequestMappingInfo 集合 - */ - private Set requestMappingInfos; - - public Class getBeanClass() { - return beanClass; - } - - public void setBeanClass(Class beanClass) { - this.beanClass = beanClass; - } - - public String getBeanName() { - return beanName; - } - - public void setBeanName(String beanName) { - this.beanName = beanName; - } - - public Set getRequestMappingInfos() { - return requestMappingInfos; - } - - public void setRequestMappingInfos(Set requestMappingInfos) { - this.requestMappingInfos = requestMappingInfos; - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/ConfigurationBuilder.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/ConfigurationBuilder.java deleted file mode 100644 index e1ed2abb0e73c4d2f4b7451a4e737fa5a9f6e8ca..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/ConfigurationBuilder.java +++ /dev/null @@ -1,303 +0,0 @@ -package com.gitee.starblues.integration; - -import org.pf4j.RuntimeMode; -import org.pf4j.util.StringUtils; -import org.springframework.util.ObjectUtils; - -import java.util.List; -import java.util.Objects; -import java.util.Set; - -/** - * 通过构造者进行配置插件初始化配置 - * - * @author starBlues - * @version 2.4.4 - */ -public class ConfigurationBuilder extends DefaultIntegrationConfiguration{ - - private final boolean enable; - - private final RuntimeMode runtimeMode; - private final List pluginPath; - private final String pluginConfigFilePath; - - private final String uploadTempPath; - private final String backupPath; - private final String pluginRestPathPrefix; - - private final Boolean enablePluginIdRestPathPrefix; - - private final Set enablePluginIds; - private final Set disablePluginIds; - private final List sortInitPluginIds; - private final Boolean enableSwaggerRefresh; - - private final String version; - private final Boolean exactVersionAllowed; - - private final Boolean enableWebSocket; - - private final Boolean stopDependents; - - public ConfigurationBuilder(Builder builder) { - this.runtimeMode = Objects.requireNonNull(builder.runtimeMode, "runtimeMode can't be empty"); - if(ObjectUtils.isEmpty(builder.pluginPath)){ - throw new IllegalArgumentException("pluginPath can't be empty"); - } - this.pluginPath = builder.pluginPath; - this.pluginConfigFilePath = Objects.requireNonNull(builder.pluginConfigFilePath, - "pluginConfigFilePath can't be empty"); - this.uploadTempPath = builder.uploadTempPath; - this.backupPath = builder.backupPath; - this.pluginRestPathPrefix = builder.pluginRestPathPrefix; - this.enablePluginIdRestPathPrefix = builder.enablePluginIdRestPathPrefix; - this.enablePluginIds = builder.enablePluginIds; - this.disablePluginIds = builder.disablePluginIds; - this.sortInitPluginIds = builder.sortInitPluginIds; - this.version = builder.version; - this.exactVersionAllowed = builder.exactVersionAllowed; - if(builder.enable == null){ - this.enable = true; - } else { - this.enable = builder.enable; - } - if(builder.enableSwaggerRefresh == null){ - this.enableSwaggerRefresh = true; - } else { - this.enableSwaggerRefresh = builder.enableSwaggerRefresh; - } - if(builder.enableWebSocket == null){ - this.enableWebSocket = false; - } else { - this.enableWebSocket = builder.enableWebSocket; - } - if(builder.stopDependents == null){ - this.stopDependents = false; - } else { - this.stopDependents = builder.stopDependents; - } - } - - public static Builder toBuilder(){ - return new Builder(); - } - - public static class Builder{ - - private Boolean enable; - - private RuntimeMode runtimeMode = RuntimeMode.DEVELOPMENT; - private List pluginPath; - private String pluginConfigFilePath = ""; - - private String uploadTempPath; - private String backupPath; - private String pluginRestPathPrefix; - private Boolean enablePluginIdRestPathPrefix; - - private Set enablePluginIds; - private Set disablePluginIds; - private List sortInitPluginIds; - private Boolean enableSwaggerRefresh; - - private String version; - private Boolean exactVersionAllowed; - - private Boolean enableWebSocket; - - private Boolean stopDependents; - - public Builder runtimeMode(RuntimeMode runtimeMode){ - this.runtimeMode = runtimeMode; - return this; - } - - public Builder enable(Boolean enable){ - this.enable = enable; - return this; - } - - public Builder pluginPath(List pluginPath){ - this.pluginPath = pluginPath; - return this; - } - - public Builder pluginConfigFilePath(String pluginConfigFilePath){ - this.pluginConfigFilePath = pluginConfigFilePath; - return this; - } - - public Builder uploadTempPath(String uploadTempPath){ - this.uploadTempPath = uploadTempPath; - return this; - } - - public Builder backupPath(String backupPath){ - this.backupPath = backupPath; - return this; - } - - public Builder pluginRestPathPrefix(String pluginRestPathPrefix){ - this.pluginRestPathPrefix = pluginRestPathPrefix; - return this; - } - - public Builder enablePluginIdRestPathPrefix(Boolean enablePluginIdRestPathPrefix){ - this.enablePluginIdRestPathPrefix = enablePluginIdRestPathPrefix; - return this; - } - - public Builder enablePluginIds(Set enablePluginIds){ - this.enablePluginIds = enablePluginIds; - return this; - } - - public Builder disablePluginIds(Set disablePluginIds){ - this.disablePluginIds = disablePluginIds; - return this; - } - - public Builder sortInitPluginIds(List sortInitPluginIds){ - this.sortInitPluginIds = sortInitPluginIds; - return this; - } - - public Builder enableSwaggerRefresh(Boolean enableSwaggerRefresh){ - this.enableSwaggerRefresh = enableSwaggerRefresh; - return this; - } - - public Builder version(String version){ - this.version = version; - return this; - } - - public Builder exactVersionAllowed(Boolean exactVersionAllowed){ - this.exactVersionAllowed = exactVersionAllowed; - return this; - } - - public Builder enableWebSocket(Boolean enableWebSocket){ - this.enableWebSocket = enableWebSocket; - return this; - } - - public Builder stopDependents(Boolean stopDependents){ - this.stopDependents = stopDependents; - return this; - } - - public ConfigurationBuilder build(){ - return new ConfigurationBuilder(this); - } - - } - - - @Override - public RuntimeMode environment() { - return runtimeMode; - } - - @Override - public List pluginPath() { - if(ObjectUtils.isEmpty(pluginPath)){ - return super.pluginPath(); - } - return pluginPath; - } - - @Override - public String pluginConfigFilePath() { - return pluginConfigFilePath; - } - - - @Override - public String uploadTempPath() { - if(StringUtils.isNullOrEmpty(uploadTempPath)){ - return super.uploadTempPath(); - } else { - return uploadTempPath; - } - } - - @Override - public String backupPath() { - if(StringUtils.isNullOrEmpty(backupPath)){ - return super.backupPath(); - } else { - return backupPath; - } - } - - @Override - public String pluginRestPathPrefix() { - if(StringUtils.isNullOrEmpty(pluginRestPathPrefix)){ - return super.pluginRestPathPrefix(); - } else { - return pluginRestPathPrefix; - } - } - - @Override - public boolean enablePluginIdRestPathPrefix() { - if(enablePluginIdRestPathPrefix == null){ - return super.enablePluginIdRestPathPrefix(); - } else { - return enablePluginIdRestPathPrefix; - } - } - - @Override - public boolean enable() { - return enable; - } - - @Override - public Set enablePluginIds() { - return enablePluginIds; - } - - @Override - public Set disablePluginIds() { - return disablePluginIds; - } - - @Override - public List sortInitPluginIds() { - return sortInitPluginIds; - } - - @Override - public boolean enableSwaggerRefresh() { - return enableSwaggerRefresh; - } - - @Override - public String version() { - if(StringUtils.isNullOrEmpty(version)){ - return super.version(); - } - return version; - } - - @Override - public boolean exactVersionAllowed() { - if(exactVersionAllowed == null){ - return super.exactVersionAllowed(); - } - return exactVersionAllowed; - } - - @Override - public boolean enableWebSocket() { - return enableWebSocket; - } - - @Override - public boolean stopDependents() { - return stopDependents; - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/PluginListenerContext.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/PluginListenerContext.java deleted file mode 100644 index 95bce6f5f12c736f9aa2fd10c6976ee5e72a9939..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/PluginListenerContext.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.gitee.starblues.integration; - - -import com.gitee.starblues.integration.listener.PluginListener; -import org.pf4j.PluginStateListener; - -import java.util.List; - -/** - * 插件bean监听者上下文。 - * 注意: 监听者必须在初始化插件前添加,否则在初始化阶段可能无法触发添加的监听者。 - * @author starBlues - * @version 2.4.0 - */ -public interface PluginListenerContext { - - /** - * 添加监听者 - * @param pluginListener 插件 bean 监听者 - */ - void addListener(PluginListener pluginListener); - - - /** - * 添加监听者 - * @param pluginListenerClass 插件监听者Class类 - * @param 继承PluginListener的子类 - */ - void addListener(Class pluginListenerClass); - - /** - * 追加多个监听者 - * @param pluginListeners 插件 bean 监听者集合 - */ - void addListener(List pluginListeners); - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/PluginStateListenerContext.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/PluginStateListenerContext.java deleted file mode 100644 index edca121986df86092ab03615618e5919707f6cb7..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/PluginStateListenerContext.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.gitee.starblues.integration; - -import org.pf4j.PluginStateListener; - -import java.util.List; - -/** - * 插件状态监听器上下文 - * @author starBlues - * @version 2.4.4 - */ -public interface PluginStateListenerContext { - - - /** - * 添加pf4j状态监听者 - * @param pluginListener 插件 bean 监听者 - */ - void addPf4jStateListener(PluginStateListener pluginListener); - - - /** - * 添加pf4j状态监听者 - * @param pluginListenerClass 插件监听者Class类 - * @param 继承PluginListener的子类 - */ - void addPf4jStateListener(Class pluginListenerClass); - - /** - * 添加pf4j状态监听者 - * @param pluginListeners 插件 bean 监听者集合 - */ - void addPf4jStateListener(List pluginListeners); - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/application/package-info.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/application/package-info.java deleted file mode 100644 index 262e0b33e58413dbe45d167d1b1a736fad4c5249..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/application/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -/** - * 插件 PluginApplication 的实现包 - * - * @author starBlues - * @version 1.0 - */ -package com.gitee.starblues.integration.application; \ No newline at end of file diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/listener/PluginListener.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/listener/PluginListener.java deleted file mode 100644 index c22c76cb9d3f5dfaa4e055c4c2f216c41e6a9244..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/listener/PluginListener.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.gitee.starblues.integration.listener; - - -/** - * 插件bean监听者 - * - * @author starBlues - * @version 2.4.4 - */ -public interface PluginListener { - - - /** - * 注册插件成功 - * @param pluginId 插件id - * @param isStartInitial 是否随着系统启动时而进行的插件注册 - */ - void registry(String pluginId, boolean isStartInitial); - - /** - * 卸载插件成功 - * @param pluginId 插件id - */ - void unRegistry(String pluginId); - - /** - * 注册错误 - * @param pluginId 插件id - * @param throwable 异常信息 - */ - void registryFailure(String pluginId, Throwable throwable); - - /** - * 注册错误 - * @param pluginId 插件id - * @param throwable 异常信息 - */ - void unRegistryFailure(String pluginId, Throwable throwable); - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/listener/PluginListenerFactory.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/listener/PluginListenerFactory.java deleted file mode 100644 index 3be286382d3e4d8dbfa582c828124543da53f02c..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/listener/PluginListenerFactory.java +++ /dev/null @@ -1,148 +0,0 @@ -package com.gitee.starblues.integration.listener; - -import com.gitee.starblues.utils.SpringBeanUtils; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.context.support.GenericApplicationContext; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * 插件监听工厂 - * - * @author starBlues - * @version 2.4.4 - */ -public class PluginListenerFactory implements PluginListener { - - private final List listeners = new ArrayList<>(); - private final List listenerClasses = new ArrayList<>(); - private boolean isBuildListenerClass = false; - - @Override - public void registry(String pluginId, boolean isInitialize) { - for (PluginListener listener : listeners) { - try { - listener.registry(pluginId, isInitialize); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - @Override - public void unRegistry(String pluginId) { - for (PluginListener listener : listeners) { - try { - listener.unRegistry(pluginId); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - @Override - public void registryFailure(String pluginId, Throwable throwable) { - for (PluginListener listener : listeners) { - try { - listener.registryFailure(pluginId, throwable); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - @Override - public void unRegistryFailure(String pluginId, Throwable throwable) { - for (PluginListener listener : listeners) { - try { - listener.unRegistryFailure(pluginId, throwable); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - - /** - * 添加监听者 - * - * @param pluginListener 插件监听者 - */ - public void addPluginListener(PluginListener pluginListener) { - if (pluginListener != null) { - listeners.add(pluginListener); - } - } - - /** - * 添加监听者 - * - * @param pluginListenerClass 插件监听者Class类 - * @param 插件监听者类。继承 PluginListener - */ - public void addPluginListener(Class pluginListenerClass) { - if (pluginListenerClass != null) { - synchronized (listenerClasses) { - listenerClasses.add(pluginListenerClass); - } - } - } - - public void buildListenerClass(GenericApplicationContext applicationContext) { - if (applicationContext == null) { - return; - } - synchronized (listenerClasses) { - if(isBuildListenerClass){ - return; - } - // 搜索Spring容器中的监听器 - List pluginListeners = SpringBeanUtils.getBeans(applicationContext, PluginListener.class); - if(pluginListeners.isEmpty()){ - pluginListeners = new ArrayList<>(); - } - for (Class listenerClass : listenerClasses) { - // 兼容 spring 4.x - applicationContext.registerBeanDefinition(listenerClass.getName(), - BeanDefinitionBuilder.genericBeanDefinition(listenerClass).getBeanDefinition()); - T bean = applicationContext.getBean(listenerClass); - pluginListeners.add(bean); - } - for (PluginListener pluginListener : pluginListeners) { - boolean find = false; - for (PluginListener listener : listeners) { - if(Objects.equals(listener, pluginListener)){ - find = true; - break; - } - } - // 防止监听器重复注册 - if(!find){ - listeners.add(pluginListener); - } - } - listenerClasses.clear(); - isBuildListenerClass = true; - } - } - - /** - * 得到监听者 - * - * @return 监听者集合 - */ - public List getListeners() { - return listeners; - } - - /** - * 得到监听者class - * - * @return 监听者class集合 - */ - public List getListenerClasses() { - return listenerClasses; - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/listener/PluginStateListenerFactory.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/listener/PluginStateListenerFactory.java deleted file mode 100644 index d041ae486e3bd0d90b3ce666e7be6917e46799c4..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/listener/PluginStateListenerFactory.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.gitee.starblues.integration.listener; - -import com.gitee.starblues.utils.SpringBeanUtils; -import org.pf4j.PluginStateListener; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.context.support.GenericApplicationContext; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * 插件状态监听器工厂 - * @author starBlues - * @version 2.4.4 - * @since 2021-06-09 - */ -public class PluginStateListenerFactory { - - private final List listeners = new ArrayList<>(); - private final List listenerClasses = new ArrayList<>(); - - public void addStateListener(PluginStateListener pluginStateListener){ - if(pluginStateListener == null){ - return; - } - listeners.add(pluginStateListener); - } - - public void addStateListener(Class pluginStateListenerClass){ - if(pluginStateListenerClass == null){ - return; - } - listenerClasses.add(pluginStateListenerClass); - } - - public List buildListenerClass(GenericApplicationContext applicationContext) { - if (applicationContext == null) { - return listeners; - } - synchronized (listenerClasses) { - if(listenerClasses.isEmpty()){ - return listeners; - } - // 搜索Spring容器中的监听器 - List pluginListeners = SpringBeanUtils.getBeans(applicationContext, PluginStateListener.class); - if(pluginListeners.isEmpty()){ - pluginListeners = new ArrayList<>(); - } - for (Class listenerClass : listenerClasses) { - // 兼容 spring 4.x - applicationContext.registerBeanDefinition(listenerClass.getName(), - BeanDefinitionBuilder.genericBeanDefinition(listenerClass).getBeanDefinition()); - T bean = applicationContext.getBean(listenerClass); - pluginListeners.add(bean); - } - for (PluginStateListener pluginListener : pluginListeners) { - boolean find = false; - for (PluginStateListener listener : listeners) { - if(Objects.equals(listener, pluginListener)){ - find = true; - break; - } - } - // 防止监听器重复注册 - if(!find){ - listeners.add(pluginListener); - } - } - listenerClasses.clear(); - } - return listeners; - } - - -} 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 deleted file mode 100644 index 27b174e4405f468a062f9a68c69823badf5cf670..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/DefaultPluginOperator.java +++ /dev/null @@ -1,682 +0,0 @@ -package com.gitee.starblues.integration.operator; - -import com.gitee.starblues.factory.DefaultPluginFactory; -import com.gitee.starblues.factory.PluginFactory; -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.integration.IntegrationConfiguration; -import com.gitee.starblues.integration.listener.PluginInitializerListener; -import com.gitee.starblues.integration.listener.PluginInitializerListenerFactory; -import com.gitee.starblues.integration.listener.PluginListenerFactory; -import com.gitee.starblues.integration.operator.module.PluginInfo; -import com.gitee.starblues.integration.operator.verify.DefaultPluginVerify; -import com.gitee.starblues.integration.operator.verify.PluginLegalVerify; -import com.gitee.starblues.utils.GlobalRegistryInfo; -import com.gitee.starblues.utils.PluginFileUtils; -import com.gitee.starblues.utils.PluginOperatorInfo; -import org.pf4j.*; -import org.pf4j.util.FileUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.util.ObjectUtils; -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.*; -import java.util.stream.Collectors; - -/** - * 默认的插件操作者 - * @author starBlues - * @version 2.4.4 - */ -public class DefaultPluginOperator implements PluginOperator { - - private boolean isInit = false; - protected final Logger log = LoggerFactory.getLogger(this.getClass()); - - private final static DateTimeFormatter FORMAT = DateTimeFormatter.ofPattern("yyyyMMddHHmmss"); - - protected final GenericApplicationContext applicationContext; - protected final IntegrationConfiguration integrationConfiguration; - protected final PluginManager pluginManager; - protected final PluginFactory pluginFactory; - protected final PluginInitializerListenerFactory pluginInitializerListenerFactory; - - protected PluginLegalVerify pluginLegalVerify; - - - public DefaultPluginOperator(ApplicationContext applicationContext, - IntegrationConfiguration integrationConfiguration, - PluginManager pluginManager, - PluginListenerFactory pluginListenerFactory) { - Objects.requireNonNull(integrationConfiguration, "applicationContext can't be null"); - Objects.requireNonNull(integrationConfiguration, "IntegrationConfiguration can't be null"); - Objects.requireNonNull(pluginManager, "PluginManager can't be null"); - this.applicationContext = (GenericApplicationContext) applicationContext; - this.integrationConfiguration = integrationConfiguration; - this.pluginManager = pluginManager; - this.pluginFactory = new DefaultPluginFactory(applicationContext, pluginListenerFactory); - this.pluginInitializerListenerFactory = new PluginInitializerListenerFactory(applicationContext); - - this.pluginLegalVerify = new DefaultPluginVerify(pluginManager); - } - - - - /** - * 设置插件校验器 - * @param uploadPluginVerify uploadPluginVerify - */ - public void setUploadPluginVerify(PluginLegalVerify uploadPluginVerify) { - if(uploadPluginVerify != null){ - this.pluginLegalVerify = uploadPluginVerify; - } - } - - @Override - public synchronized boolean initPlugins(PluginInitializerListener pluginInitializerListener) throws Exception { - if(isInit){ - throw new RuntimeException("Plugins Already initialized. Cannot be initialized again"); - } - try { - pluginInitializerListenerFactory.addPluginInitializerListeners(pluginInitializerListener); - log.info("Plugins start initialize of root path '{}'", pluginManager.getPluginsRoot().toString()); - // 触发插件初始化监听器 - pluginInitializerListenerFactory.before(); - if(!integrationConfiguration.enable()){ - // 如果禁用的话, 直接返回 - pluginInitializerListenerFactory.complete(); - return false; - } - - // 启动前, 清除空文件 - PluginFileUtils.cleanEmptyFile(pluginManager.getPluginsRoot()); - - // 开始初始化插件工厂 - pluginFactory.initialize(); - // 开始加载插件 - pluginManager.loadPlugins(); - pluginManager.startPlugins(); - List pluginWrappers = pluginManager.getStartedPlugins(); - if(pluginWrappers == null || pluginWrappers.isEmpty()){ - log.warn("Not found plugin!"); - pluginInitializerListenerFactory.complete(); - return false; - } - boolean isFoundException = false; - for (PluginWrapper pluginWrapper : pluginWrappers) { - String pluginId = pluginWrapper.getPluginId(); - GlobalRegistryInfo.addOperatorPluginInfo(pluginId, - PluginOperatorInfo.OperatorType.INSTALL, false); - try { - // 依次注册插件信息到Spring boot - pluginFactory.registry(PluginRegistryInfo.build(pluginWrapper, pluginManager, - applicationContext, true)); - } catch (Exception e){ - log.error("Plugin '{}' registry failure. Reason : {}", pluginId, e.getMessage(), e); - isFoundException = true; - } - } - pluginFactory.build(); - isInit = true; - if(isFoundException){ - log.error("Plugins initialize failure"); - pluginInitializerListenerFactory.failure(new Exception("Plugins initialize failure")); - return false; - } else { - log.info("Plugins initialize success"); - pluginInitializerListenerFactory.complete(); - return true; - } - } catch (Exception e){ - pluginInitializerListenerFactory.failure(e); - throw e; - } - } - - @Override - public boolean verify(Path jarPath) throws Exception { - pluginLegalVerify.verify(jarPath); - return true; - } - - - @Override - public synchronized PluginInfo install(Path jarPath) throws Exception { - if(isDev()){ - throw new RuntimeException("Plugin cannot be installed in 'dev' environment"); - } - if(jarPath == null){ - throw new IllegalArgumentException("Method:install param 'pluginId' can not be empty"); - } - String pluginId = null; - try { - PluginInfo pluginInfo = load(jarPath); - if(pluginInfo == null){ - log.error("Plugin '{}' install failure, this pluginInfo id is empty.", pluginId); - return null; - } - PluginDescriptor pluginDescriptor = pluginInfo.getPluginDescriptor(); - if(pluginDescriptor == null){ - log.error("Plugin install failure."); - return null; - } - pluginId = pluginDescriptor.getPluginId(); - GlobalRegistryInfo.addOperatorPluginInfo(pluginId, PluginOperatorInfo.OperatorType.INSTALL, true); - if(start(pluginId)){ - log.info("Plugin '{}' install success", pluginId); - return getPluginInfo(pluginId); - } else { - try { - uninstall(pluginId, false); - } catch (Exception uninstallException){ - log.error("Plugin '{}' uninstall failure. {}", pluginId, uninstallException.getMessage()); - } - return null; - } - } catch (Exception e){ - // 说明load成功, 但是没有启动成功, 则卸载该插件 - log.error("Plugin '{}' install failure. {}", pluginId, e.getMessage()); - if(!ObjectUtils.isEmpty(pluginId)){ - try { - uninstall(pluginId, false); - } catch (Exception uninstallException){ - log.error("Plugin '{}' uninstall failure. {}", pluginId, uninstallException.getMessage()); - } - } - throw e; - } finally { - if(!ObjectUtils.isEmpty(pluginId)){ - GlobalRegistryInfo.setOperatorPluginInfo(pluginId, false); - } - } - } - - @Override - public synchronized boolean uninstall(String pluginId, boolean isBackup) throws Exception { - if(isDev()){ - throw new RuntimeException("Plugin cannot be uninstalled in 'dev' environment"); - } - if(ObjectUtils.isEmpty(pluginId)){ - throw new IllegalArgumentException("Method:uninstall param 'pluginId' can not be empty"); - } - PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId); - if(pluginWrapper == null){ - throw new Exception("Plugin uninstall failure, Not found plugin '" + pluginId + "'"); - } - - Exception exception = null; - if(pluginWrapper.getPluginState() == PluginState.STARTED){ - try { - pluginFactory.unRegistry(pluginId); - pluginFactory.build(); - } catch (Exception e){ - log.error("Plugin '{}' uninstall failure, {}", pluginId, e.getMessage()); - exception = e; - } - } - - try { - if (pluginManager.unloadPlugin(pluginId)) { - Path pluginPath = pluginWrapper.getPluginPath(); - boolean opPluginFile; - if(isBackup){ - // 将插件文件移到备份文件中 - opPluginFile = backup(pluginPath, "uninstall", 1); - } else { - // 不备份的话。直接删除该文件 - opPluginFile = Files.deleteIfExists(pluginPath); - } - if(opPluginFile){ - log.info("Plugin '{}' uninstall success", pluginId); - } else { - log.error("Plugin '{}' uninstall failure. process plugin file failure", pluginId); - } - return opPluginFile; - } else { - log.error("Plugin '{}' uninstall failure", pluginId); - return false; - } - } catch (Exception e){ - if(exception != null){ - exception.printStackTrace(); - } - log.error("Plugin '{}' uninstall failure. {}", pluginId, e.getMessage()); - throw e; - } - } - - @Override - public PluginInfo load(Path jarPath) throws Exception { - if(!Files.exists(jarPath)){ - throw new FileNotFoundException("Not found this path " + jarPath); - } - // 校验插件文件 - pluginLegalVerify.verify(jarPath); - Path pluginsRoot = pluginManager.getPluginsRoot(); - Path pluginPath = null; - if(jarPath.getParent().compareTo(pluginsRoot) == 0){ - // 说明该插件文件存在于插件root目录下。直接加载该插件 - pluginPath = jarPath; - } else { - File sourceFile = jarPath.toFile(); - String targetPathString = pluginsRoot.toString() + File.separator + - sourceFile.getName(); - Path targetPath = Paths.get(targetPathString); - if(Files.exists(targetPath)){ - // 如果存在该文件, 则移动备份 - backup(targetPath, "install-backup", 1); - } - PluginFileUtils.createExistFile(targetPath); - Files.copy(jarPath, targetPath, StandardCopyOption.REPLACE_EXISTING); - pluginPath = targetPath; - } - String pluginId = null; - try { - pluginId = pluginManager.loadPlugin(pluginPath); - if(pluginId != null){ - return getPluginInfo(pluginId); - } - return null; - } catch (Exception e){ - if(pluginId != null){ - log.error("Load plugin success, but get pluginInfo failure. so remove plugin '{}'", pluginId); - try { - pluginManager.unloadPlugin(pluginId); - } finally { - Files.delete(pluginPath); - } - } else { - log.error("Load plugin failure"); - } - return null; - } - } - - @Override - public PluginInfo load(MultipartFile pluginFile) throws Exception { - if(isDev()){ - throw new RuntimeException("Plugin cannot uploadPluginAndStart in the 'dev' environment"); - } - if(pluginFile == null){ - throw new IllegalArgumentException("Method:uploadPluginAndStart param 'pluginFile' can not be null"); - } - Path jarPath = uploadPlugin(pluginFile); - return load(jarPath); - } - - @Override - public boolean unload(String pluginId, boolean isBackup) throws Exception { - PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId); - if(pluginWrapper == null){ - throw new Exception("Plugin unload failure, Not found plugin '" + pluginId + "'"); - } - pluginManager.unloadPlugin(pluginId); - if(isBackup){ - backup(pluginWrapper.getPluginPath(), "unload", 1); - } - return true; - } - - @Override - public synchronized boolean start(String pluginId) throws Exception { - if(ObjectUtils.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 Exception("This plugin '" + pluginId + "' have already started"); - } - try { - PluginState pluginState = pluginManager.startPlugin(pluginId); - if(pluginState == PluginState.STARTED){ - GlobalRegistryInfo.addOperatorPluginInfo(pluginId, PluginOperatorInfo.OperatorType.START, false); - pluginFactory.registry(PluginRegistryInfo.build(pluginWrapper, pluginManager, - applicationContext,false)); - pluginFactory.build(); - log.info("Plugin '{}' start success", pluginId); - return true; - } - log.error("Plugin '{}' start failure, plugin state is not start. Current plugin state is '{}'", - pluginId, pluginState.toString()); - - } catch (Exception e){ - log.error("Plugin '{}' start failure. {}", pluginId, e.getMessage(), e); - log.info("Start stop plugin '{}'", pluginId); - try { - stop(pluginId); - } catch (Exception stopException){ - log.error("Plugin '{}' stop failure. {}", pluginId, e.getMessage()); - } - } - return false; - } - - @Override - public synchronized boolean stop(String pluginId) throws Exception { - if(ObjectUtils.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 Exception("This plugin '" + pluginId + "' is not started"); - } - try { - pluginFactory.unRegistry(pluginId); - pluginFactory.build(); - log.info("Plugin '{}' unRegistry success", pluginId); - } catch (Exception e){ - log.error("Plugin '{}' stop failure. {}", pluginId, e.getMessage(), e); - } - try { - pluginManager.stopPlugin(pluginId); - log.info("Plugin '{}' stop success", pluginId); - return true; - } catch (Exception e){ - log.error("Plugin '{}' stop failure. {}", pluginId, e.getMessage()); - throw e; - } - } - - - @Override - public synchronized PluginInfo uploadPluginAndStart(MultipartFile pluginFile) throws Exception { - if(isDev()){ - throw new RuntimeException("Plugin cannot uploadPluginAndStart in the 'dev' environment"); - } - if(pluginFile == null){ - throw new IllegalArgumentException("Method:uploadPluginAndStart param 'pluginFile' can not be null"); - } - Path path = uploadPlugin(pluginFile); - return this.install(path); - } - - @Override - public synchronized boolean installConfigFile(Path configFilePath) throws Exception { - if(isDev()){ - throw new RuntimeException("Plugin config file cannot be installed in the 'dev' environment"); - } - if(!Files.exists(configFilePath)){ - throw new FileNotFoundException("configFilePath '" + configFilePath + "' does not exist!"); - } - File sourceFile = configFilePath.toFile(); - String configPath = integrationConfiguration.pluginConfigFilePath() + - File.separator + sourceFile.getName(); - Path targetPath = PluginFileUtils.createExistFile(Paths.get(configPath)); - if(Files.exists(targetPath)){ - // 如果文件存在, 则移动备份 - backup(targetPath, "install-config-backup",1); - } - Files.copy(configFilePath, targetPath, StandardCopyOption.REPLACE_EXISTING); - return true; - } - - @Override - public synchronized boolean uploadConfigFile(MultipartFile configFile) throws Exception { - if(isDev()){ - throw new RuntimeException("Plugin config file cannot be uploaded in the 'dev' environment"); - } - if(configFile == null){ - throw new IllegalArgumentException("Method:uploadConfigFile param 'configFile' can not be null"); - } - String fileName = configFile.getOriginalFilename(); - String configPath = integrationConfiguration.pluginConfigFilePath() + - File.separator + fileName; - Path targetPath = PluginFileUtils.createExistFile(Paths.get(configPath)); - if(Files.exists(targetPath)){ - // 如果文件存在, 则拷贝备份 - backup(targetPath, "upload-config-backup",2); - } - // 然后写入数据到该文件 - Files.write(targetPath, configFile.getBytes()); - return true; - } - - @Override - public synchronized boolean backupPlugin(Path backDirPath, String sign) throws Exception { - if(isDev()){ - throw new RuntimeException("Plugin cannot backup in the 'dev' environment"); - } - Objects.requireNonNull(backDirPath); - return backup(backDirPath, sign, 2); - } - - - @Override - public synchronized boolean backupPlugin(String pluginId, String sign) throws Exception { - if(isDev()){ - throw new RuntimeException("Plugin cannot backup in the 'dev' environment"); - } - PluginWrapper pluginManager = getPluginWrapper(pluginId, "BackupPlugin by pluginId"); - return backupPlugin(pluginManager.getPluginPath(), sign); - } - - @Override - public List getPluginInfo() { - List startedPlugins = pluginManager.getPlugins(); - List pluginInfos = new ArrayList<>(); - if(startedPlugins == null){ - return pluginInfos; - } - return startedPlugins.stream() - .filter(pluginWrapper -> pluginWrapper != null) - .map(pw -> { - return getPluginInfo(pw); - }) - .collect(Collectors.toList()); - } - - @Override - public PluginInfo getPluginInfo(String pluginId) { - PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId); - if(pluginWrapper == null){ - log.warn("Not found plugin '{}'", pluginId); - return null; - } - return getPluginInfo(pluginWrapper); - } - - @Override - public Set getPluginFilePaths() throws Exception { - RuntimeMode environment = integrationConfiguration.environment(); - if(environment == RuntimeMode.DEVELOPMENT){ - return new HashSet<>(integrationConfiguration.pluginPath()); - } - List pluginPath = integrationConfiguration.pluginPath(); - Set pathString = new HashSet<>(pluginPath.size()); - for (String path : pluginPath) { - if(ObjectUtils.isEmpty(path)){ - continue; - } - List jars = FileUtils.getJars(Paths.get(path)); - for (File jar : jars) { - pathString.add(jar.getAbsolutePath()); - } - } - return pathString; - } - - @Override - public List getPluginWrapper() { - return pluginManager.getPlugins(); - } - - @Override - public PluginWrapper getPluginWrapper(String pluginId) { - return pluginManager.getPlugin(pluginId); - } - - /** - * 上传插件 - * @param pluginFile 插件文件 - * @return 返回上传的插件路径 - * @throws Exception 异常信息 - */ - protected 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 tempPathString = integrationConfiguration.uploadTempPath() + File.separator + fileName; - Path tempPath = PluginFileUtils.createExistFile(Paths.get(tempPathString)); - Files.write(tempPath, pluginFile.getBytes()); - try { - Path verifyPath = pluginLegalVerify.verify(tempPath); - if(verifyPath != null){ - String targetPathString = pluginManager.getPluginsRoot().toString() + - File.separator + fileName; - Path targetPluginPath = Paths.get(targetPathString); - if(Files.exists(targetPluginPath)){ - // 存在则拷贝一份 - backup(targetPluginPath, "upload", 2); - } - // 拷贝校验的路径到插件路径下 - Files.copy(verifyPath, targetPluginPath, StandardCopyOption.REPLACE_EXISTING); - // 删除临时文件 - Files.deleteIfExists(tempPath); - return targetPluginPath; - } else { - Exception exception = - new Exception(fileName + " verify failure, verifyPath is null"); - verifyFailureDelete(tempPath, exception); - throw exception; - } - } catch (Exception e){ - // 出现异常, 删除刚才上传的临时文件 - verifyFailureDelete(tempPath, e); - throw e; - } - } - - - /** - * 得到插件包装类 - * @param pluginId 插件id - * @param errorMsg 错误信息 - * @return PluginWrapper - * @throws Exception 插件装配异常 - */ - protected PluginWrapper getPluginWrapper(String pluginId, String errorMsg) throws Exception { - PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId); - if (pluginWrapper == null) { - throw new Exception(errorMsg + " -> Not found plugin " + pluginId); - } - return pluginWrapper; - } - - - - /** - * 校验文件失败后, 删除临时文件 - * @param tempPluginFile 临时文件路径 - * @param e 异常信息 - * @throws Exception Exception - */ - protected void verifyFailureDelete(Path tempPluginFile, Exception e) throws Exception { - try { - Files.deleteIfExists(tempPluginFile); - }catch (IOException e1){ - throw new Exception("Verify failure and delete temp file failure : " + e.getMessage(), e); - } - } - - /** - * 备份 - * @param sourcePath 源文件的路径 - * @param sign 文件标志 - * @param type 类型 1移动 2拷贝 - * @return 结果 - */ - protected boolean backup(Path sourcePath, String sign, int type) { - try { - if(isDev()){ - // 如果是开发环境, 则不进行备份 - return false; - } - if(sourcePath == null){ - return false; - } - if(!Files.exists(sourcePath)){ - log.error("Path '{}' does not exist", sourcePath.toString()); - return false; - } - String fileName = sourcePath.getFileName().toString(); - String targetName = integrationConfiguration.backupPath() + File.separator; - if(!ObjectUtils.isEmpty(sign)){ - targetName = targetName + sign; - } - targetName = targetName + "_" +getNowTimeByFormat(); - Path target = Paths.get(targetName + "_" + fileName); - if(!Files.exists(target.getParent())){ - Files.createDirectories(target.getParent()); - } - File sourceFile = sourcePath.toFile(); - if(sourceFile.length() == 0){ - // 源文件字节为0, 说明为删除的插件。不需要备份 - return true; - } - if(type == 1){ - // 是移动的话, 则删除源文件 - Files.move(sourcePath, target, StandardCopyOption.REPLACE_EXISTING); - } else { - // 拷贝 - Files.copy(sourcePath, target, StandardCopyOption.REPLACE_EXISTING); - } - return true; - } catch (IOException e) { - log.error("Backup plugin jar '{}' failure. {}", sourcePath.toString(), e.getMessage(), e); - return false; - } - } - - /** - * 获取现在的时间 - * @return String - */ - protected String getNowTimeByFormat(){ - LocalDateTime localDateTime = LocalDateTime.now(); - return FORMAT.format(localDateTime); - } - - /** - * 是否是开发环境 - * @return bolean - */ - protected boolean isDev(){ - return integrationConfiguration.environment() == RuntimeMode.DEVELOPMENT; - } - - /** - * 通过PluginWrapper得到插件信息 - * @param pluginWrapper pluginWrapper - * @return PluginInfo - */ - private PluginInfo getPluginInfo(PluginWrapper pluginWrapper) { - return new PluginInfo(pluginWrapper.getDescriptor(), pluginWrapper.getPluginState(), - pluginWrapper.getPluginPath().toAbsolutePath().toString(), - pluginManager.getRuntimeMode().toString()); - } - - - -} 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 deleted file mode 100644 index d9d3a68fa4720366e3ca9ba75da105b43f38f7fe..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/PluginOperator.java +++ /dev/null @@ -1,177 +0,0 @@ -package com.gitee.starblues.integration.operator; - -import com.gitee.starblues.integration.listener.PluginInitializerListener; -import com.gitee.starblues.integration.operator.module.PluginInfo; -import org.pf4j.PluginWrapper; -import org.springframework.web.multipart.MultipartFile; - -import java.nio.file.Path; -import java.util.List; -import java.util.Set; - -/** - * 操作插件的接口 - * @author starBlues - * @version 2.4.4 - * @see DefaultPluginOperator - */ -public interface PluginOperator { - - /** - * 初始化插件。该方法只能执行一次。 - * @param pluginInitializerListener 插件初始化监听者 - * @return 成功: 返回true; 失败: 抛出异常或者返回false - * @throws Exception 异常信息 - */ - boolean initPlugins(PluginInitializerListener pluginInitializerListener) throws Exception; - - /** - * 校验插件jar包 - * @param jarPath 插件包的路径 - * @return 成功: 返回true; 失败: 抛出异常或者返回false - * @throws Exception 校验异常 - */ - boolean verify(Path jarPath) throws Exception; - - /** - * 通过路径安装插件(会启用), 该插件文件必须存在于服务器 [适用于生产环境] - * 如果在插件目录存在同名的插件包, 系统会自动备份该插件包。备份文件命名规则为;[install-backup][时间]_原jar名.jar - * @param jarPath 插件路径 - * @return 成功: 返回插件信息PluginInfo; 失败: 抛出异常或者返回null - * @throws Exception 异常信息 - */ - PluginInfo install(Path jarPath) throws Exception; - - /** - * 卸载插件 [适用于生产环境] - * @param pluginId 插件id - * @param isBackup 是否备份原来的插件。备份文件命名规则为;[uninstall][时间]_原jar名.jar - * @return 成功: 返回true; 失败: 抛出异常或者返回false - * @throws Exception 异常信息 - */ - boolean uninstall(String pluginId, boolean isBackup) throws Exception; - - /** - * 加载插件, 但不启动 [适用于生产环境] - * @param jarPath 插件路径 - * @return 成功: 返回插件信息PluginInfo; 失败: 抛出异常或者返回null - * @throws Exception 异常信息 - */ - PluginInfo load(Path jarPath) throws Exception; - - /** - * 加载插件, 但不启动 [适用于生产环境] - * @param pluginFile 插件文件 - * @return 成功: 返回插件信息PluginInfo; 失败: 抛出异常或者返回null - * @throws Exception 异常信息 - */ - PluginInfo load(MultipartFile pluginFile) throws Exception; - - /** - * 配合load使用. 针对load的插件进行unload [适用于生产环境] - * @param pluginId 插件id - * @param isBackup 是否备份原来的插件。备份文件命名规则为;[uninstall][时间]_原jar名.jar - * @return 成功返回true.不成功抛出异常或者返回false - * @throws Exception 异常信息 - */ - boolean unload(String pluginId, boolean isBackup) throws Exception; - - /** - * 启用插件 [适用于生产环境、开发环境] - * @param pluginId 插件id - * @return 成功返回true.不成功抛出异常或者返回false - * @throws Exception 异常信息 - */ - boolean start(String pluginId) throws Exception; - - - /** - * 停止插件 [适用于生产环境、开发环境] - * @param pluginId 插件id - * @return 成功: 返回true; 失败: 抛出异常或者返回false - * @throws Exception 异常信息 - */ - boolean stop(String pluginId) throws Exception; - - - /** - * 上传插件并启用插件。[适用于生产环境] - * 如果在插件目录存在同名的插件包, 系统会自动备份该插件包。备份文件命名规则为;[install-backup][时间]_原jar名.jar - * @param pluginFile 插件文件 - * @return 成功: 返回插件信息PluginInfo; 失败: 抛出异常或者返回null - * @throws Exception 异常信息 - */ - PluginInfo uploadPluginAndStart(MultipartFile pluginFile) throws Exception; - - /** - * 通过路径安装插件的配置文件。该文件必须存在于服务器。[适用于生产环境] - * 如果配置文件目录存在同名的配置文件, 系统会自动备份该配置文件。备份文件命名规则为;[install-config-backup][时间]_原jar名.jar - * @param configFilePath 配置文件路径。 - * @return 成功: 返回true; 失败: 抛出异常或者返回false - * @throws Exception 安装异常 - */ - boolean installConfigFile(Path configFilePath) throws Exception; - - - /** - * 上传配置文件。[适用于生产环境] - * 如果配置文件目录存在同名的配置文件, 系统会自动备份该配置文件。备份文件命名规则为;[upload-config-backup][时间]_原jar名.jar - * @param configFile 配置文件 - * @return 成功: 返回true; 失败: 抛出异常或者返回false - * @throws Exception 异常信息 - */ - boolean uploadConfigFile(MultipartFile configFile) throws Exception; - - /** - * 通过路径备份文件。可备份插件和插件的配置文件。[适用于生产环境] - * @param backDirPath 备份的目录路径 - * @param sign 备份文件的自定义标识 - * @return 成功: 返回true; 失败: 抛出异常或者返回false - * @throws Exception 异常信息 - */ - boolean backupPlugin(Path backDirPath, String sign) throws Exception; - - /** - * 通过插件id备份插件。[适用于生产环境] - * @param pluginId 插件id - * @param sign 备份文件的自定义标识 - * @return 成功: 返回true; 失败: 抛出异常或者返回false - * @throws Exception 异常信息 - */ - boolean backupPlugin(String pluginId, String sign) throws Exception; - - /** - * 获取插件信息 [适用于生产环境、开发环境] - * @return 返回插件信息列表 - */ - List getPluginInfo(); - - /** - * 根据插件id获取插件信息 [适用于生产环境、开发环境] - * @param pluginId 插件id - * @return 插件信息 - */ - PluginInfo getPluginInfo(String pluginId); - - /** - * 得到插件文件的路径 [适用于生产环境] - * @return 返回插件路径列表 - * @throws Exception 异常信息 - */ - Set getPluginFilePaths() throws Exception; - - /** - * 得到所有插件的包装类 [适用于生产环境、开发环境] - * @return 返回插件包装类集合 - */ - List getPluginWrapper(); - - /** - * 通过插件id得到插件的包装类 [适用于生产环境、开发环境] - * @param pluginId 插件id - * @return 返回插件包装类集合 - */ - PluginWrapper getPluginWrapper(String pluginId); - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/module/PluginInfo.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/module/PluginInfo.java deleted file mode 100644 index abc527ad541b3f865d342aff440d393a875d6398..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/module/PluginInfo.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.gitee.starblues.integration.operator.module; - -import org.pf4j.PluginDescriptor; -import org.pf4j.PluginState; - - -/** - * 插件信息 - * @author starBlues - * @version 1.0 - */ -public class PluginInfo { - - /** - * 插件基本信息 - */ - private PluginDescriptor pluginDescriptor; - - /** - * 插件状态 - */ - private PluginState pluginState; - - /** - * 插件路径 - */ - private String path; - - /** - * 运行模式 - */ - private String runMode; - - - public PluginInfo(PluginDescriptor pluginDescriptor, - PluginState pluginState, - String path, - String runMode) { - this.pluginDescriptor = pluginDescriptor; - this.pluginState = pluginState; - this.path = path; - this.runMode = runMode; - } - - public PluginDescriptor getPluginDescriptor() { - return pluginDescriptor; - } - - public PluginState getPluginState() { - return pluginState; - } - - public String getPluginStateString() { - return pluginState.toString(); - } - - public String getPath() { - return path; - } - - public String getRunMode() { - return runMode; - } - - @Override - public String toString() { - return "PluginInfo{" + - "pluginDescriptor=" + pluginDescriptor + - ", pluginState=" + pluginState + - ", path='" + path + '\'' + - ", runMode='" + runMode + '\'' + - '}'; - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/verify/DefaultPluginVerify.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/verify/DefaultPluginVerify.java deleted file mode 100644 index fa8692b6a9df8f0dfcd42702ad54738253ee03f9..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/verify/DefaultPluginVerify.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.gitee.starblues.integration.operator.verify; - -import com.gitee.starblues.integration.pf4j.DefaultPf4jFactory; -import org.pf4j.*; - -import java.nio.file.Path; -import java.util.Objects; - -/** - * 默认的插件校验器 - * @author starBlues - * @version 2.2.2 - */ -public class DefaultPluginVerify extends PluginLegalVerify{ - - private final PluginManager pluginManager; - - public DefaultPluginVerify(PluginManager pluginManager) { - super(DefaultPf4jFactory.getPluginDescriptorFinder(pluginManager.getRuntimeMode())); - Objects.requireNonNull(pluginManager); - this.pluginManager = pluginManager; - } - - - @Override - protected Path postVerify(Path path, PluginDescriptor pluginDescriptor) throws Exception { - PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginDescriptor.getPluginId()); - if(pluginWrapper == null){ - // 当前没有该插件包运行 - return path; - } - // 如果当前插件在当前环境存在, 则抛出异常 - PluginDescriptor runPluginDescriptor = pluginWrapper.getDescriptor(); - StringBuffer errorMsg = new StringBuffer("The plugin (") - .append("id:<").append(runPluginDescriptor.getPluginId()) - .append("> ; version <").append(runPluginDescriptor.getVersion()) - .append("> ) is already exist in the current environment。 ") - .append("Please uninstall the plugin, then upload and update the plugin"); - throw new Exception(errorMsg.toString()); - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/verify/PluginLegalVerify.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/verify/PluginLegalVerify.java deleted file mode 100644 index f659b22259b2d2d9c60b6eedfeaeb73354331a73..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/verify/PluginLegalVerify.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.gitee.starblues.integration.operator.verify; - -import org.pf4j.*; -import org.springframework.util.StringUtils; - -import java.nio.file.Path; -import java.util.Objects; - -/** - * 插件包合法校验 - * @author starBlues - * @version 1.0 - */ -public class PluginLegalVerify implements PluginVerify{ - - protected final PluginDescriptorFinder pluginDescriptorFinder; - - public PluginLegalVerify(PluginDescriptorFinder pluginDescriptorFinder) { - Objects.requireNonNull(pluginDescriptorFinder); - this.pluginDescriptorFinder = pluginDescriptorFinder; - } - - - @Override - public Path verify(Path path) throws Exception { - if(path == null){ - throw new IllegalArgumentException("path can not be null"); - } - if(!pluginDescriptorFinder.isApplicable(path)){ - // 插件包不合法 - throw new Exception(path.toString() + " : plugin illegal"); - } - PluginDescriptor pluginDescriptor = pluginDescriptorFinder.find(path); - if(pluginDescriptor == null){ - throw new Exception(path.toString() + " : Not found plugin Descriptor"); - } - if(StringUtils.isEmpty(pluginDescriptor.getPluginId())){ - throw new Exception(path.toString() + " : Plugin id can't be empty"); - } - if(StringUtils.isEmpty(pluginDescriptor.getPluginClass())){ - throw new Exception(path.toString() + " : Not found plugin Class"); - } - return postVerify(path, pluginDescriptor); - } - - /** - * 合法后的校验.可扩展校验 - * @param path 路径 - * @param pluginDescriptor 插件解析者 - * @return 返回路径 - * @throws Exception 插件异常 - */ - protected Path postVerify(Path path, PluginDescriptor pluginDescriptor) throws Exception{ - return path; - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/verify/PluginVerify.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/verify/PluginVerify.java deleted file mode 100644 index d15c418fd2e00b6a614cdfe322f6a79c7a93b529..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/operator/verify/PluginVerify.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.gitee.starblues.integration.operator.verify; - - -import java.nio.file.Path; - -/** - * 插件合法校验接口 - * @author starBlues - * @version 1.0 - * @see DefaultPluginVerify - * @see PluginLegalVerify - */ -public interface PluginVerify { - - /** - * 校验插件包 - * @param path 插件路径 - * @return 返回校验成功的路径 - * @throws Exception 插件异常 - */ - Path verify(Path path) throws Exception; - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/ConfigPluginStatusProvider.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/ConfigPluginStatusProvider.java deleted file mode 100644 index d669f058e7362b2ef61779e69e0c00088e665969..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/ConfigPluginStatusProvider.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.gitee.starblues.integration.pf4j; - -import org.pf4j.PluginStatusProvider; - -import java.util.HashSet; -import java.util.Set; - -/** - * @author starBlues - * @version 2.4.0 - */ -public class ConfigPluginStatusProvider implements PluginStatusProvider { - - private Set enablePluginIds = new HashSet<>(); - private Set disabledPlugins = new HashSet<>(); - - public ConfigPluginStatusProvider() { - this(null, null); - } - - public ConfigPluginStatusProvider(Set enablePluginIds, - Set disabledPluginIds) { - if(enablePluginIds != null && !enablePluginIds.isEmpty()){ - this.enablePluginIds.addAll(enablePluginIds); - } - if(disabledPluginIds != null && !disabledPluginIds.isEmpty()){ - this.disabledPlugins.addAll(disabledPluginIds); - } - } - - - @Override - public boolean isPluginDisabled(String pluginId) { - if(disabledPlugins.contains("*")){ - return true; - } - if (disabledPlugins.contains(pluginId)) { - return true; - } - - return !enablePluginIds.isEmpty() && !enablePluginIds.contains(pluginId); - } - - @Override - public void disablePlugin(String pluginId) { - if (isPluginDisabled(pluginId)) { - return; - } - disabledPlugins.add(pluginId); - enablePluginIds.remove(pluginId); - } - - @Override - public void enablePlugin(String pluginId) { - if (!isPluginDisabled(pluginId)) { - return; - } - disabledPlugins.remove(pluginId); - enablePluginIds.add(pluginId); - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/DefaultPf4jFactory.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/DefaultPf4jFactory.java deleted file mode 100644 index b74365e0560cabae2b6bf112a2568ba967fef7c5..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/DefaultPf4jFactory.java +++ /dev/null @@ -1,142 +0,0 @@ -package com.gitee.starblues.integration.pf4j; - -import com.gitee.starblues.integration.IntegrationConfiguration; -import com.gitee.starblues.integration.pf4j.descriptor.ManifestPluginDescriptorFinderExtend; -import com.gitee.starblues.integration.pf4j.descriptor.ResolvePropertiesPluginDescriptorFinder; -import com.gitee.starblues.integration.pf4j.descriptor.ResourcesPluginDescriptorFinder; -import org.pf4j.*; - -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; -import java.util.stream.Collectors; - -/** - * 默认的插件集成工厂 - * @author starBlues - * @version 2.4.0 - */ -public class DefaultPf4jFactory implements Pf4jFactory { - - private final IntegrationConfiguration configuration; - - public DefaultPf4jFactory(IntegrationConfiguration configuration) { - this.configuration = configuration; - } - - - @Override - public PluginManager getPluginManager() { - if(configuration == null){ - throw new NullPointerException("IntegrationConfiguration is null"); - } - RuntimeMode environment = configuration.environment(); - if(environment == null){ - throw new RuntimeException("Configuration RuntimeMode is null" + configuration.environment()); - } - List sortInitPluginIds = configuration.sortInitPluginIds(); - DefaultPluginManager defaultPluginManager = null; - - List pluginDir = configuration.pluginPath(); - List pluginDirPath = pluginDir.stream().map(Paths::get).collect(Collectors.toList()); - - if(RuntimeMode.DEVELOPMENT == environment){ - // 开发环境下的插件管理者 - defaultPluginManager = new DefaultPluginManager(pluginDirPath){ - - @Override - protected void initialize() { - super.initialize(); - dependencyResolver = new SortDependencyResolver(sortInitPluginIds, versionManager); - } - - @Override - public RuntimeMode getRuntimeMode() { - System.setProperty("pf4j.mode", RuntimeMode.DEVELOPMENT.toString()); - return RuntimeMode.DEVELOPMENT; - } - - @Override - protected PluginDescriptorFinder createPluginDescriptorFinder() { - return DefaultPf4jFactory.getPluginDescriptorFinder(RuntimeMode.DEVELOPMENT); - } - - @Override - protected PluginLoader createPluginLoader() { - return new CompoundPluginLoader() - .add(new DevelopmentPluginLoader(this), this::isDevelopment); - } - - @Override - protected PluginStatusProvider createPluginStatusProvider() { - return new ConfigPluginStatusProvider( - configuration.enablePluginIds(), - configuration.disablePluginIds()); - } - - @Override - public PluginState stopPlugin(String pluginId) { - return stopPlugin(pluginId, configuration.stopDependents()); - } - }; - } else if(RuntimeMode.DEPLOYMENT == environment){ - // 运行环境下的插件管理者 - defaultPluginManager = new DefaultPluginManager(pluginDirPath){ - - @Override - protected void initialize() { - super.initialize(); - dependencyResolver = new SortDependencyResolver(sortInitPluginIds, versionManager); - } - - @Override - protected PluginDescriptorFinder createPluginDescriptorFinder() { - return DefaultPf4jFactory.getPluginDescriptorFinder(RuntimeMode.DEPLOYMENT); - } - - @Override - protected PluginStatusProvider createPluginStatusProvider() { - return new ConfigPluginStatusProvider( - configuration.enablePluginIds(), - configuration.disablePluginIds()); - } - - @Override - protected PluginLoader createPluginLoader() { - return new CompoundPluginLoader() - .add(new JarPluginLoader(this), this::isNotDevelopment) - .add(new DefaultPluginLoader(this), this::isNotDevelopment); - } - - @Override - public PluginState stopPlugin(String pluginId) { - return stopPlugin(pluginId, configuration.stopDependents()); - } - - }; - } - if(defaultPluginManager == null){ - throw new RuntimeException("Not found run environment " + configuration.environment()); - } - defaultPluginManager.setSystemVersion(configuration.version()); - defaultPluginManager.setExactVersionAllowed(configuration.exactVersionAllowed()); - return defaultPluginManager; - } - - - public static PluginDescriptorFinder getPluginDescriptorFinder(RuntimeMode runtimeMode){ - if(runtimeMode == RuntimeMode.DEPLOYMENT){ - // 生产 - return new CompoundPluginDescriptorFinder() - .add(new ResourcesPluginDescriptorFinder(runtimeMode)) - .add(new ManifestPluginDescriptorFinderExtend()); - } else { - // 开发 - return new CompoundPluginDescriptorFinder() - .add(new ResourcesPluginDescriptorFinder(runtimeMode)) - .add(new ResolvePropertiesPluginDescriptorFinder()) - .add(new ManifestPluginDescriptorFinderExtend()); - } - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/JarPluginLoader.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/JarPluginLoader.java deleted file mode 100644 index c1d31858233952628606b940ec14004bf2132cc2..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/JarPluginLoader.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.gitee.starblues.integration.pf4j; - -import org.pf4j.*; -import org.pf4j.util.FileUtils; - -import java.nio.file.Files; -import java.nio.file.Path; - -/** - * 生成环境下jar包的加载器 - * @author starBlues - * @version 2.4.0 - */ -public class JarPluginLoader implements PluginLoader { - - protected PluginManager pluginManager; - - public JarPluginLoader(PluginManager pluginManager) { - this.pluginManager = pluginManager; - } - - @Override - public boolean isApplicable(Path pluginPath) { - return Files.exists(pluginPath) && FileUtils.isJarFile(pluginPath); - } - - @Override - public ClassLoader loadPlugin(Path pluginPath, PluginDescriptor pluginDescriptor) { - PluginClassLoader pluginClassLoader = - new PluginClassLoader(pluginManager, pluginDescriptor, - this.getClass().getClassLoader(), - ClassLoadingStrategy.APD); - pluginClassLoader.addFile(pluginPath.toFile()); - return pluginClassLoader; - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/Pf4jFactory.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/Pf4jFactory.java deleted file mode 100644 index f36903cbfca6660f58e23e40597661e169e2ccdd..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/Pf4jFactory.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.gitee.starblues.integration.pf4j; - -import com.gitee.starblues.integration.application.DefaultPluginApplication; -import org.pf4j.PluginManager; - -/** - * Pf4j 集成工厂。获取Pf4j的PluginManager对象 - * @author starBlues - * @version 2.2.0 - * @see DefaultPluginApplication - */ -public interface Pf4jFactory { - - /** - * 得到插件管理者 - * @return 插件管理者 - */ - PluginManager getPluginManager(); - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/SortDependencyResolver.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/SortDependencyResolver.java deleted file mode 100644 index 33d9bf28709c4f178f4999c810a88c79bff4b088..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/SortDependencyResolver.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.gitee.starblues.integration.pf4j; - -import org.pf4j.DependencyResolver; -import org.pf4j.PluginDescriptor; -import org.pf4j.VersionManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.ReflectionUtils; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Objects; - -/** - * 控制插件启动顺序 - * @author starBlues - * @version 2.4.0 - */ -public class SortDependencyResolver extends DependencyResolver { - - private final static Logger LOGGER = LoggerFactory.getLogger(SortDependencyResolver.class); - - private final List sortInitPluginIds; - - public SortDependencyResolver(List sortInitPluginIds, VersionManager versionManager) { - super(versionManager); - this.sortInitPluginIds = sortInitPluginIds; - } - - @Override - public Result resolve(List plugins) { - Result resolve = super.resolve(plugins); - if(sortInitPluginIds == null || sortInitPluginIds.isEmpty()){ - return resolve; - } - List sortedPlugins = resolve.getSortedPlugins(); - List newSortPluginIds = new ArrayList<>(sortedPlugins.size()); - for (String sortPluginId : sortInitPluginIds) { - Iterator iterator = sortedPlugins.iterator(); - while (iterator.hasNext()){ - String id = iterator.next(); - if(Objects.equals(id, sortPluginId)){ - newSortPluginIds.add(id); - iterator.remove(); - } - } - } - if(!sortedPlugins.isEmpty()){ - newSortPluginIds.addAll(sortedPlugins); - } - - return getSortResult(resolve, newSortPluginIds); - } - - @SuppressWarnings("unchecked") - private Result getSortResult(Result resolve, List newSortPluginIds ){ - try { - Field sortedPluginsField = ReflectionUtils.findField(Result.class, "sortedPlugins"); - List sortedPlugins = null; - if(sortedPluginsField != null){ - if (!sortedPluginsField.isAccessible()) { - sortedPluginsField.setAccessible(true); - } - sortedPlugins = (List) sortedPluginsField.get(resolve); - } - if(sortedPlugins == null){ - return resolve; - } - sortedPlugins.clear(); - sortedPlugins.addAll(newSortPluginIds); - return resolve; - } catch (Exception e){ - LOGGER.error("Set plugin init sort failure. use default sort init plugin. " + e.getMessage()); - return resolve; - } - } - - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/descriptor/DefaultPluginDescriptorExtend.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/descriptor/DefaultPluginDescriptorExtend.java deleted file mode 100644 index 701d42e9e19c00328e6a73399fb84dd9b52cd69c..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/descriptor/DefaultPluginDescriptorExtend.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.gitee.starblues.integration.pf4j.descriptor; - -import org.pf4j.DefaultPluginDescriptor; - -/** - * 扩展 DefaultPluginDescriptor 的功能 - * @author starBlues - * @version 2.4.5 - */ -public class DefaultPluginDescriptorExtend extends DefaultPluginDescriptor implements PluginDescriptorExtend{ - - - private String configFileName; - private String configFileProfile; - - public DefaultPluginDescriptorExtend() { - super(); - } - - public DefaultPluginDescriptorExtend(String pluginId, String pluginDescription, String pluginClass, - String version, String requires, String provider, String license) { - super(pluginId, pluginDescription, pluginClass, version, requires, provider, license); - } - - @Override - public String getConfigFileName() { - return configFileName; - } - - @Override - public String getConfigFileProfile() { - return configFileProfile; - } - - public void setConfigFileName(String configFileName) { - this.configFileName = configFileName; - } - - public void setConfigFileProfile(String configFileProfile) { - this.configFileProfile = configFileProfile; - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/descriptor/ManifestPluginDescriptorFinderExtend.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/descriptor/ManifestPluginDescriptorFinderExtend.java deleted file mode 100644 index b683b775b8eda43bf7df5eb414cc3d9258e81be3..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/descriptor/ManifestPluginDescriptorFinderExtend.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.gitee.starblues.integration.pf4j.descriptor; - -import org.pf4j.DefaultPluginDescriptor; -import org.pf4j.ManifestPluginDescriptorFinder; -import org.pf4j.PluginDescriptor; -import org.pf4j.util.StringUtils; - -import java.util.jar.Attributes; -import java.util.jar.Manifest; - -/** - * 扩展 ManifestPluginDescriptorFinderExtend 的功能 - * @author starBlues - * @version 2.4.5 - */ -public class ManifestPluginDescriptorFinderExtend extends ManifestPluginDescriptorFinder { - - public static final String PLUGIN_CONFIG_FILE_NAME = "Plugin-ConfigFileName"; - public static final String PLUGIN_CONFIG_FILE_PROFILE = "Plugin-ConfigFileProfile"; - - - @Override - protected PluginDescriptor createPluginDescriptor(Manifest manifest) { - DefaultPluginDescriptorExtend pluginDescriptor = (DefaultPluginDescriptorExtend) - super.createPluginDescriptor(manifest); - Attributes attributes = manifest.getMainAttributes(); - String configFileName = attributes.getValue(PLUGIN_CONFIG_FILE_NAME); - if (StringUtils.isNullOrEmpty(configFileName)) { - pluginDescriptor.setConfigFileName(configFileName); - } - String configFileProfile = attributes.getValue(PLUGIN_CONFIG_FILE_PROFILE); - if (!StringUtils.isNullOrEmpty(configFileProfile)) { - pluginDescriptor.setConfigFileProfile(configFileProfile); - } - return pluginDescriptor; - } - - @Override - protected DefaultPluginDescriptor createPluginDescriptorInstance() { - return new DefaultPluginDescriptorExtend(); - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/descriptor/PluginDescriptorExtend.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/descriptor/PluginDescriptorExtend.java deleted file mode 100644 index 4a4f8d9fe9f046a1c50c6ad272b046b44960e2c7..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/descriptor/PluginDescriptorExtend.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.gitee.starblues.integration.pf4j.descriptor; - -import org.pf4j.PluginDescriptor; - -/** - * 扩展 PluginDescriptor 的功能 - * @author starBlues - * @version 2.4.5 - */ -public interface PluginDescriptorExtend extends PluginDescriptor { - - - /** - * 获取配置文件名称 - * @return 文件名称 - */ - String getConfigFileName(); - - /** - * 配置文件Profile - * @return Profile - */ - String getConfigFileProfile(); - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/descriptor/ResolvePropertiesPluginDescriptorFinder.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/descriptor/ResolvePropertiesPluginDescriptorFinder.java deleted file mode 100644 index 1054d1615513d3929b618b1dbe160a2109d7efaa..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/descriptor/ResolvePropertiesPluginDescriptorFinder.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.gitee.starblues.integration.pf4j.descriptor; - -import org.pf4j.DefaultPluginDescriptor; -import org.pf4j.PluginDescriptor; -import org.pf4j.PluginRuntimeException; -import org.pf4j.PropertiesPluginDescriptorFinder; -import org.pf4j.util.FileUtils; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Properties; - -/** - * 解决乱码问题 - * - * @author starBlues - * @version 2.4.5 - */ -public class ResolvePropertiesPluginDescriptorFinder extends PropertiesPluginDescriptorFinder { - - @Override - protected Properties readProperties(Path pluginPath) { - Path propertiesPath = getPropertiesPath(pluginPath, propertiesFileName); - return getProperties(propertiesPath); - } - - @Override - protected PluginDescriptor createPluginDescriptor(Properties properties) { - DefaultPluginDescriptorExtend pluginDescriptor = (DefaultPluginDescriptorExtend) - super.createPluginDescriptor(properties); - return ResourcesPluginDescriptorFinder.resolvePluginDescriptor(properties, pluginDescriptor); - } - - @Override - protected DefaultPluginDescriptor createPluginDescriptorInstance() { - return new DefaultPluginDescriptorExtend(); - } - - public static Properties getProperties(Path propertiesPath){ - if (propertiesPath == null) { - throw new PluginRuntimeException("Cannot find the properties path"); - } - - if (Files.notExists(propertiesPath)) { - throw new PluginRuntimeException("Cannot find '{}' path", propertiesPath); - } - - Properties properties = new Properties(); - - try (InputStreamReader input = new InputStreamReader(Files.newInputStream(propertiesPath), - StandardCharsets.UTF_8)) { - properties.load(input); - } catch (IOException e) { - throw new PluginRuntimeException(e); - } finally { - FileUtils.closePath(propertiesPath); - } - - return properties; - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/descriptor/ResourcesPluginDescriptorFinder.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/descriptor/ResourcesPluginDescriptorFinder.java deleted file mode 100644 index d6690439cf259f11c1e4e363826b9dca5452d420..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/pf4j/descriptor/ResourcesPluginDescriptorFinder.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.gitee.starblues.integration.pf4j.descriptor; - -import org.pf4j.DefaultPluginDescriptor; -import org.pf4j.PluginDescriptor; -import org.pf4j.PropertiesPluginDescriptorFinder; -import org.pf4j.RuntimeMode; -import org.pf4j.util.StringUtils; - - -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Properties; - -/** - * 读取 resources 目录下的 plugin.properties 文件 - * @author starBlues - * @version 2.4.5 - */ -public class ResourcesPluginDescriptorFinder extends PropertiesPluginDescriptorFinder { - - public static final String PLUGIN_CONFIG_FILE_NAME = "plugin.configFileName"; - public static final String PLUGIN_CONFIG_FILE_PROFILE = "plugin.configFileProfile"; - - private final RuntimeMode runtimeMode; - - public ResourcesPluginDescriptorFinder(RuntimeMode runtimeMode) { - this.runtimeMode = runtimeMode; - } - - - @Override - public boolean isApplicable(Path pluginPath) { - Path propFilePath = getPropFilePath(pluginPath); - return super.isApplicable(propFilePath); - } - - @Override - public PluginDescriptor find(Path pluginPath) { - Path propFilePath = getPropFilePath(pluginPath); - return super.find(propFilePath); - } - - @Override - protected Properties readProperties(Path pluginPath) { - Path propertiesPath = getPropertiesPath(pluginPath, propertiesFileName); - return ResolvePropertiesPluginDescriptorFinder.getProperties(propertiesPath); - } - - @Override - protected PluginDescriptor createPluginDescriptor(Properties properties) { - DefaultPluginDescriptorExtend pluginDescriptor = (DefaultPluginDescriptorExtend) - super.createPluginDescriptor(properties); - return resolvePluginDescriptor(properties, pluginDescriptor); - } - - static PluginDescriptor resolvePluginDescriptor(Properties properties, - DefaultPluginDescriptorExtend pluginDescriptor){ - String configFileName = properties.getProperty(PLUGIN_CONFIG_FILE_NAME); - if (!StringUtils.isNullOrEmpty(configFileName)) { - pluginDescriptor.setConfigFileName(configFileName); - } - - String configFileProfile = properties.getProperty(PLUGIN_CONFIG_FILE_PROFILE); - if (!StringUtils.isNullOrEmpty(configFileProfile)) { - pluginDescriptor.setConfigFileProfile(configFileProfile); - } - - return pluginDescriptor; - } - - @Override - protected DefaultPluginDescriptor createPluginDescriptorInstance() { - return new DefaultPluginDescriptorExtend(); - } - - private Path getPropFilePath(Path pluginPath){ - if(runtimeMode == RuntimeMode.DEPLOYMENT){ - // 生产环境 - return pluginPath; - } else { - // 开发环境 - return Paths.get(pluginPath.toString(), "src", "main", "resources"); - } - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/refresh/AbstractPluginSpringBeanRefresh.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/refresh/AbstractPluginSpringBeanRefresh.java deleted file mode 100644 index 6b625a48a3ac96fd9ab35a2ee65c8c61a6edb2b1..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/refresh/AbstractPluginSpringBeanRefresh.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.gitee.starblues.integration.refresh; - -import com.gitee.starblues.integration.application.PluginApplication; - -import java.util.List; - -/** - * 抽象的插件SpringBean刷新类监听类. - * 继承该类。在插件动态的注册卸载时, refresh方法被触发, 可以获取到当前环境所有T实现的所有beans(不包括主程序中的beans) - * - * @author starBlues - * @version 2.0.2 - */ -public abstract class AbstractPluginSpringBeanRefresh extends AbstractSpringBeanRefresh { - - - public AbstractPluginSpringBeanRefresh(PluginApplication pluginApplication) { - super(pluginApplication); - } - - - /** - * 刷新bean - */ - @Override - protected List refresh(){ - return pluginApplication - .getPluginUser() - .getPluginBeans(typeClass); - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/refresh/AbstractSpringBeanRefresh.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/refresh/AbstractSpringBeanRefresh.java deleted file mode 100644 index d553a3a0e4afda7a69d5db80f14c9352e55845e1..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/refresh/AbstractSpringBeanRefresh.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.gitee.starblues.integration.refresh; - -import com.gitee.starblues.integration.application.PluginApplication; -import com.gitee.starblues.integration.listener.PluginListener; - -import java.lang.reflect.ParameterizedType; -import java.util.List; - -/** - * 抽象的SpringBean刷新类监听类. - * 继承该类。在插件动态的注册卸载时, refresh方法被触发, 可以获取到当前环境所有T实现的所有beans(包括主程序中的beans) - * - * @author starBlues - * @version 2.4.0 - */ -public abstract class AbstractSpringBeanRefresh implements PluginListener { - - private List beans; - - protected final Class typeClass; - protected final PluginApplication pluginApplication; - - - public AbstractSpringBeanRefresh(PluginApplication pluginApplication) { - this.pluginApplication = pluginApplication; - pluginApplication.addListener(this); - this.typeClass = (Class)((ParameterizedType)getClass() - .getGenericSuperclass()) - .getActualTypeArguments()[0]; - } - - @Override - public void registry(String pluginId, boolean isInitialize) { - this.beans = refresh(); - registryEvent(beans); - } - - @Override - public void unRegistry(String pluginId) { - this.beans = refresh(); - unRegistryEvent(beans); - } - - /** - * 注册事件 - * @param beans 当前所有实现的bean - */ - protected void registryEvent(List beans){ - - } - - /** - * 卸载事件 - * @param beans 当前卸载后所有的beans - */ - protected void unRegistryEvent(List beans){ - - } - - /** - * 刷新bean - * @return 返回刷新后的Bean集合 - */ - protected List refresh(){ - return pluginApplication - .getPluginUser() - .getBeans(typeClass); - } - - - /** - * 得到beans - * @return beansMap - */ - public List getBeans() { - return beans; - } -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/user/DefaultPluginUser.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/user/DefaultPluginUser.java deleted file mode 100644 index 3f3d75ac65e76ed9596ea27d5e6124ab87baadf4..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/user/DefaultPluginUser.java +++ /dev/null @@ -1,217 +0,0 @@ -package com.gitee.starblues.integration.user; - -import com.gitee.starblues.factory.process.pipe.PluginInfoContainers; -import com.gitee.starblues.utils.SpringBeanUtils; -import org.pf4j.PluginManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.util.ObjectUtils; - -import java.lang.annotation.Annotation; -import java.util.*; - -/** - * 默认插件使用者 - * @author starBlues - * @version 2.4.0 - */ -public class DefaultPluginUser implements PluginUser{ - - protected final Logger log = LoggerFactory.getLogger(this.getClass()); - - protected final GenericApplicationContext parentApplicationContext; - - protected final PluginManager pluginManager; - - public DefaultPluginUser(ApplicationContext parentApplicationContext, PluginManager pluginManager) { - Objects.requireNonNull(parentApplicationContext, "ApplicationContext can't be null"); - Objects.requireNonNull(pluginManager, "PluginManager can't be null"); - this.parentApplicationContext = (GenericApplicationContext)parentApplicationContext; - this.pluginManager = pluginManager; - } - - /** - * 通过bean名称得到插件的bean。(Spring管理的bean) - * @param name 插件bean的名称。spring体系中的bean名称。可以通过注解定义,也可以自定义生成。具体可百度 - * @param bean的类型 - * @return 返回bean - */ - @Override - public T getBean(String name){ - return getBean(name, true); - } - - @Override - public T getBean(Class aClass) { - return getBean(aClass, true); - } - - @Override - public T getPluginBean(String name) { - return getBean(name, false); - } - - /** - * 在主程序中定义的接口。插件或者主程序实现该接口。可以该方法获取到实现该接口的所有实现类。(Spring管理的bean) - * @param aClass 接口的类 - * @param bean的类型 - * @return List - */ - @Override - public List getBeans(Class aClass){ - return getBeans(aClass, 3); - } - - @Override - public List getMainBeans(Class aClass) { - return getBeans(aClass, 1); - } - - /** - * 在主程序中定义的接口。获取插件中实现该接口的实现类。(Spring管理的bean) - * @param aClass 接口的类 - * @param bean的类型 - * @return List - */ - @Override - public List getPluginBeans(Class aClass) { - return getBeans(aClass, 2); - } - - @Override - public List getPluginBeans(String pluginId, Class aClass) { - GenericApplicationContext pluginApplicationContext = - PluginInfoContainers.getPluginApplicationContext(pluginId); - if(pluginApplicationContext == null){ - return Collections.emptyList(); - } - return SpringBeanUtils.getBeans(pluginApplicationContext, aClass); - } - - @Override - public List getPluginBeansWithAnnotation(Class annotationType) { - List pluginApplicationContexts = PluginInfoContainers.getPluginApplicationContexts(); - List beans = new ArrayList<>(); - for (GenericApplicationContext pluginApplicationContext : pluginApplicationContexts) { - Map beanMap = pluginApplicationContext.getBeansWithAnnotation(annotationType); - if(!ObjectUtils.isEmpty(beanMap)){ - beans.addAll(beanMap.values()); - } - } - return beans; - } - - @Override - public List getPluginBeansWithAnnotation(String pluginId, Class annotationType) { - GenericApplicationContext genericApplicationContext = PluginInfoContainers.getPluginApplicationContext(pluginId); - if(genericApplicationContext == null){ - return Collections.emptyList(); - } - Map beanMap = genericApplicationContext.getBeansWithAnnotation(annotationType); - if(!ObjectUtils.isEmpty(beanMap)){ - return new ArrayList<>(beanMap.values()); - } else { - return Collections.emptyList(); - } - } - - @Override - public T generateNewInstance(T object) { - if(object == null){ - return null; - } - List pluginApplicationContexts = PluginInfoContainers.getPluginApplicationContexts(); - pluginApplicationContexts.add(parentApplicationContext); - Class aClass = object.getClass(); - for (GenericApplicationContext pluginApplicationContext : pluginApplicationContexts) { - try { - // 判断是否存在 - pluginApplicationContext.getBean(aClass); - Object newBean = pluginApplicationContext.getBeanFactory() - .createBean(aClass); - return (T) newBean; - } catch (Exception e){ - // 忽略 - } - } - return null; - } - - - /** - * 得到插件扩展接口实现的bean。(非Spring管理) - * @param tClass 接口的类 - * @param bean的类型 - * @return 返回bean - */ - @Override - public List getPluginExtensions(Class tClass){ - return pluginManager.getExtensions(tClass); - } - - - private T getBean(String name, boolean haveParent){ - List pluginApplicationContexts = PluginInfoContainers.getPluginApplicationContexts(); - if(haveParent){ - pluginApplicationContexts.add(parentApplicationContext); - } - for (GenericApplicationContext pluginApplicationContext : pluginApplicationContexts) { - if(pluginApplicationContext.containsBean(name)){ - return (T) pluginApplicationContext.getBean(name); - } - } - return null; - } - - private T getBean(Class aClass, boolean haveParent) { - List pluginApplicationContexts = PluginInfoContainers.getPluginApplicationContexts(); - if(haveParent){ - pluginApplicationContexts.add(parentApplicationContext); - } - for (GenericApplicationContext pluginApplicationContext : pluginApplicationContexts) { - try { - T bean = pluginApplicationContext.getBean(aClass); - if(bean != null){ - return bean; - } - } catch (Exception e){ - // 忽略 - } - } - return null; - } - - /** - * 获取多个bean. - * @param aClass 接口或者抽象类类类型 - * @param type 1 获取主程序的, 2 获取插件中的, 3 获取所有的 - * @param 类类型 - * @return List - */ - private List getBeans(Class aClass, int type) { - List pluginApplicationContexts = new ArrayList<>(1); - - if(type == 1){ - pluginApplicationContexts.add(parentApplicationContext); - } else if(type == 2){ - pluginApplicationContexts.addAll(PluginInfoContainers.getPluginApplicationContexts()); - } else if(type == 3){ - pluginApplicationContexts.add(parentApplicationContext); - pluginApplicationContexts.addAll(PluginInfoContainers.getPluginApplicationContexts()); - } else { - return Collections.emptyList(); - } - - List result = new ArrayList<>(); - for (GenericApplicationContext pluginApplicationContext : pluginApplicationContexts) { - List pluginBeans = SpringBeanUtils.getBeans(pluginApplicationContext, aClass); - if(!pluginBeans.isEmpty()){ - result.addAll(pluginBeans); - } - } - return result; - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/user/PluginUser.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/user/PluginUser.java deleted file mode 100644 index 65bcbf029ed75ea61f6c3048e3704fc911d45f79..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/integration/user/PluginUser.java +++ /dev/null @@ -1,130 +0,0 @@ -package com.gitee.starblues.integration.user; - -import java.lang.annotation.Annotation; -import java.util.List; -import java.util.Map; - -/** - * 该接口用于在主程序操作Spring管理的插件bean. - * 主要用途: 在主程序定义接口。插件中实现该接口做扩展, 主程序通过接口class可以获取到插件中的实现类。 - * @author starBlues - * @version 2.2.2 - */ -public interface PluginUser { - - /** - * 通过bean名称得到bean。(Spring管理的bean) - * @param name bean的名称。spring体系中的bean名称。可以通过注解定义,也可以自定义生成。具体可百度 - * @param bean的类型 - * @return T - */ - T getBean(String name); - - /** - * 通过aClass得到bean。(Spring管理的bean) - * @param aClass class - * @param bean的类型 - * @return T - */ - T getBean(Class aClass); - - /** - * 通过bean名称得到插件中的bean。(Spring管理的bean) - * @param name 插件中bean的名称。spring体系中的bean名称。可以通过注解定义,也可以自定义生成。具体可百度 - * @param bean的类型 - * @return T - */ - T getPluginBean(String name); - - /** - * 在主程序中定义的接口。 - * 插件或者主程序实现该接口。可以该方法获取到实现该接口的所有实现类。(Spring管理的bean) - * 使用场景: - * 1. 在主程序定义接口 - * 2. 在主程序和插件包中都存在实现该接口, 并使用Spring的组件注解(@Component、@Service) - * 3. 使用该方法可以获取到所以实现该接口的实现类(主程序和插件中)。 - * @param aClass 接口的类 - * @param bean的类型 - * @return List - */ - List getBeans(Class aClass); - - /** - * 得到主函数中定义的类。 - * 使用场景: - * 1. 在主程序定义接口 - * 2. 在主程序和插件包中都存在实现该接口, 并使用Spring的组件注解(@Component、@Service) - * 3. 使用该方法可以获取到主程序实现该接口的实现类。 - * @param aClass 类/接口的类 - * @param bean 的类型 - * @return List - */ - List getMainBeans(Class aClass); - - - /** - * 在主程序中定义的接口。获取插件中实现该接口的实现类。(Spring管理的bean) - * 使用场景: - * 1. 在主程序定义接口 - * 2. 插件包中实现该接口, 并使用Spring的组件注解(@Component、@Service) - * 3. 使用该方法可以获取到插件中实现该接口的实现类(不包括主程序)。 - * @param aClass 接口的类 - * @param bean的类型 - * @return 实现 aClass 接口的实现类的集合 - */ - List getPluginBeans(Class aClass); - - /** - * 在主程序中定义的接口。获取指定插件中实现该接口的实现类。(Spring管理的bean) - * 使用场景: - * 1. 在主程序定义接口 - * 2. 插件包中实现该接口, 并使用Spring的组件注解(@Component、@Service) - * 3. 使用该方法可以获取到指定插件中实现该接口的实现类。 - * @param pluginId 插件id - * @param aClass 接口的类 - * @param bean的类型 - * @return 实现 aClass 接口的实现类的集合 - */ - List getPluginBeans(String pluginId, Class aClass); - - /** - * 通过注解获取所有插件中的bean。(Spring管理的bean) - * @param annotationType 注解类型 - * @return 该注解的bean集合 - */ - List getPluginBeansWithAnnotation(Class annotationType); - - /** - * 通过注解获取具体插件中的bean。(Spring管理的bean) - * @param pluginId 插件id - * @param annotationType 注解类型 - * @return 该注解的bean集合 - */ - List getPluginBeansWithAnnotation(String pluginId, Class annotationType); - - - /** - * 生成一个新的Spring实例Bean. - * 使用场景:主要用于非单例对象的生成。 - * @param object 旧实例对象 - * @param 实例泛型 - * @return 新实例对象 - */ - @Deprecated - T generateNewInstance(T object); - - - - /** - * 使用场景: - * 1. 在主程序定义接口(该接口需要继承 ExtensionPoint 接口)。 - * 2. 插件包中实现该接口 - * 3. 在主程序可以使用该方法获取到实现该接口的实现类。(实现类可以配合 @Extension 控制顺序) - * 注意: 该场景用于非Spring管理的bean, 使用Spring注解无效 - * @param tClass bean的类型 - * @param bean的类型 - * @return List - */ - List getPluginExtensions(Class tClass); - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/realize/BasePlugin.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/realize/BasePlugin.java deleted file mode 100644 index 9d55eabaa057287e6dcc9b05565228f6b3cba1e1..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/realize/BasePlugin.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.gitee.starblues.realize; - -import org.pf4j.Plugin; -import org.pf4j.PluginWrapper; - -/** - * 插件包要继承的抽象类。 - * 注意: 实现该类的子类无法使用依赖注入 - * @author starBlues - * @version 2.4.0 - */ -public abstract class BasePlugin extends Plugin { - - private final BasePluginExtend basePluginExtend; - private String springBootConfigFilePath; - - public BasePlugin(PluginWrapper wrapper) { - super(wrapper); - this.basePluginExtend = new BasePluginExtend(this); - } - - - @Override - public final void start() { - try { - startEvent(); - } catch (Exception e) { - e.printStackTrace(); - } finally { - basePluginExtend.startEvent(); - } - } - - - @Override - public final void delete() { - try { - deleteEvent(); - } catch (Exception e){ - e.printStackTrace(); - } finally { - basePluginExtend.deleteEvent(); - } - - } - - @Override - public final void stop() { - try { - stopEvent(); - } catch (Exception e){ - e.printStackTrace(); - } finally { - basePluginExtend.startEvent(); - } - } - - /** - * 扫描包。默认为当前类包名。可重写自定义包名 - * @return 包名 - */ - public String scanPackage(){ - // 获取当前实现类的包名 - return this.getClass().getPackage().getName(); - } - - /** - * 得到插件扩展的信息 - * @return BasePluginExtend - */ - public final BasePluginExtend getBasePluginExtend() { - return basePluginExtend; - } - - /** - * 启动事件. Spring 容器都没有准备。无法使用注入。 - */ - protected void startEvent(){ - - } - - /** - * 删除事件. 在插件删除时触发。 - */ - protected void deleteEvent(){ - - } - - /** - * 停止事件. 在插件停止时触发。 - */ - protected void stopEvent(){ - - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/realize/BasePluginExtend.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/realize/BasePluginExtend.java deleted file mode 100644 index 1a0ce32acc3b6e0dd7283a23bb452c63d1597172..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/realize/BasePluginExtend.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.gitee.starblues.realize; - -/** - * 扩展的BasePlugin信息 - * - * @author starBlues - * @version 2.4.0 - */ -public final class BasePluginExtend { - - private final BasePlugin basePlugin; - private Long startTimestamp; - private Long stopTimestamp; - - BasePluginExtend(BasePlugin basePlugin){ - this.basePlugin = basePlugin; - } - - public long getStartTimestamp() { - return startTimestamp; - } - - public Long getStopTimestamp() { - return stopTimestamp; - } - - void startEvent(){ - startTimestamp = System.currentTimeMillis(); - } - - void deleteEvent(){ - stopTimestamp = System.currentTimeMillis(); - } - - void stopEvent(){ - stopTimestamp = System.currentTimeMillis(); - } - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/realize/ConfigBean.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/realize/ConfigBean.java deleted file mode 100644 index 73f6f827e4d263b006d31370829a670a8b1c2c51..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/realize/ConfigBean.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.gitee.starblues.realize; - -/** - * 插件可配置自定义bean的接口。 - * 注意:该实现类只能注入插件中的配置文件和主程序bean. 不能注入插件中其他的组件bean。 - * bean 指的是Spring 容器中管理的bean - * - * @author starBlues - * @version 2.4.0 - */ -public interface ConfigBean { - - - /** - * 初始化。所有bean的初始化工作在此处实现 - * @throws Exception 初始化异常 - */ - void initialize() throws Exception; - - /** - * 销毁实现 - * @throws Exception 销毁异常 - */ - void destroy() throws Exception; - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/realize/ConfigDefinitionTip.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/realize/ConfigDefinitionTip.java deleted file mode 100644 index dcb3ae5a0c09f788292f389080a6ab2d7ce3b68f..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/realize/ConfigDefinitionTip.java +++ /dev/null @@ -1,139 +0,0 @@ -package com.gitee.starblues.realize; - -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.integration.operator.module.PluginInfo; -import com.gitee.starblues.utils.SpringBeanUtils; -import org.pf4j.PluginManager; -import org.pf4j.PluginWrapper; -import org.springframework.boot.context.properties.bind.Bindable; -import org.springframework.boot.context.properties.bind.Binder; -import org.springframework.context.ApplicationContext; -import org.springframework.core.env.ConfigurableEnvironment; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * 给系统中所有ConfigDefinition 提供一个小工具类。可通过它获取主程序的某些bean. 和当前插件一些信息 - * @author starBlues - * @version 2.4.2 - */ -public class ConfigDefinitionTip { - - private final PluginRegistryInfo pluginRegistryInfo; - - public ConfigDefinitionTip(PluginRegistryInfo pluginRegistryInfo) { - this.pluginRegistryInfo = pluginRegistryInfo; - } - - /** - * 得到当前插件信息 - * @return 插件信息 - */ - public PluginInfo getCurrentPluginInfo(){ - PluginWrapper pluginWrapper = pluginRegistryInfo.getPluginWrapper(); - PluginManager pluginManager = pluginWrapper.getPluginManager(); - return new PluginInfo(pluginWrapper.getDescriptor(), pluginWrapper.getPluginState(), - pluginWrapper.getPluginPath().toAbsolutePath().toString(), - pluginManager.getRuntimeMode().toString()); - } - - /** - * 得到主程序的 ApplicationContext - * @return ApplicationContext - */ - public ApplicationContext getMainApplication() { - return pluginRegistryInfo.getMainApplicationContext(); - } - - /** - * 获取当前插件的其他的ConfigDefinition - * @param configDefinitionClass ConfigDefinition的类类型 - * @param 类类型 - * @return T - */ - public T getOtherConfigDefinition(Class configDefinitionClass){ - return SpringBeanUtils.getObjectClass( - pluginRegistryInfo.getConfigSingletons(), - configDefinitionClass); - } - - /** - * 将Springboot类型的配置文件中的值映射为bean - * 注意: 只针对插件扩展的 springboot 配置文件生效 - * @param prefix 配置文件中的前置, 比如: plugin.config - * @param type 配置文件中结构对应的类类型, 比如: plugin.config 下定义的键值对和type类类型一致 - * @param 类类型 - * @return T - */ - public T getConfigOfBean(String prefix, Class type){ - return pluginRegistryInfo.getPluginBinder() - .bind(prefix, Bindable.of(type)) - .orElseGet(() -> null); - } - - /** - * 将Springboot类型的配置文件中的值映射为 List - * 注意: 只针对插件扩展的 springboot 配置文件生效 - * @param prefix 配置文件中的前置, 比如: plugin.config - * @param type List元素的类类型 - * @param List中定义的类类型 - * @return List - */ - public List getConfigOfList(String prefix, Class type){ - return pluginRegistryInfo.getPluginBinder() - .bind(prefix, Bindable.listOf(type)) - .orElseGet(Collections::emptyList); - } - - /** - * 将Springboot类型的配置文件中的值映射为 Set - * 注意: 只针对插件扩展的 springboot 配置文件生效 - * @param prefix 配置文件中的前置, 比如: plugin.config - * @param type Set元素的类类型 - * @param 类类型 - * @return Set - */ - public Set getConfigOfSet(String prefix, Class type){ - return pluginRegistryInfo.getPluginBinder() - .bind(prefix, Bindable.setOf(type)) - .orElseGet(Collections::emptySet); - } - - - /** - * 将Springboot类型的配置文件中的值映射为 Map - * 注意: 只针对插件扩展的 springboot 配置文件生效 - * @param prefix 配置文件中的前置, 比如: plugin.config - * @param keyType map的key元素类型 - * @param valueType map的值元素类型 - * @param map key 元素的类类型 - * @param map value 元素的类类型 - * @return Map - */ - public Map getConfigOfSet(String prefix, Class keyType, Class valueType){ - return pluginRegistryInfo.getPluginBinder() - .bind(prefix, Bindable.mapOf(keyType, valueType)) - .orElseGet(Collections::emptyMap); - } - - - /** - * 返回当前插件的ConfigurableEnvironment - * @return ConfigurableEnvironment - */ - public ConfigurableEnvironment getPluginEnvironment(){ - return pluginRegistryInfo.getPluginApplicationContext().getEnvironment(); - } - - /** - * 返回当前插件的Binder - * @return Binder - */ - public Binder getPluginBinder(){ - return pluginRegistryInfo.getPluginBinder(); - } - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/realize/OneselfListener.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/realize/OneselfListener.java deleted file mode 100644 index ff7fc90550afabc3b825208e3677df2c20c32d12..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/realize/OneselfListener.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.gitee.starblues.realize; - -import com.gitee.starblues.utils.OrderPriority; - -/** - * 监听本插件模块事件的监听者接口 - * - * @author starBlues - * @version 2.4.0 - */ -public interface OneselfListener { - - /** - * 执行优先级。用于多个监听器的时候 - * @return OrderPriority - */ - OrderPriority order(); - - - /** - * 启动事件 - * @param basePlugin 当前插件实现的BasePlugin类 - */ - void startEvent(BasePlugin basePlugin); - - - /** - * 停止事件 - * @param basePlugin 当前插件实现的BasePlugin类 - */ - void stopEvent(BasePlugin basePlugin); - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/realize/PluginUtils.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/realize/PluginUtils.java deleted file mode 100644 index d750e2a2ab16780560bb7f1e5f9800152fa2f26e..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/realize/PluginUtils.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.gitee.starblues.realize; - -import com.gitee.starblues.utils.SpringBeanUtils; -import org.pf4j.PluginDescriptor; -import org.springframework.context.ApplicationContext; - -import java.util.List; - -/** - * 插件工具类 - * @author starBlues - * @version 2.4.0 - */ -public class PluginUtils { - - protected final ApplicationContext parentApplicationContext; - protected final ApplicationContext pluginApplicationContext; - protected final PluginDescriptor pluginDescriptor; - - public PluginUtils(ApplicationContext parentApplicationContext, - ApplicationContext pluginApplicationContext, - PluginDescriptor pluginDescriptor) { - this.parentApplicationContext = parentApplicationContext; - this.pluginApplicationContext = pluginApplicationContext; - this.pluginDescriptor = pluginDescriptor; - } - - /** - * 获取主程序的 ApplicationContext - * @return ApplicationContext - */ - public ApplicationContext getMainApplicationContext() { - return parentApplicationContext; - } - - /** - * 获取当前插件的 ApplicationContext - * @return ApplicationContext - */ - public ApplicationContext getPluginApplicationContext() { - return pluginApplicationContext; - } - - /** - * 获取当前插件的描述信息 - * @return PluginDescriptor - */ - public PluginDescriptor getPluginDescriptor(){ - return pluginDescriptor; - } - - - /** - * 通过 bean名称得到主程序中的bean - * @param name bean 名称 - * @param bean 类型 - * @return bean - */ - public T getMainBean(String name){ - Object bean = parentApplicationContext.getBean(name); - if(bean == null){ - return null; - } - return (T) bean; - } - - /** - * 通过bean类型得到主程序中的bean - * @param aClass bean 类型 - * @param bean 类型 - * @return bean - */ - public T getMainBean(Class aClass) { - return parentApplicationContext.getBean(aClass); - } - - /** - * 通过接口或者抽象类类型得到主程序中的多个实现对象 - * @param aClass bean 类型 - * @param bean 类型 - * @return bean - */ - public List getMainBeans(Class aClass){ - return SpringBeanUtils.getBeans(parentApplicationContext, aClass); - } - -} 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 deleted file mode 100644 index 9fd2bf68e9a6f44707866548dc013820b28db6ca..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/AopUtils.java +++ /dev/null @@ -1,168 +0,0 @@ -package com.gitee.starblues.utils; - -import org.pf4j.PluginWrapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.aop.framework.ProxyProcessorSupport; -import org.springframework.context.ApplicationContext; -import org.springframework.util.ClassUtils; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * AOP 无法找到插件类的解决工具类(被遗弃) - * - * @author starBlues - * @version 2.4.0 - */ -@Deprecated -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(){} - - /** - * 解决AOP无法代理到插件类的问题 - * @param applicationContext 插件包装类 - */ - public static synchronized void registered(ApplicationContext applicationContext) { - Map beansOfType = applicationContext - .getBeansOfType(ProxyProcessorSupport.class); - if(beansOfType.isEmpty()){ - LOG.warn("Not found ProxyProcessorSupports, And Plugin AOP can't used"); - return; - } - for (ProxyProcessorSupport support : beansOfType.values()) { - if(support == null){ - continue; - } - ProxyWrapper proxyWrapper = new ProxyWrapper(); - proxyWrapper.setProxyProcessorSupport(support); - PROXY_WRAPPERS.add(proxyWrapper); - } - } - - /** - * 解决AOP无法代理到插件类的问题 - * @param pluginWrapper 插件包装类 - */ - public static synchronized void resolveAop(PluginWrapper pluginWrapper){ - if(PROXY_WRAPPERS.isEmpty()){ - 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(); - ClassLoader classLoader = getClassLoader(proxyProcessorSupport); - proxyWrapper.setOriginalClassLoader(classLoader); - proxyProcessorSupport.setProxyClassLoader(pluginClassLoader); - } - } - - /** - * 恢复AOP 的 BeanClassLoader - */ - public static synchronized void recoverAop(){ - if(PROXY_WRAPPERS.isEmpty()){ - return; - } - for (ProxyWrapper proxyWrapper : PROXY_WRAPPERS) { - ProxyProcessorSupport proxyProcessorSupport = proxyWrapper.getProxyProcessorSupport(); - proxyProcessorSupport.setProxyClassLoader(proxyWrapper.getOriginalClassLoader()); - } - isRecover.set(true); - } - - /** - * 反射获取代理支持处理者的ClassLoader属性值 - * @param proxyProcessorSupport proxyProcessorSupport - * @return ClassLoader - */ - private static ClassLoader getClassLoader(ProxyProcessorSupport proxyProcessorSupport){ - Class aClass = proxyProcessorSupport.getClass(); - while (aClass != null){ - if(aClass != ProxyProcessorSupport.class){ - aClass = aClass.getSuperclass(); - continue; - } - Field[] declaredFields = aClass.getDeclaredFields(); - if(declaredFields == null || declaredFields.length == 0){ - break; - } - for (Field field : declaredFields) { - if(Objects.equals("proxyClassLoader", field.getName()) || field.getType() == ClassLoader.class){ - field.setAccessible(true); - try { - Object o = field.get(proxyProcessorSupport); - if(o instanceof ClassLoader){ - return (ClassLoader) o; - } else { - LOG.warn("Get {} classLoader type not is ClassLoader type, And Return DefaultClassLoader", - aClass.getName()); - return ClassUtils.getDefaultClassLoader(); - } - } catch (IllegalAccessException e) { - LOG.error("Get {} classLoader failure {}, And Return DefaultClassLoader", - aClass.getName(), - e.getMessage()); - return ClassUtils.getDefaultClassLoader(); - } - } - } - - } - LOG.warn("Not found classLoader field, And Return DefaultClassLoader", - aClass.getName()); - return ClassUtils.getDefaultClassLoader(); - } - - - - /** - * 代理包装类 - */ - private static class ProxyWrapper{ - ProxyProcessorSupport proxyProcessorSupport; - ClassLoader originalClassLoader; - - ProxyProcessorSupport getProxyProcessorSupport() { - return proxyProcessorSupport; - } - - void setProxyProcessorSupport(ProxyProcessorSupport proxyProcessorSupport) { - this.proxyProcessorSupport = proxyProcessorSupport; - } - - ClassLoader getOriginalClassLoader() { - return originalClassLoader; - } - - 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/CommonUtils.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/CommonUtils.java deleted file mode 100644 index 87a270c9b5482906882d2b5f0987ee3efc7e6ecb..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/CommonUtils.java +++ /dev/null @@ -1,166 +0,0 @@ -package com.gitee.starblues.utils; - -import com.gitee.starblues.integration.IntegrationConfiguration; -import org.pf4j.util.StringUtils; - -import java.io.File; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.function.Function; - -/** - * 通用工具 - * - * @author starBlues - * @version 2.2.1 - */ -public class CommonUtils { - - private CommonUtils(){} - - /** - * list按照int排序. 数字越大, 越排在前面 - * @param list list集合 - * @param orderImpl 排序实现 - * @param T - * @return List - */ - public static List order(List list, Function orderImpl){ - if(list == null){ - return list; - } - Collections.sort(list, Comparator.comparing(orderImpl, Comparator.nullsLast(Comparator.reverseOrder()))); - return list; - } - - - /** - * 对 OrderPriority 进行排序操作 - * @param order OrderPriority - * @param 当前操作要被排序的bean - * @return Comparator - */ - public static Comparator orderPriority(final Function order){ - return Comparator.comparing(t -> { - OrderPriority orderPriority = order.apply(t); - if(orderPriority == null){ - orderPriority = OrderPriority.getLowPriority(); - } - return orderPriority.getPriority(); - }, Comparator.nullsLast(Comparator.reverseOrder())); - } - - - /** - * 得到插件接口前缀 - * @param configuration 配置 - * @param pluginId 插件id - * @return 接口前缀 - */ - public static String getPluginRestPrefix(IntegrationConfiguration configuration, String pluginId){ - String pathPrefix = configuration.pluginRestPathPrefix(); - if(configuration.enablePluginIdRestPathPrefix()){ - if(pathPrefix != null && !"".equals(pathPrefix)){ - pathPrefix = restJoiningPath(pathPrefix, pluginId); - } else { - pathPrefix = pluginId; - } - return pathPrefix; - } else { - if(pathPrefix == null || "".equals(pathPrefix)){ - // 不启用插件id作为路径前缀, 并且路径前缀为空, 则直接返回。 - return null; - } - } - return pathPrefix; - } - - - /** - * rest接口拼接路径 - * @param path1 路径1 - * @param path2 路径2 - * @return 拼接的路径 - */ - public static String restJoiningPath(String path1, String path2){ - if(path1 != null && path2 != null){ - if(path1.endsWith("/") && path2.startsWith("/")){ - return path1 + path2.substring(1); - } else if(!path1.endsWith("/") && !path2.startsWith("/")){ - return path1 + "/" + path2; - } else { - return path1 + path2; - } - } else if(path1 != null){ - return path1; - } else if(path2 != null){ - return path2; - } else { - return ""; - } - } - - - /** - * 拼接url路径 - * @param paths 拼接的路径 - * @return 拼接的路径 - */ - public static String joiningPath(String ...paths){ - if(paths == null || paths.length == 0){ - return ""; - } - StringBuilder stringBuilder = new StringBuilder(); - int length = paths.length; - for (int i = 0; i < length; i++) { - String path = paths[i]; - if(StringUtils.isNullOrEmpty(path)) { - continue; - } - if((i < length - 1) && path.endsWith("/")){ - path = path.substring(path.lastIndexOf("/")); - } - if(path.startsWith("/")){ - stringBuilder.append(path); - } else { - stringBuilder.append("/").append(path); - } - } - - return stringBuilder.toString(); - } - - /** - * 拼接file路径 - * @param paths 拼接的路径 - * @return 拼接的路径 - */ - public static String joiningFilePath(String ...paths){ - if(paths == null || paths.length == 0){ - return ""; - } - StringBuilder stringBuilder = new StringBuilder(); - int length = paths.length; - for (int i = 0; i < length; i++) { - String path = paths[i]; - if(StringUtils.isNullOrEmpty(path)) { - continue; - } - if(i > 0){ - if(path.startsWith(File.separator) || path.startsWith("/") || - path.startsWith("\\") || path.startsWith("//")){ - stringBuilder.append(path); - } else { - stringBuilder.append(File.separator).append(path); - } - } else { - stringBuilder.append(path); - } - } - - return stringBuilder.toString(); - } - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/GlobalRegistryInfo.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/GlobalRegistryInfo.java deleted file mode 100644 index 827f2610d5f0eb913620ae589dc2b85ce5258e99..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/GlobalRegistryInfo.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.gitee.starblues.utils; - - -import com.gitee.starblues.integration.IntegrationConfiguration; - -import java.util.HashMap; -import java.util.Map; - -/** - * 全局注册信息 - * - * @author starBlues - * @version 2.2.0 - */ -public final class GlobalRegistryInfo { - - private GlobalRegistryInfo(){} - - /** - * 全局插件安装次数 - */ - private static Map operatorPluginInfos = new HashMap<>(); - - /** - * 全局扩展信息 - */ - private static Map extensionMap = new HashMap<>(); - - - /** - * 添加操作插件信息 - * @param pluginId 插件id - * @param operatorType 操作类型 - * @param isLock 是否加锁 - */ - public static synchronized void addOperatorPluginInfo(String pluginId, - PluginOperatorInfo.OperatorType operatorType, - boolean isLock){ - PluginOperatorInfo operatorPluginInfo = operatorPluginInfos.get(pluginId); - if(operatorPluginInfo == null){ - operatorPluginInfo = new PluginOperatorInfo(); - operatorPluginInfos.put(pluginId, operatorPluginInfo); - } - operatorPluginInfo.setOperatorType(operatorType); - operatorPluginInfo.setLock(isLock); - } - - - /** - * 设置操作插件的信息 - * @param pluginId 插件id - * @param isLock 是否加锁 - */ - public static synchronized void setOperatorPluginInfo(String pluginId, boolean isLock){ - PluginOperatorInfo operatorPluginInfo = operatorPluginInfos.get(pluginId); - if(operatorPluginInfo != null){ - operatorPluginInfo.setLock(isLock); - } - } - - - - /** - * 获取插件安装次数 - * @param pluginId 插件id - * @return 操作插件类型 - */ - public static synchronized PluginOperatorInfo getPluginInstallNum(String pluginId){ - return operatorPluginInfos.get(pluginId); - } - - - /** - * 添加全局扩展数据 - * @param key 扩展的key - * @param value 扩展值 - */ - public static synchronized void addExtension(String key, Object value){ - extensionMap.put(key, value); - } - - /** - * 删除全局扩展数据 - * @param key 扩展的key - */ - public static synchronized void removeExtension(String key){ - extensionMap.remove(key); - } - - /** - * 获取全局扩展值 - * @param key 全局扩展的key - * @param 返回值泛型 - * @return 扩展值 - */ - public static synchronized T getExtension(String key){ - Object o = extensionMap.get(key); - if(o == null){ - return null; - } else { - return (T) o; - } - } - - - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/OrderExecution.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/OrderExecution.java deleted file mode 100644 index 4f30f3bf6ec5a350e8ed7ed086a87bcb90ec00be..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/OrderExecution.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.gitee.starblues.utils; - -/** - * 执行顺序 - * - * @author starBlues - * @version 1.0 - */ -public class OrderExecution { - - private OrderExecution(){} - - /** - * 低优先级 - */ - public static final int LOW = Integer.MAX_VALUE; - - - /** - * 中优先级 - */ - public static final int MIDDLE = Integer.MAX_VALUE; - - - - /** - * 高优先级 - */ - public static final int HIGH = Integer.MIN_VALUE; - - - -} 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 deleted file mode 100644 index e40b1bf381b660ba48fdfc6b7e11b16e22b400bd..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/PluginFileUtils.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.gitee.starblues.utils; - -import com.gitee.starblues.annotation.ConfigDefinition; -import org.pf4j.RuntimeMode; -import org.pf4j.util.StringUtils; - -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 starBlues - * @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){ - try { - Files.deleteIfExists(subPath); - } catch (IOException e) { - e.printStackTrace(); - } - } - }); - } catch (IOException e) { - e.printStackTrace(); - } - - } - - - - /** - * 如果文件不存在, 则会创建 - * @param path 插件路径 - * @return 插件路径 - * @throws IOException 没有发现文件异常 - */ - public static Path createExistFile(Path path) throws IOException { - Path parent = path.getParent(); - if(!Files.exists(parent)){ - Files.createDirectories(parent); - } - if(!Files.exists(path)){ - Files.createFile(path); - } - return path; - } - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/PluginOperatorInfo.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/PluginOperatorInfo.java deleted file mode 100644 index be3b0ec9e216c12b2d635e409723549c475f1dc6..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/PluginOperatorInfo.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.gitee.starblues.utils; - -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * 操作插件信息。为了解决连续上传安装后, 停止后, 无法启动的问题。 - * - * @author starBlues - * @version 2.2.0 - */ -public class PluginOperatorInfo { - - - /** - * 当前操作类型 - */ - private OperatorType operatorType; - - /** - * 是否锁定。如果锁定, 则不允许更新操作类型。 - * 该参数主要解决在安装插件时 setOperatorType 一次操作类型,但是它又调用了启动插件,而启动插件也要 setOperatorType 一次操作类型 - * 所以, 使用该参数, 用于在安装插件时, 锁定 setOperatorType, 在启动时就无法再 setOperatorType 了, 就不会覆盖该值 - */ - private final AtomicBoolean isLock = new AtomicBoolean(false); - - /** - * 设置操作类型 - * @param operatorType 操作类型 - */ - public void setOperatorType(OperatorType operatorType) { - if(operatorType != null && !isLock.get()){ - // 如果锁定了, 则不能更新操作类型 - this.operatorType = operatorType; - } - } - - /** - * 主要用于锁定或者解锁。 - * @param isLock 是否锁定。true 锁定, false 解锁 - */ - public void setLock(boolean isLock){ - this.isLock.set(isLock); - } - - public OperatorType getOperatorType() { - return operatorType; - } - - /** - * 操作类型 - */ - public enum OperatorType{ - /** - * 启动插件 - */ - START, - - /** - * 安装插件 - */ - INSTALL, - } - - -} diff --git a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/ResourceUtils.java b/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/ResourceUtils.java deleted file mode 100644 index ae61d51f897584b9c2d2e84d2a6ddfe6bfb36d7c..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/main/java/com/gitee/starblues/utils/ResourceUtils.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.gitee.starblues.utils; - -import com.gitee.starblues.factory.PluginRegistryInfo; -import com.gitee.starblues.integration.IntegrationConfiguration; -import org.pf4j.PluginWrapper; -import org.pf4j.RuntimeMode; -import org.pf4j.util.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.nio.file.Paths; - -/** - * 对资源解析的工具类 - * - * @author starBlues - * @version 2.4.4 - */ -public class ResourceUtils { - - private static final Logger LOGGER = LoggerFactory.getLogger(ResourceUtils.class); - - public final static String TYPE_FILE = "file"; - public final static String TYPE_CLASSPATH = "classpath"; - public final static String TYPE_PACKAGE = "package"; - - public static final String ROOT_PLUGIN_SIGN = "~"; - - public final static String TYPE_SPLIT = ":"; - - /** - * 获取匹配路绝 - * @param locationMatch 原始匹配路径。规则为: file:xxx, classpath:xxx , package:xxx - * @return 整合出完整的匹配路绝 - */ - public static String getMatchLocation(String locationMatch){ - if(StringUtils.isNullOrEmpty(locationMatch)){ - return null; - } - String classPathType = TYPE_CLASSPATH + TYPE_SPLIT; - if(isClasspath(locationMatch)){ - return locationMatch.replaceFirst(classPathType, ""); - } - String fileType = TYPE_FILE + TYPE_SPLIT; - if(isFile(locationMatch)){ - return locationMatch.replaceFirst(fileType, ""); - } - String packageType = TYPE_PACKAGE + TYPE_SPLIT; - if(isPackage(locationMatch)){ - String location = locationMatch.replaceFirst(packageType, ""); - return location.replace(".", "/"); - } - LOGGER.error("locationMatch {} illegal", locationMatch); - return null; - } - - public static boolean isClasspath(String locationMatch){ - return locationMatch.startsWith(TYPE_CLASSPATH + TYPE_SPLIT); - } - - public static boolean isFile(String locationMatch){ - return locationMatch.startsWith(TYPE_FILE + TYPE_SPLIT); - } - - public static boolean isPackage(String locationMatch){ - return locationMatch.startsWith(TYPE_PACKAGE + TYPE_SPLIT); - } - - /** - * 根据 ~ 标记获取, 得到绝对路径 - * @param pluginRegistryInfo pluginRegistryInfo - * @param rootDir 根目录 - * @return java.lang.String - **/ - public static String getAbsolutePath(PluginRegistryInfo pluginRegistryInfo, String rootDir){ - if(StringUtils.isNullOrEmpty(rootDir)){ - return rootDir; - } - String home = null; - if(rootDir.startsWith(ResourceUtils.ROOT_PLUGIN_SIGN)){ - String pluginRootDir; - PluginWrapper pluginWrapper = pluginRegistryInfo.getPluginWrapper(); - RuntimeMode runtimeMode = pluginWrapper.getRuntimeMode(); - if(runtimeMode == RuntimeMode.DEVELOPMENT){ - pluginRootDir = pluginWrapper.getPluginPath().toString(); - } else { - pluginRootDir = System.getProperty("user.dir"); - } - // 如果root路径中开始存在ROOT_PLUGIN_SIGN,则说明进行插件根路替换 - home = rootDir.replaceFirst("\\" + ResourceUtils.ROOT_PLUGIN_SIGN, ""); - home = CommonUtils.joiningFilePath(pluginRootDir, home); - } else { - home = rootDir; - } - return home; - } - -} diff --git a/springboot-plugin-framework/src/test/java/com/gitee/starblues/utils/CommonUtilsTest.java b/springboot-plugin-framework/src/test/java/com/gitee/starblues/utils/CommonUtilsTest.java deleted file mode 100644 index 19e945fb7e141f2785edcb9dd0d202dea45f443b..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/test/java/com/gitee/starblues/utils/CommonUtilsTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.gitee.starblues.utils; - -import org.junit.Assert; -import org.junit.Test; - -/** - * @author starBlues - * @version 1.0 - */ -public class CommonUtilsTest { - - @Test - public void testJoin(){ - Assert.assertEquals(CommonUtils.joiningPath("/p1", "p2", "p3"), "/p1/p2/p3"); - Assert.assertEquals(CommonUtils.joiningPath("/p1", "p2", "p3/"), "/p1/p2/p3/"); - Assert.assertEquals(CommonUtils.joiningPath("p1", "p2", "p3/"), "/p1/p2/p3/"); - } - - - -} diff --git a/springboot-plugin-framework/src/test/java/com/gitee/starblues/utils/PluginConfigUtilsTest.java b/springboot-plugin-framework/src/test/java/com/gitee/starblues/utils/PluginConfigUtilsTest.java deleted file mode 100644 index e350ce7d80dc0261a112addb28b0a54b915e8841..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/test/java/com/gitee/starblues/utils/PluginConfigUtilsTest.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.gitee.starblues.utils; - -import org.junit.Assert; -import org.junit.Test; -import org.pf4j.RuntimeMode; - -/** - * @author starBlues - * @version 1.0 - */ -public class PluginConfigUtilsTest { - - - @Test - public void testGetConfigFileName(){ - String fileName = "fileName.yml"; - String prod = "prod"; - String dev = "dev"; - PluginConfigUtils.FileNamePack configFileName = PluginConfigUtils.getConfigFileName(fileName, - prod, dev, RuntimeMode.DEPLOYMENT); - Assert.assertNotNull(configFileName); - Assert.assertEquals(fileName, configFileName.getSourceFileName()); - Assert.assertEquals(prod, configFileName.getFileSuffix()); - - configFileName = PluginConfigUtils.getConfigFileName(fileName, - prod, dev, RuntimeMode.DEVELOPMENT); - Assert.assertNotNull(configFileName); - Assert.assertEquals(fileName, configFileName.getSourceFileName()); - Assert.assertEquals(dev, configFileName.getFileSuffix()); - } - - - @Test - public void testJoinConfigFileName_1(){ - String fileName = "fileName.yml"; - String suffix = "prod"; - String joinConfigFileName = PluginConfigUtils.joinConfigFileName(fileName, suffix); - Assert.assertNotNull(joinConfigFileName); - Assert.assertEquals("fileName-prod.yml", joinConfigFileName); - } - - @Test - public void testJoinConfigFileName_2(){ - String fileName = "fileName.yml"; - String suffix = ""; - String joinConfigFileName = PluginConfigUtils.joinConfigFileName(fileName, suffix); - Assert.assertNotNull(joinConfigFileName); - Assert.assertEquals("fileName.yml", joinConfigFileName); - } - - @Test - public void testJoinConfigFileName_3(){ - String fileName = "fileName.yml"; - String suffix = null; - String joinConfigFileName = PluginConfigUtils.joinConfigFileName(fileName, suffix); - Assert.assertNotNull(joinConfigFileName); - Assert.assertEquals("fileName.yml", joinConfigFileName); - } - - @Test - public void testJoinConfigFileName_4(){ - String fileName = null; - String suffix = null; - String joinConfigFileName = PluginConfigUtils.joinConfigFileName(fileName, suffix); - Assert.assertNull(joinConfigFileName); - } - -} diff --git a/springboot-plugin-framework/src/test/java/com/gitee/starblues/utils/ResourceUtilsTest.java b/springboot-plugin-framework/src/test/java/com/gitee/starblues/utils/ResourceUtilsTest.java deleted file mode 100644 index 1f2f6d15697ddcd1408300ae60bbfac42db54f93..0000000000000000000000000000000000000000 --- a/springboot-plugin-framework/src/test/java/com/gitee/starblues/utils/ResourceUtilsTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.gitee.starblues.utils; - - -import org.junit.Assert; -import org.junit.Test; - -/** - * @author starBlues - * @version 2.3.4 - * @since 2021-06-01 - */ -public class ResourceUtilsTest { - - @Test - public void test(){ - String locationMatch = "file:C:\\Users\\Desktop\\plugin1-log.xml"; - String matchLocation = ResourceUtils.getMatchLocation(locationMatch); - Assert.assertEquals("C:\\Users\\Desktop\\plugin1-log.xml", - matchLocation); - - locationMatch = "classpath:C:\\Users\\Desktop\\plugin1-log.xml"; - matchLocation = ResourceUtils.getMatchLocation(locationMatch); - Assert.assertEquals("C:\\Users\\Desktop\\plugin1-log.xml", - matchLocation); - - locationMatch = "package:com.test.aa"; - matchLocation = ResourceUtils.getMatchLocation(locationMatch); - Assert.assertEquals("com/test/aa", - matchLocation); - - - } - -} \ No newline at end of file