Skip to content

Commit d3cf4ba

Browse files
committed
WIP: new plugin system first iteration
1 parent 12d7b8c commit d3cf4ba

File tree

15 files changed

+971
-329
lines changed

15 files changed

+971
-329
lines changed

server/api-service/PLUGIN.md

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Lowcoder plugin system (WIP)
2+
3+
This is an ongoing effort to refactor current plugin system based on pf4j library.
4+
5+
## Reasoning
6+
7+
1. create a cleaner and simpler plugin system with clearly defined purpose(s) (new endpoints, new datasource types, etc..)
8+
2. lowcoder does not need live plugin loading/reloading/unloading/updates, therefore the main feature of pf4j is rendered useless, in fact it adds a lot of complexity due to classloaders used for managing plugins (especially in spring/boot applications)
9+
3. simpler and easier plugin detection - just a jar with a class implementing a common interface (be it a simple pojo project or a complex spring/boot implementation)
10+
11+
## How it works
12+
13+
The main entrypoint for plugin system is in **lowcoder-server** module with class **org.lowcoder.api.framework.configuration.PluginConfiguration**
14+
It creates:
15+
- LowcoderPluginManager bean which is responsible for plugin lifecycle management
16+
- Adds plugin defined endpoints to lowcoder by creating **pluginEndpoints** bean
17+
- TODO: Adds plugin defined datasources to lowcoder by creating **pluginDatasources** bean
18+
19+
### lowcoder-plugin-api library
20+
21+
This library contains APIs for plugin implementations.
22+
It is used by both, lowcoder API server as well as all plugins.
23+
24+
### PluginLoader
25+
26+
The sole purpose of a PluginLoader is to find plugin candidates and load them into VM.
27+
There is currently one implementation that based on paths - **PathBasedPluginLoader**, it:
28+
- looks in folders and subfolders defined in **application.yaml** - entries can point to a folder or specific jar file. If a relative path is supplied, the location of lowcoder API server application jar is used as parent folder (when run in non-packaged state, eg. in IDE, it uses the folder where ServerApplication.class is generated)
29+
30+
```yaml
31+
common:
32+
plugin-dirs:
33+
- plugins
34+
- /some/custom/path/myGreatPlugin.jar
35+
```
36+
- finds all **jar**(s) and inspects them for classes implementing **LowcoderPlugin** interface
37+
- instantiates all LowcoderPlugin implementations
38+
39+
### LowcoderPluginManager
40+
41+
The main job of plugin manager is to:
42+
- register plugins found and instantiated by **PluginLoader**
43+
- start registered plugins by calling **LowcoderPlugin.load()** method
44+
- create and register **RouterFunction**(s) for all loaded plugin endpoints
45+
- TODO: create and register datasources for all loaded plugin datasources
46+
47+
## Plugin project structure
48+
49+
Plugin jar can be structured in any way you like. It can be a plain java project, but also a spring/boot based project or based on any other framework.
50+
51+
It is composed from several parts:
52+
- class(es) implementing **LowcoderPlugin** interface
53+
- class(es) implementing **LowcoderEndpoint** interface, containing endpoint handler functions marked with **@EndpointExtension** annotation. These functions must obey following format:
54+
55+
```java
56+
@EndpointExtension(uri = <endpoint uri>, method = <HTTP method>)
57+
public Mono<ServerResponse> <handler name>(ServerRequest request)
58+
{
59+
... your endpoint logic implementation
60+
}
61+
62+
for example:
63+
64+
@EndpointExtension(uri = "/hello-world", method = Method.GET)
65+
public Mono<ServerResponse> helloWorld(ServerRequest request)
66+
{
67+
return ServerResponse.ok().body(Mono.just(Hello.builder().message("Hello world!").build()), Hello.class);
68+
}
69+
```
70+
- TODO: class(es) impelemting **LowcoderDatasource** interface
71+
72+
### LowcoderPlugin implementations
73+
74+
Methods of interest:
75+
- **pluginId()** - unique plugin ID - if a plugin with such ID is already loaded, subsequent plugins whith this ID will be ignored
76+
- **description()** - short plugin description
77+
- **load(ApplicationContext parentContext)** - is called during plugin startup - this is the place where you should completely initialize your plugin. If initialization fails, return false
78+
- **unload()** - is called during lowcoder API server shutdown - this is the place where you should release all resources
79+
- **endpoints()** - needs to contain all initialized **PluginEndpoints** you want to expose, for example:
80+
81+
```java
82+
@Override
83+
public List<PluginEndpoint> endpoints()
84+
{
85+
List<PluginEndpoint> endpoints = new ArrayList<>();
86+
87+
endpoints.add(new HelloWorldEndpoint());
88+
89+
return endpoints;
90+
}
91+
```
92+
- **pluginInfo()** - should return a record object with additional information about your plugin. It is serialized to JSON as part of the **/plugins** listing (see **"info"** object in this example):
93+
94+
```json
95+
[
96+
{
97+
"id": "example-plugin",
98+
"description": "Example plugin for lowcoder platform",
99+
"info": {}
100+
},
101+
{
102+
"id": "enterprise",
103+
"description": "Lowcoder enterprise plugin",
104+
"info": {
105+
"enabledFeatures": [
106+
"endpointApiUsage"
107+
]
108+
}
109+
}
110+
]
111+
```
112+
113+
## TODOs
114+
115+
1. Implement endpoint security - currently all plugin endpoints are public (probably by adding **security** attribute to **@EndpointExtension** and enforcing it)
116+
117+
118+
## QUESTIONS / CONSIDERATIONS
119+
120+
1. currently the plugin endpoints are prefixed with **/plugin/{pluginId}/** - this is hardcoded, do we want to make it configurable?
121+
122+
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
<?xml version="1.0"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
6+
<parent>
7+
<artifactId>lowcoder-root</artifactId>
8+
<groupId>org.lowcoder</groupId>
9+
<version>${revision}</version>
10+
</parent>
11+
12+
<modelVersion>4.0.0</modelVersion>
13+
<artifactId>lowcoder-dependencies</artifactId>
14+
<packaging>pom</packaging>
15+
16+
<dependencyManagement>
17+
<dependencies>
18+
<dependency>
19+
<groupId>org.springframework.boot</groupId>
20+
<artifactId>spring-boot-dependencies</artifactId>
21+
<version>3.1.2</version>
22+
<type>pom</type>
23+
<scope>import</scope>
24+
</dependency>
25+
26+
<dependency>
27+
<groupId>org.pf4j</groupId>
28+
<artifactId>pf4j</artifactId>
29+
<version>3.5.0</version>
30+
</dependency>
31+
32+
<dependency>
33+
<groupId>org.json</groupId>
34+
<artifactId>json</artifactId>
35+
<version>20230227</version>
36+
</dependency>
37+
38+
<dependency>
39+
<groupId>org.projectlombok</groupId>
40+
<artifactId>lombok</artifactId>
41+
<version>1.18.26</version>
42+
</dependency>
43+
44+
<dependency>
45+
<groupId>org.apache.commons</groupId>
46+
<artifactId>commons-text</artifactId>
47+
<version>1.10.0</version>
48+
</dependency>
49+
<dependency>
50+
<groupId>commons-io</groupId>
51+
<artifactId>commons-io</artifactId>
52+
<version>2.13.0</version>
53+
</dependency>
54+
<dependency>
55+
<groupId>org.glassfish</groupId>
56+
<artifactId>javax.el</artifactId>
57+
<version>3.0.0</version>
58+
</dependency>
59+
<dependency>
60+
<groupId>javax.el</groupId>
61+
<artifactId>javax.el-api</artifactId>
62+
<version>3.0.0</version>
63+
</dependency>
64+
65+
<dependency>
66+
<groupId>org.eclipse.jgit</groupId>
67+
<artifactId>org.eclipse.jgit</artifactId>
68+
<version>6.5.0.202303070854-r</version>
69+
</dependency>
70+
71+
<dependency>
72+
<groupId>org.apache.commons</groupId>
73+
<artifactId>commons-collections4</artifactId>
74+
<version>4.4</version>
75+
</dependency>
76+
<dependency>
77+
<groupId>com.google.guava</groupId>
78+
<artifactId>guava</artifactId>
79+
<version>30.0-jre</version>
80+
</dependency>
81+
82+
<dependency>
83+
<groupId>tv.twelvetone.rjson</groupId>
84+
<artifactId>rjson</artifactId>
85+
<version>1.3.1-SNAPSHOT</version>
86+
</dependency>
87+
<dependency>
88+
<groupId>org.jetbrains.kotlin</groupId>
89+
<artifactId>kotlin-stdlib-jdk7</artifactId>
90+
<version>1.6.21</version>
91+
</dependency>
92+
93+
<dependency>
94+
<groupId>com.jayway.jsonpath</groupId>
95+
<artifactId>json-path</artifactId>
96+
<version>2.7.0</version>
97+
</dependency>
98+
<dependency>
99+
<groupId>com.github.ben-manes.caffeine</groupId>
100+
<artifactId>caffeine</artifactId>
101+
<version>3.0.5</version>
102+
</dependency>
103+
<dependency>
104+
<groupId>es.moki.ratelimitj</groupId>
105+
<artifactId>ratelimitj-core</artifactId>
106+
<version>0.7.0</version>
107+
</dependency>
108+
<dependency>
109+
<groupId>com.github.spullara.mustache.java</groupId>
110+
<artifactId>compiler</artifactId>
111+
<version>0.9.6</version>
112+
</dependency>
113+
114+
<dependency>
115+
<groupId>es.moki.ratelimitj</groupId>
116+
<artifactId>ratelimitj-redis</artifactId>
117+
<version>0.7.0</version>
118+
</dependency>
119+
120+
<dependency>
121+
<groupId>io.projectreactor</groupId>
122+
<artifactId>reactor-core</artifactId>
123+
<version>3.4.29</version>
124+
</dependency>
125+
126+
<dependency>
127+
<groupId>org.pf4j</groupId>
128+
<artifactId>pf4j-spring</artifactId>
129+
<version>0.8.0</version>
130+
</dependency>
131+
132+
<dependency>
133+
<groupId>com.querydsl</groupId>
134+
<artifactId>querydsl-apt</artifactId>
135+
<version>5.0.0</version>
136+
</dependency>
137+
138+
<dependency>
139+
<groupId>io.sentry</groupId>
140+
<artifactId>sentry-spring-boot-starter</artifactId>
141+
<version>3.1.2</version>
142+
</dependency>
143+
144+
<dependency>
145+
<groupId>org.jgrapht</groupId>
146+
<artifactId>jgrapht-core</artifactId>
147+
<version>1.5.0</version>
148+
</dependency>
149+
150+
<dependency>
151+
<groupId>javax.xml.bind</groupId>
152+
<artifactId>jaxb-api</artifactId>
153+
<version>2.3.1</version>
154+
</dependency>
155+
<dependency>
156+
<groupId>javax.activation</groupId>
157+
<artifactId>activation</artifactId>
158+
<version>1.1.1</version>
159+
</dependency>
160+
<!-- no more than 2.3.3-->
161+
<dependency>
162+
<groupId>org.glassfish.jaxb</groupId>
163+
<artifactId>jaxb-runtime</artifactId>
164+
<version>2.3.3</version>
165+
</dependency>
166+
167+
<dependency>
168+
<groupId>com.github.cloudyrock.mongock</groupId>
169+
<artifactId>mongock-bom</artifactId>
170+
<version>4.3.8</version>
171+
<type>pom</type>
172+
<scope>import</scope>
173+
</dependency>
174+
175+
<dependency>
176+
<groupId>io.projectreactor.tools</groupId>
177+
<artifactId>blockhound</artifactId>
178+
<version>1.0.6.RELEASE</version>
179+
</dependency>
180+
181+
<dependency>
182+
<groupId>jakarta.servlet</groupId>
183+
<artifactId>jakarta.servlet-api</artifactId>
184+
<version>6.0.0</version>
185+
</dependency>
186+
187+
<dependency>
188+
<groupId>io.projectreactor</groupId>
189+
<artifactId>reactor-test</artifactId>
190+
<version>3.3.5.RELEASE</version>
191+
</dependency>
192+
<dependency>
193+
<groupId>org.apache.httpcomponents</groupId>
194+
<artifactId>httpclient</artifactId>
195+
<version>4.5.14</version>
196+
</dependency>
197+
<dependency>
198+
<groupId>de.flapdoodle.embed</groupId>
199+
<artifactId>de.flapdoodle.embed.mongo.spring30x</artifactId>
200+
<version>4.7.0</version>
201+
</dependency>
202+
<dependency>
203+
<groupId>org.mockito</groupId>
204+
<artifactId>mockito-inline</artifactId>
205+
<version>5.2.0</version>
206+
<scope>test</scope>
207+
</dependency>
208+
<dependency>
209+
<groupId>javax.validation</groupId>
210+
<artifactId>validation-api</artifactId>
211+
<version>2.0.1.Final</version>
212+
</dependency>
213+
</dependencies>
214+
</dependencyManagement>
215+
216+
217+
</project>
218+

server/api-service/lowcoder-domain/pom.xml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<artifactId>lowcoder-root</artifactId>
77
<groupId>org.lowcoder</groupId>
8-
<version>${revision}</version>
8+
<version>${revision}</version>
99
</parent>
1010
<modelVersion>4.0.0</modelVersion>
1111

@@ -273,4 +273,16 @@
273273
<maven.compiler.target>17</maven.compiler.target>
274274
</properties>
275275

276+
<dependencyManagement>
277+
<dependencies>
278+
<dependency>
279+
<groupId>org.lowcoder</groupId>
280+
<artifactId>lowcoder-dependencies</artifactId>
281+
<version>${revision}</version>
282+
<type>pom</type>
283+
<scope>import</scope>
284+
</dependency>
285+
</dependencies>
286+
</dependencyManagement>
287+
276288
</project>

server/api-service/lowcoder-infra/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,16 @@
137137
<maven.compiler.target>17</maven.compiler.target>
138138
</properties>
139139

140+
<dependencyManagement>
141+
<dependencies>
142+
<dependency>
143+
<groupId>org.lowcoder</groupId>
144+
<artifactId>lowcoder-dependencies</artifactId>
145+
<version>${revision}</version>
146+
<type>pom</type>
147+
<scope>import</scope>
148+
</dependency>
149+
</dependencies>
150+
</dependencyManagement>
151+
140152
</project>

server/api-service/lowcoder-plugins/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,14 @@
7979

8080
<dependencyManagement>
8181
<dependencies>
82+
<dependency>
83+
<groupId>org.lowcoder</groupId>
84+
<artifactId>lowcoder-dependencies</artifactId>
85+
<version>${revision}</version>
86+
<type>pom</type>
87+
<scope>import</scope>
88+
</dependency>
89+
8290
<dependency>
8391
<groupId>org.lowcoder</groupId>
8492
<artifactId>sqlBasedPlugin</artifactId>

0 commit comments

Comments
 (0)