Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit6e57773

Browse files
committed
WIP: new plugin system first iteration
1 parent4bd4373 commit6e57773

File tree

5 files changed

+249
-26
lines changed

5 files changed

+249
-26
lines changed

‎server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/configuration/PluginConfiguration.java‎

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
importjava.util.ArrayList;
44

55
importorg.lowcoder.api.framework.plugin.LowcoderPluginManager;
6-
importorg.lowcoder.api.framework.plugin.endpoint.PluginEndpointHandler;
6+
importorg.lowcoder.api.framework.plugin.PluginLoader;
7+
importorg.springframework.context.ApplicationContext;
78
importorg.springframework.context.annotation.Bean;
89
importorg.springframework.context.annotation.Configuration;
910
importorg.springframework.context.annotation.DependsOn;
@@ -19,17 +20,24 @@
1920
@Configuration
2021
publicclassPluginConfiguration
2122
{
23+
privatefinalApplicationContextapplicationContext;
24+
privatefinalPluginLoaderpluginLoader;
25+
26+
publicLowcoderPluginManagerlowcoderPluginManager()
27+
{
28+
returnnewLowcoderPluginManager(applicationContext,pluginLoader);
29+
}
2230

2331
@SuppressWarnings("unchecked")
2432
@Bean
2533
@DependsOn("lowcoderPluginManager")
26-
RouterFunction<?>pluginEndpoints(LowcoderPluginManagerpluginManager,PluginEndpointHandlerpluginEndpointHandler)
34+
RouterFunction<?>pluginEndpoints(LowcoderPluginManagerpluginManager)
2735
{
2836
RouterFunction<?>pluginsList =RouterFunctions.route()
2937
.GET(RequestPredicates.path("/plugins"),req ->ServerResponse.ok().body(Mono.just(pluginManager.getLoadedPluginsInfo()),ArrayList.class))
3038
.build();
3139

32-
RouterFunction<?>endpoints =pluginEndpointHandler.registeredEndpoints().stream()
40+
RouterFunction<?>endpoints =pluginManager.getEndpoints().stream()
3341
.map(r-> (RouterFunction<ServerResponse>)r)
3442
.reduce((o,r )-> (RouterFunction<ServerResponse>)o.andOther(r))
3543
.orElse(null);
Lines changed: 193 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,217 @@
11
packageorg.lowcoder.api.framework.plugin;
22

3+
importstaticorg.springframework.web.reactive.function.server.RequestPredicates.DELETE;
4+
importstaticorg.springframework.web.reactive.function.server.RequestPredicates.GET;
5+
importstaticorg.springframework.web.reactive.function.server.RequestPredicates.OPTIONS;
6+
importstaticorg.springframework.web.reactive.function.server.RequestPredicates.PATCH;
7+
importstaticorg.springframework.web.reactive.function.server.RequestPredicates.POST;
8+
importstaticorg.springframework.web.reactive.function.server.RequestPredicates.PUT;
9+
importstaticorg.springframework.web.reactive.function.server.RouterFunctions.route;
10+
11+
importjava.lang.reflect.InvocationTargetException;
12+
importjava.lang.reflect.Method;
13+
importjava.util.ArrayList;
14+
importjava.util.Comparator;
15+
importjava.util.LinkedHashMap;
16+
importjava.util.List;
317
importjava.util.Map;
418

19+
importorg.apache.commons.collections4.CollectionUtils;
20+
importorg.apache.commons.lang3.StringUtils;
21+
importorg.lowcoder.plugin.EndpointExtension;
522
importorg.lowcoder.plugin.LowcoderPlugin;
6-
importorg.lowcoder.sdk.config.CommonConfig;
7-
importorg.springframework.boot.system.ApplicationHome;
8-
importorg.springframework.context.ConfigurableApplicationContext;
23+
importorg.lowcoder.plugin.PluginEndpoint;
24+
importorg.lowcoder.sdk.exception.BaseException;
25+
importorg.springframework.context.ApplicationContext;
26+
importorg.springframework.core.ResolvableType;
927
importorg.springframework.stereotype.Component;
28+
importorg.springframework.web.reactive.function.server.RequestPredicate;
29+
importorg.springframework.web.reactive.function.server.RouterFunction;
30+
importorg.springframework.web.reactive.function.server.ServerRequest;
31+
importorg.springframework.web.reactive.function.server.ServerResponse;
1032

1133
importjakarta.annotation.PostConstruct;
34+
importjakarta.annotation.PreDestroy;
1235
importlombok.RequiredArgsConstructor;
1336
importlombok.extern.slf4j.Slf4j;
37+
importreactor.core.publisher.Mono;
1438

1539
@RequiredArgsConstructor
1640
@Component
1741
@Slf4j
1842
publicclassLowcoderPluginManager
1943
{
20-
privatefinalConfigurableApplicationContextapplicationContext;
21-
privatefinalCommonConfigcommon;
22-
privatefinalApplicationHomeapplicationHome;
23-
24-
privateMap<String,LowcoderPlugin>plugins;
25-
44+
privatefinalApplicationContextapplicationContext;
45+
privatefinalPluginLoaderpluginLoader;
2646

47+
privateMap<String,LowcoderPlugin>plugins =newLinkedHashMap<>();
48+
privateList<RouterFunction<ServerResponse>>routes =newArrayList<>();
49+
2750
@PostConstruct
2851
privatevoidloadPlugins()
2952
{
53+
registerPlugins();
54+
List<LowcoderPlugin>sorted =newArrayList<>(plugins.values());
55+
sorted.sort(Comparator.comparing(LowcoderPlugin::loadOrder));
56+
57+
for (LowcoderPluginplugin :sorted)
58+
{
59+
if (plugin.load(applicationContext))
60+
{
61+
log.info("Plugin [{}] loaded successfully.",plugin.pluginId());
62+
registerEndpoints(plugin);
63+
}
64+
}
65+
}
66+
67+
@PreDestroy
68+
publicvoidunloadPlugins()
69+
{
70+
for (LowcoderPluginplugin :plugins.values())
71+
{
72+
try
73+
{
74+
plugin.unload();
75+
}
76+
catch(Throwablecause)
77+
{
78+
log.warn("Error unloading plugin: {}!",plugin.pluginId(),cause);
79+
}
80+
}
81+
}
82+
83+
publicList<RouterFunction<ServerResponse>>getEndpoints()
84+
{
85+
returnthis.routes;
86+
}
87+
88+
publicList<PluginInfo>getLoadedPluginsInfo()
89+
{
90+
List<PluginInfo>infos =newArrayList<>();
91+
for (LowcoderPluginplugin :plugins.values())
92+
{
93+
infos.add(newPluginInfo(plugin.pluginId(),plugin.description(),plugin.pluginInfo()));
94+
}
95+
returninfos;
96+
}
97+
98+
privatevoidregisterPlugins()
99+
{
100+
List<LowcoderPlugin>loaded =pluginLoader.loadPlugins();
101+
if (CollectionUtils.isNotEmpty(loaded))
102+
{
103+
for (LowcoderPluginplugin :loaded)
104+
{
105+
if (!plugins.containsKey(plugin.pluginId()))
106+
{
107+
log.info("Registered plugin: {} ({})",plugin.pluginId(),plugin.getClass().getName());
108+
plugins.put(plugin.pluginId(),plugin);
109+
}
110+
else
111+
{
112+
log.warn("Plugin {} already registered (from: {}), skipping {}.",plugin.pluginId(),
113+
plugins.get(plugin.pluginId()).getClass().getName(),
114+
plugin.getClass().getName());
115+
}
116+
}
117+
}
118+
}
119+
120+
121+
privatevoidregisterEndpoints(LowcoderPluginplugin)
122+
{
123+
if (CollectionUtils.isNotEmpty(plugin.endpoints()))
124+
{
125+
for (PluginEndpointendpoint :plugin.endpoints())
126+
{
127+
Method[]handlers =endpoint.getClass().getDeclaredMethods();
128+
if (handlers !=null &&handlers.length >0)
129+
{
130+
for (Methodhandler :handlers)
131+
{
132+
registerEndpointHandler(plugin,endpoint,handler);
133+
}
134+
}
135+
}
136+
}
137+
}
138+
139+
@SuppressWarnings("unchecked")
140+
privatevoidregisterEndpointHandler(LowcoderPluginplugin,PluginEndpointendpoint,Methodhandler)
141+
{
142+
if (handler.isAnnotationPresent(EndpointExtension.class))
143+
{
144+
if (checkHandlerMethod(handler))
145+
{
146+
147+
EndpointExtensionendpointMeta =handler.getAnnotation(EndpointExtension.class);
148+
routes.add(route(createRequestPredicate(plugin,endpointMeta),req -> {
149+
Mono<ServerResponse>result =null;
150+
try
151+
{
152+
result = (Mono<ServerResponse>)handler.invoke(endpoint,req);
153+
}
154+
catch (IllegalAccessException |InvocationTargetExceptioncause)
155+
{
156+
thrownewBaseException("Error running handler for [ " +endpointMeta.method() +": " +endpointMeta.uri() +"] !");
157+
}
158+
returnresult;
159+
})
160+
);
161+
log.info("Registered plugin endpoint: {} -> {} -> {}: {}",plugin.pluginId(),endpoint.getClass().getSimpleName(),endpointMeta.method(),endpointMeta.uri());
162+
}
163+
else
164+
{
165+
log.error("Cannot register plugin endpoint: {} -> {} -> {}! Handler method must be defined as: public Mono<ServerResponse> {}(ServerRequest request)",plugin.pluginId(),endpoint.getClass().getSimpleName(),handler.getName(),handler.getName());
166+
}
167+
}
168+
}
169+
170+
171+
privatebooleancheckHandlerMethod(Methodmethod)
172+
{
173+
ResolvableTypereturnType =ResolvableType.forMethodReturnType(method);
30174

175+
return (returnType.isAssignableFrom(Mono.class)
176+
&&returnType.getGenerics().length ==1
177+
&&returnType.getGeneric(0).isAssignableFrom(ServerResponse.class)
178+
&&method.getParameterCount() ==1
179+
&&method.getParameterTypes()[0].isAssignableFrom(ServerRequest.class)
180+
);
31181
}
32182

183+
privateRequestPredicatecreateRequestPredicate(LowcoderPluginplugin,EndpointExtensionendpoint)
184+
{
185+
StringbasePath ="/plugins/" +plugin.pluginId();
186+
187+
switch(endpoint.method())
188+
{
189+
caseGET:
190+
returnGET(pluginEndpointUri(basePath,endpoint.uri()));
191+
casePOST:
192+
returnPOST(pluginEndpointUri(basePath,endpoint.uri()));
193+
casePUT:
194+
returnPUT(pluginEndpointUri(basePath,endpoint.uri()));
195+
casePATCH:
196+
returnPATCH(pluginEndpointUri(basePath,endpoint.uri()));
197+
caseDELETE:
198+
returnDELETE(pluginEndpointUri(basePath,endpoint.uri()));
199+
caseOPTIONS:
200+
returnOPTIONS(pluginEndpointUri(basePath,endpoint.uri()));
201+
}
202+
returnnull;
203+
}
204+
205+
privateStringpluginEndpointUri(StringbasePath,Stringuri)
206+
{
207+
returnStringUtils.join(basePath,StringUtils.prependIfMissing(uri,"/"));
208+
}
209+
210+
211+
privaterecordPluginInfo(
212+
Stringid,
213+
Stringdescription,
214+
Objectinfo
215+
) {}
216+
33217
}

‎server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/plugin/PathBasedPluginLoader.java‎

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
11
packageorg.lowcoder.api.framework.plugin;
22

33
importjava.io.IOException;
4+
importjava.net.URL;
5+
importjava.net.URLClassLoader;
46
importjava.nio.file.Files;
57
importjava.nio.file.Path;
68
importjava.util.ArrayList;
7-
importjava.util.Iterator;
89
importjava.util.List;
9-
importjava.util.ServiceLoader;
10+
importjava.util.Set;
1011

1112
importorg.apache.commons.collections4.CollectionUtils;
1213
importorg.apache.commons.lang3.StringUtils;
13-
importorg.lowcoder.plugin.api.LowcoderPlugin;
14+
importorg.lowcoder.plugin.LowcoderPlugin;
1415
importorg.lowcoder.sdk.config.CommonConfig;
16+
importorg.reflections.Reflections;
17+
importorg.reflections.scanners.SubTypesScanner;
18+
importorg.reflections.scanners.TypeAnnotationsScanner;
19+
importorg.reflections.util.ClasspathHelper;
20+
importorg.reflections.util.ConfigurationBuilder;
21+
importorg.springframework.beans.factory.BeanClassLoaderAware;
1522
importorg.springframework.boot.system.ApplicationHome;
1623
importorg.springframework.stereotype.Component;
1724

@@ -21,10 +28,12 @@
2128
@Slf4j
2229
@RequiredArgsConstructor
2330
@Component
24-
publicclassPathBasedPluginLoaderimplementsPluginLoader
31+
publicclassPathBasedPluginLoaderimplementsPluginLoader,BeanClassLoaderAware
2532
{
2633
privatefinalCommonConfigcommon;
2734
privatefinalApplicationHomeapplicationHome;
35+
36+
privateClassLoaderbeanClassLoader;
2837

2938
@Override
3039
publicList<LowcoderPlugin>loadPlugins()
@@ -49,6 +58,7 @@ public List<LowcoderPlugin> loadPlugins()
4958
{
5059
for (LowcoderPluginplugin :loadedPlugins)
5160
{
61+
log.debug(" - loaded plugin: {} :: {}",plugin.pluginId(),plugin.description());
5262
plugins.add(plugin);
5363
}
5464
}
@@ -94,25 +104,38 @@ protected List<String> findPluginCandidates(Path pluginsDir)
94104
returnpluginCandidates;
95105
}
96106

97-
protectedList<LowcoderPlugin>loadPluginCandidates(StringpluginJar)
107+
protectedList<LowcoderPlugin>loadPluginCandidates(StringpluginsDir)
98108
{
99109
List<LowcoderPlugin>pluginCandidates =newArrayList<>();
100-
110+
111+
URLClassLoadertestClassLoader =null;
112+
101113
try
102114
{
103-
PathpluginPath =Path.of(pluginJar);
104-
PluginClassLoaderpluginClassLoader =newPluginClassLoader(pluginPath.getFileName().toString(),pluginPath);
115+
testClassLoader =URLClassLoader.newInstance(newURL[] {
116+
Path.of(pluginsDir).toUri().toURL()
117+
},beanClassLoader);
118+
119+
Reflectionsreflections =newReflections(newConfigurationBuilder()
120+
.addClassLoader(testClassLoader)
121+
.addUrls(ClasspathHelper.forClassLoader(testClassLoader))
122+
.setScanners(newSubTypesScanner(false),newTypeAnnotationsScanner())
123+
);
105124

106-
ServiceLoader<LowcoderPlugin>pluginServices =ServiceLoader.load(LowcoderPlugin.class,pluginClassLoader);
107-
if (pluginServices !=null)
125+
Set<Class<?extendsLowcoderPlugin>>found =reflections.getSubTypesOf(LowcoderPlugin.class);
126+
for (Class<?extendsLowcoderPlugin>pluginClass :found)
108127
{
109-
Iterator<LowcoderPlugin>pluginIterator =pluginServices.iterator();
110-
while(pluginIterator.hasNext())
128+
log.debug(" - found plugin: {}",pluginClass.getName());
129+
try
111130
{
112-
LowcoderPluginplugin =pluginIterator.next();
131+
LowcoderPluginplugin =pluginClass.getConstructor().newInstance();
113132
log.debug(" - loaded plugin: {} - {}",plugin.pluginId(),plugin.description());
114133
pluginCandidates.add(plugin);
115134
}
135+
catch(ThrowableloadFail)
136+
{
137+
log.error(" - error loading plugin: {}!",pluginClass.getName(),loadFail);
138+
}
116139
}
117140
}
118141
catch(Throwablecause)
@@ -137,4 +160,11 @@ private Path getAbsoluteNormalizedPath(String path)
137160

138161
returnnull;
139162
}
163+
164+
165+
@Override
166+
publicvoidsetBeanClassLoader(ClassLoaderclassLoader)
167+
{
168+
this.beanClassLoader =classLoader;
169+
}
140170
}

‎server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/plugin/PluginLoader.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
importjava.util.List;
44

5-
importorg.lowcoder.plugin.api.LowcoderPlugin;
5+
importorg.lowcoder.plugin.LowcoderPlugin;
66

77
publicinterfacePluginLoader
88
{

‎server/api-service/lowcoder-server/src/main/resources/selfhost/ce/application.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ common:
5252
max-query-timeout:${LOWCODER_MAX_QUERY_TIMEOUT:120}
5353
plugin-dirs:
5454
-plugins
55+
-/tmp/plugins
5556

5657
material:
5758
mongodb-grid-fs:

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp