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

Commitd394e76

Browse files
committed
WIP: new plugin system first iteration
1 parente582b1e commitd394e76

File tree

5 files changed

+244
-30
lines changed

5 files changed

+244
-30
lines changed

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@
2020
@Configuration
2121
publicclassPluginConfiguration
2222
{
23-
//private final ApplicationContext applicationContext;
24-
//private final PluginLoader pluginLoader;
25-
//
26-
//public LowcoderPluginManager lowcoderPluginManager()
27-
//{
28-
//return new LowcoderPluginManager(applicationContext, pluginLoader);
29-
//}
23+
privatefinalApplicationContextapplicationContext;
24+
privatefinalPluginLoaderpluginLoader;
25+
26+
publicLowcoderPluginManagerlowcoderPluginManager()
27+
{
28+
returnnewLowcoderPluginManager(applicationContext,pluginLoader);
29+
}
3030

3131
@SuppressWarnings("unchecked")
3232
@Bean
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: 42 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,26 +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<>();
100110

101-
PluginJarClassLoaderpluginClassLoader =null;
111+
URLClassLoadertestClassLoader =null;
112+
102113
try
103114
{
104-
pluginClassLoader =newPluginJarClassLoader(getClass().getClassLoader(),Path.of(pluginJar));
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-
107-
ServiceLoader<LowcoderPlugin>pluginServices =ServiceLoader.load(LowcoderPlugin.class,pluginClassLoader);
108-
if (pluginServices !=null )
125+
Set<Class<?extendsLowcoderPlugin>>found =reflections.getSubTypesOf(LowcoderPlugin.class);
126+
for (Class<?extendsLowcoderPlugin>pluginClass :found)
109127
{
110-
Iterator<LowcoderPlugin>pluginIterator =pluginServices.iterator();
111-
while(pluginIterator.hasNext())
128+
log.debug(" - found plugin: {}",pluginClass.getName());
129+
try
112130
{
113-
LowcoderPluginplugin =pluginIterator.next();
131+
LowcoderPluginplugin =pluginClass.getConstructor().newInstance();
114132
log.debug(" - loaded plugin: {} - {}",plugin.pluginId(),plugin.description());
115133
pluginCandidates.add(plugin);
116134
}
135+
catch(ThrowableloadFail)
136+
{
137+
log.error(" - error loading plugin: {}!",pluginClass.getName(),loadFail);
138+
}
117139
}
118140
}
119141
catch(Throwablecause)
@@ -138,4 +160,11 @@ private Path getAbsoluteNormalizedPath(String path)
138160

139161
returnnull;
140162
}
163+
164+
165+
@Override
166+
publicvoidsetBeanClassLoader(ClassLoaderclassLoader)
167+
{
168+
this.beanClassLoader =classLoader;
169+
}
141170
}

‎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