Skip to content

Commit

Permalink
Support plugin management and dynamic plugin installation with extens…
Browse files Browse the repository at this point in the history
…ions (#984)

* plugin management for flink poc

* support dynamic install plugin

* fix SofaArkBootstrap.launch

* support dynamic plugin installation

* fix plugin export when finding export class

* support all plugin are visible for biz

* support dynamic biz classpath extension

* rm unuse code

* ArkLoaderException.cause cache,not use ClassNotFoundException.ex field

* getBizUcp只加载依赖的plugin

* 1.biz安装支持多版本,版本从params传入,默认使用pom文件中的版本
2.biz的dependent-plugins参数通过params传入
3.plugin支持安装的时候传入name
4.BizFactoryServiceImpl#getBizUcp 这里构造biz的ucp的时候 把所有plugin都add进来了 这里应该是只需要加载biz依赖的plugin
5.ArkClient提供一个checkplugin/biz是否存在的接口

* format

* fix ClassLoaderTest

* fix ut

* fix code review

* 默认biz可见全部plugins,sofa.ark.biz.specify.dependent.plugins.enable=true时,指定可见范围

* format

* Revert "fix SofaArkBootstrap.launch"

This reverts commit 0bbd4e3.

* fix ut

* format

* format

---------

Co-authored-by: yuanyuan <[email protected]>
  • Loading branch information
yuanyuancin and yuanyuan2021 authored Jan 9, 2025
1 parent 41fe026 commit d1c277f
Show file tree
Hide file tree
Showing 22 changed files with 882 additions and 163 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import com.alipay.sofa.ark.spi.event.biz.BeforeBizStopEvent;
import com.alipay.sofa.ark.spi.model.Biz;
import com.alipay.sofa.ark.spi.model.BizState;
import com.alipay.sofa.ark.spi.model.Plugin;
import com.alipay.sofa.ark.spi.service.biz.BizManagerService;
import com.alipay.sofa.ark.spi.service.event.EventAdminService;

Expand All @@ -49,6 +50,7 @@
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashSet;
Expand Down Expand Up @@ -116,6 +118,8 @@ public class BizModel implements Biz {

private List<BizStateRecord> bizStateRecords = new CopyOnWriteArrayList<>();

private Set<Plugin> dependentPlugins = new HashSet<>();

public BizModel setBizName(String bizName) {
AssertUtils.isFalse(StringUtils.isEmpty(bizName), "Biz Name must not be empty!");
this.bizName = bizName;
Expand Down Expand Up @@ -234,6 +238,15 @@ private void addStateChangeLog(StateChangeReason reason, String message) {
bizStateRecords.add(new BizStateRecord(new Date(), bizState, reason, message));
}

public Set<Plugin> getDependentPlugins() {
return dependentPlugins;
}

public BizModel setDependentPlugins(Set<Plugin> dependentPlugins) {
this.dependentPlugins = dependentPlugins;
return this;
}

@Override
public String getBizName() {
return bizName;
Expand Down Expand Up @@ -663,4 +676,39 @@ private static String getStackTraceAsString(Throwable throwable) {
throwable.printStackTrace(pw);
return sw.toString();
}

/* export class and classloader relationship cache */
private ConcurrentHashMap<String, Plugin> exportClassAndClassLoaderMap = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, Plugin> exportNodeAndClassLoaderMap = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, Plugin> exportStemAndClassLoaderMap = new ConcurrentHashMap<>();

/* export cache and classloader relationship cache */
private ConcurrentHashMap<String, List<Plugin>> exportResourceAndClassLoaderMap = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, List<Plugin>> exportPrefixStemResourceAndClassLoaderMap = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, List<Plugin>> exportSuffixStemResourceAndClassLoaderMap = new ConcurrentHashMap<>();

public ConcurrentHashMap<String, Plugin> getExportClassAndClassLoaderMap() {
return exportClassAndClassLoaderMap;
}

public ConcurrentHashMap<String, Plugin> getExportNodeAndClassLoaderMap() {
return exportNodeAndClassLoaderMap;
}

public ConcurrentHashMap<String, Plugin> getExportStemAndClassLoaderMap() {
return exportStemAndClassLoaderMap;
}

public ConcurrentHashMap<String, List<Plugin>> getExportResourceAndClassLoaderMap() {
return exportResourceAndClassLoaderMap;
}

public ConcurrentHashMap<String, List<Plugin>> getExportPrefixStemResourceAndClassLoaderMap() {
return exportPrefixStemResourceAndClassLoaderMap;
}

public ConcurrentHashMap<String, List<Plugin>> getExportSuffixStemResourceAndClassLoaderMap() {
return exportSuffixStemResourceAndClassLoaderMap;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.alipay.sofa.ark.spi.service.biz.BizManagerService;
import com.alipay.sofa.ark.spi.service.event.EventAdminService;
import com.alipay.sofa.ark.spi.service.injection.InjectionService;
import com.alipay.sofa.ark.spi.service.plugin.PluginFactoryService;
import com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;
import com.google.inject.Binding;
import com.google.inject.Guice;
Expand Down Expand Up @@ -92,6 +93,7 @@ public void start() throws ArkRuntimeException {
ArkClient.setInjectionService(getService(InjectionService.class));
ArkClient.setEventAdminService(getService(EventAdminService.class));
ArkClient.setPluginManagerService(getService(PluginManagerService.class));
ArkClient.setPluginFactoryService(getService(PluginFactoryService.class));
ArkClient.setArguments(arguments);
ArkLoggerFactory.getDefaultLogger().info("Finish to start ArkServiceContainer");
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,8 @@
import com.alipay.sofa.ark.spi.archive.Archive;
import com.alipay.sofa.ark.spi.archive.BizArchive;
import com.alipay.sofa.ark.spi.constant.Constants;
import com.alipay.sofa.ark.spi.model.Biz;
import com.alipay.sofa.ark.spi.model.*;
import com.alipay.sofa.ark.spi.model.BizInfo.StateChangeReason;
import com.alipay.sofa.ark.spi.model.BizOperation;
import com.alipay.sofa.ark.spi.model.BizState;
import com.alipay.sofa.ark.spi.model.Plugin;
import com.alipay.sofa.ark.spi.service.biz.BizFactoryService;
import com.alipay.sofa.ark.spi.service.plugin.PluginManagerService;
import com.google.inject.Inject;
Expand All @@ -48,9 +45,11 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.stream.Stream;

import static com.alipay.sofa.ark.spi.constant.Constants.*;

Expand All @@ -68,15 +67,60 @@ public class BizFactoryServiceImpl implements BizFactoryService {

@Override
public Biz createBiz(BizArchive bizArchive) throws IOException {
return createBiz(bizArchive, new BizConfig());
}

@Override
public Biz createBiz(BizArchive bizArchive, URL[] extensionUrls) throws IOException {
BizConfig bizConfig = new BizConfig();
bizConfig.setExtensionUrls(extensionUrls);
return createBiz(bizArchive, bizConfig);
}

@Override
public Biz createBiz(File file) throws IOException {
BizArchive bizArchive = prepareBizArchive(file);
return createBiz(bizArchive, new BizConfig());
}

@Override
public Biz createBiz(File file, URL[] extensionUrls) throws IOException {
BizArchive bizArchive = prepareBizArchive(file);
BizConfig bizConfig = new BizConfig();
bizConfig.setExtensionUrls(extensionUrls);
return createBiz(bizArchive, bizConfig);
}

@Override
public Biz createBiz(BizOperation bizOperation, File file) throws IOException {
BizArchive bizArchive = prepareBizArchive(file);
BizConfig bizConfig = new BizConfig();
bizConfig.setSpecifiedVersion(bizOperation.getBizVersion());
return createBiz(bizArchive, bizConfig);
}

@Override
public Biz createBiz(File file, BizConfig bizConfig) throws IOException {
BizArchive bizArchive = prepareBizArchive(file);
return createBiz(bizArchive, bizConfig);
}

@Override
public Biz createBiz(BizArchive bizArchive, BizConfig bizConfig) throws IOException {
AssertUtils.isTrue(isArkBiz(bizArchive), "Archive must be a ark biz!");
BizModel bizModel = new BizModel();
AssertUtils.isTrue(bizConfig != null, "BizConfig must not be null!");

Attributes manifestMainAttributes = bizArchive.getManifest().getMainAttributes();
String mainClass = manifestMainAttributes.getValue(MAIN_CLASS_ATTRIBUTE);
String startClass = manifestMainAttributes.getValue(START_CLASS_ATTRIBUTE);
BizModel bizModel = new BizModel();
bizModel
.setBizState(BizState.RESOLVED, StateChangeReason.CREATED)
.setBizName(manifestMainAttributes.getValue(ARK_BIZ_NAME))
.setBizVersion(manifestMainAttributes.getValue(ARK_BIZ_VERSION))
.setBizVersion(
!StringUtils.isEmpty(bizConfig.getSpecifiedVersion()) ? bizConfig
.getSpecifiedVersion() : manifestMainAttributes.getValue(ARK_BIZ_VERSION))
.setBizUrl(!(bizArchive instanceof DirectoryBizArchive) ? bizArchive.getUrl() : null)
.setMainClass(!StringUtils.isEmpty(startClass) ? startClass : mainClass)
.setPriority(manifestMainAttributes.getValue(PRIORITY_ATTRIBUTE))
.setWebContextPath(manifestMainAttributes.getValue(WEB_CONTEXT_PATH))
Expand All @@ -87,22 +131,49 @@ public Biz createBiz(BizArchive bizArchive) throws IOException {
getInjectDependencies(manifestMainAttributes.getValue(INJECT_PLUGIN_DEPENDENCIES)))
.setInjectExportPackages(manifestMainAttributes.getValue(INJECT_EXPORT_PACKAGES))
.setDeclaredLibraries(manifestMainAttributes.getValue(DECLARED_LIBRARIES))
.setClassPath(bizArchive.getUrls()).setPluginClassPath(getPluginURLs());
.setClassPath(getMergedBizClassPath(bizArchive.getUrls(), bizConfig.getExtensionUrls()));

if (!(bizArchive instanceof DirectoryBizArchive)) {
bizModel.setBizUrl(bizArchive.getUrl());
// prepare dependent plugins and plugin export map
List<String> dependentPlugins = bizConfig.getDependentPlugins();
if (dependentPlugins == null || dependentPlugins.isEmpty()) {
dependentPlugins = StringUtils.strToList(
manifestMainAttributes.getValue("dependent-plugins"),
Constants.MANIFEST_VALUE_SPLIT);
}
resolveExportMapIfNecessary(bizModel, dependentPlugins);

// must be after prepare dependent plugins
bizModel.setPluginClassPath(getPluginURLs(bizModel));

// create biz classloader
BizClassLoader bizClassLoader = new BizClassLoader(bizModel.getIdentity(),
getBizUcp(bizModel.getClassPath()), bizArchive instanceof ExplodedBizArchive
|| bizArchive instanceof DirectoryBizArchive);
getBizUcp(bizModel), bizArchive instanceof ExplodedBizArchive
|| bizArchive instanceof DirectoryBizArchive);
bizClassLoader.setBizModel(bizModel);
bizModel.setClassLoader(bizClassLoader);

// set biz work dir
if (bizModel.getBizUrl() != null) {
bizModel.setBizTempWorkDir(new File(bizModel.getBizUrl().getFile()));
}

return bizModel;
}

@Override
public Biz createBiz(File file) throws IOException {
public Biz createEmbedMasterBiz(ClassLoader masterClassLoader) {
BizModel bizModel = new BizModel();
bizModel.setBizState(BizState.RESOLVED, StateChangeReason.CREATED)
.setBizName(ArkConfigs.getStringValue(MASTER_BIZ)).setBizVersion("1.0.0")
.setMainClass("embed main").setPriority("100").setWebContextPath("/")
.setDenyImportPackages(null).setDenyImportClasses(null).setDenyImportResources(null)
.setInjectPluginDependencies(new HashSet<>()).setInjectExportPackages(null)
.setClassPath(ClassLoaderUtils.getURLs(masterClassLoader))
.setClassLoader(masterClassLoader);
return bizModel;
}

private BizArchive prepareBizArchive(File file) throws IOException {
BizArchive bizArchive;
boolean unpackBizWhenInstall = Boolean.parseBoolean(ArkConfigs.getStringValue(
UNPACK_BIZ_WHEN_INSTALL, "true"));
Expand All @@ -121,35 +192,56 @@ public Biz createBiz(File file) throws IOException {
JarFileArchive jarFileArchive = new JarFileArchive(bizFile);
bizArchive = new JarBizArchive(jarFileArchive);
}
BizModel biz = (BizModel) createBiz(bizArchive);
biz.setBizTempWorkDir(file);
return biz;
return bizArchive;
}

@Override
public Biz createBiz(BizOperation bizOperation, File file) throws IOException {
BizModel biz = (BizModel) createBiz(file);
if (bizOperation != null && !StringUtils.isEmpty(bizOperation.getBizVersion())) {
biz.setBizVersion(bizOperation.getBizVersion());
if (biz.getBizClassLoader() instanceof BizClassLoader) {
BizClassLoader bizClassLoader = (BizClassLoader) (biz.getBizClassLoader());
bizClassLoader.setBizIdentity(biz.getIdentity());
}
private URL[] getMergedBizClassPath(URL[] bizArchiveUrls, URL[] extensionUrls) {
if (extensionUrls == null || extensionUrls.length == 0) {
return bizArchiveUrls;
}
return biz;
return Stream.concat(Arrays.stream(bizArchiveUrls), Arrays.stream(extensionUrls)).toArray(URL[]::new);
}

@Override
public Biz createEmbedMasterBiz(ClassLoader masterClassLoader) {
BizModel bizModel = new BizModel();
bizModel.setBizState(BizState.RESOLVED, StateChangeReason.CREATED)
.setBizName(ArkConfigs.getStringValue(MASTER_BIZ)).setBizVersion("1.0.0")
.setMainClass("embed main").setPriority("100").setWebContextPath("/")
.setDenyImportPackages(null).setDenyImportClasses(null).setDenyImportResources(null)
.setInjectPluginDependencies(new HashSet<>()).setInjectExportPackages(null)
.setClassPath(ClassLoaderUtils.getURLs(masterClassLoader))
.setClassLoader(masterClassLoader);
return bizModel;
private void resolveExportMapIfNecessary(BizModel bizModel, List<String> dependentPlugins) {
Set<Plugin> plugins = new HashSet<>();
if (ArkConfigs.isBizSpecifyDependentPluginsEnable()) {
if (dependentPlugins != null && !dependentPlugins.isEmpty()) {
for (String pluginName : dependentPlugins) {
Plugin plugin = pluginManagerService.getPluginByName(pluginName);
plugins.add(plugin);
}
}
} else {
plugins.addAll(pluginManagerService.getPluginsInOrder());
}

bizModel.setDependentPlugins(plugins);
for (Plugin plugin : plugins) {
for (String exportIndex : plugin.getExportPackageNodes()) {
bizModel.getExportNodeAndClassLoaderMap().putIfAbsent(exportIndex, plugin);
}
for (String exportIndex : plugin.getExportPackageStems()) {
bizModel.getExportStemAndClassLoaderMap().putIfAbsent(exportIndex, plugin);
}
for (String exportIndex : plugin.getExportClasses()) {
bizModel.getExportClassAndClassLoaderMap().putIfAbsent(exportIndex, plugin);
}
for (String resource : plugin.getExportResources()) {
bizModel.getExportResourceAndClassLoaderMap().putIfAbsent(resource,
new LinkedList<>());
bizModel.getExportResourceAndClassLoaderMap().get(resource).add(plugin);
}
for (String resource : plugin.getExportPrefixResourceStems()) {
bizModel.getExportPrefixStemResourceAndClassLoaderMap().putIfAbsent(resource,
new LinkedList<>());
bizModel.getExportPrefixStemResourceAndClassLoaderMap().get(resource).add(plugin);
}
for (String resource : plugin.getExportSuffixResourceStems()) {
bizModel.getExportSuffixStemResourceAndClassLoaderMap().putIfAbsent(resource,
new LinkedList<>());
bizModel.getExportSuffixStemResourceAndClassLoaderMap().get(resource).add(plugin);
}
}
}

private Set<String> getInjectDependencies(String injectPluginDependencies) {
Expand All @@ -173,16 +265,16 @@ public boolean matches(Archive.Entry entry) {
});
}

private URL[] getBizUcp(URL[] bizClassPath) {
private URL[] getBizUcp(BizModel bizModel) {
List<URL> bizUcp = new ArrayList<>();
bizUcp.addAll(Arrays.asList(bizClassPath));
bizUcp.addAll(Arrays.asList(getPluginURLs()));
bizUcp.addAll(Arrays.asList(bizModel.getClassPath()));
bizUcp.addAll(Arrays.asList(getPluginURLs(bizModel)));
return bizUcp.toArray(new URL[bizUcp.size()]);
}

private URL[] getPluginURLs() {
private URL[] getPluginURLs(BizModel bizModel) {
List<URL> pluginUrls = new ArrayList<>();
for (Plugin plugin : pluginManagerService.getPluginsInOrder()) {
for (Plugin plugin : bizModel.getDependentPlugins()) {
pluginUrls.add(plugin.getPluginURL());
}
return pluginUrls.toArray(new URL[pluginUrls.size()]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,13 @@ private byte[] getClassBytesFromJar(String jarFilePath, String className) throws

private Class<?> doResolveExportClass(String name) {
if (shouldFindExportedClass(name)) {
ClassLoader importClassLoader = classloaderService.findExportClassLoader(name);
ClassLoader importClassLoader = null;
if (this instanceof BizClassLoader) {
importClassLoader = classloaderService.findExportClassLoaderByBiz(
((BizClassLoader) this).getBizModel(), name);
} else if (this instanceof PluginClassLoader) {
importClassLoader = classloaderService.findExportClassLoader(name);
}
if (importClassLoader != null) {
try {
Class<?> clazz = importClassLoader.loadClass(name);
Expand Down Expand Up @@ -555,12 +561,18 @@ protected Class<?> resolveJavaAgentClass(String name) {
*/
protected URL getExportResource(String resourceName) {
if (shouldFindExportedResource(resourceName)) {
URL url;
List<ClassLoader> exportResourceClassLoadersInOrder = classloaderService
.findExportResourceClassLoadersInOrder(resourceName);
List<ClassLoader> exportResourceClassLoadersInOrder = null;
if (this instanceof BizClassLoader) {
exportResourceClassLoadersInOrder = classloaderService
.findExportResourceClassLoadersInOrderByBiz(
((BizClassLoader) this).getBizModel(), resourceName);
} else if (this instanceof PluginClassLoader) {
exportResourceClassLoadersInOrder = classloaderService
.findExportResourceClassLoadersInOrder(resourceName);
}
if (exportResourceClassLoadersInOrder != null) {
for (ClassLoader exportResourceClassLoader : exportResourceClassLoadersInOrder) {
url = exportResourceClassLoader.getResource(resourceName);
URL url = exportResourceClassLoader.getResource(resourceName);
if (url != null && this instanceof BizClassLoader) {
if (((BizClassLoader) (this)).getBizModel().isDeclared(url, resourceName)) {
return url;
Expand Down Expand Up @@ -629,8 +641,15 @@ private String transformClassName(String name) {
@SuppressWarnings("unchecked")
protected Enumeration<URL> getExportResources(String resourceName) throws IOException {
if (shouldFindExportedResource(resourceName)) {
List<ClassLoader> exportResourceClassLoadersInOrder = classloaderService
.findExportResourceClassLoadersInOrder(resourceName);
List<ClassLoader> exportResourceClassLoadersInOrder = null;
if (this instanceof BizClassLoader) {
exportResourceClassLoadersInOrder = classloaderService
.findExportResourceClassLoadersInOrderByBiz(
((BizClassLoader) this).getBizModel(), resourceName);
} else if (this instanceof PluginClassLoader) {
exportResourceClassLoadersInOrder = classloaderService
.findExportResourceClassLoadersInOrder(resourceName);
}
if (exportResourceClassLoadersInOrder != null) {
List<Enumeration<URL>> enumerationList = new ArrayList<>();
for (ClassLoader exportResourceClassLoader : exportResourceClassLoadersInOrder) {
Expand Down
Loading

0 comments on commit d1c277f

Please sign in to comment.