Skip to content

Commit

Permalink
支持重复安装插件,通过'#'分隔插件副本,插件服务和配置的Key信息携带类加载器
Browse files Browse the repository at this point in the history
  • Loading branch information
luanwenfei-venus committed Oct 19, 2023
1 parent 6b19b6b commit fb279c6
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,8 @@ public static void initPlugins(Set<String> pluginNames, boolean isDynamic) {
continue;
}
try {
final String pluginPath = pluginPackage + File.separatorChar + pluginName;
// 去除插件名副本标记,获取实际所需要用到的资源目录
final String pluginPath = pluginPackage + File.separatorChar + getRealPluginName(pluginName);
if (!new File(pluginPath).exists()) {
LOGGER.log(Level.WARNING, "Plugin directory {0} does not exist, so skip initializing {1}. ",
new String[]{pluginPath, pluginName});
Expand Down Expand Up @@ -284,7 +285,8 @@ private static boolean processByJarFile(String pluginName, File jar, boolean ifC
JarFile jarFile = null;
try {
jarFile = new JarFile(jar);
if (ifCheckSchema && !PluginSchemaValidator.checkSchema(pluginName, jarFile)) {
if (ifCheckSchema && !PluginSchemaValidator.checkSchema(pluginName, getRealPluginName(pluginName),
jarFile)) {
throw new SchemaException(SchemaException.UNEXPECTED_EXT_JAR, jar.getPath());
}
if (consumer != null) {
Expand All @@ -305,6 +307,16 @@ private static boolean processByJarFile(String pluginName, File jar, boolean ifC
}
}

/**
* 形如 plugin-name#1 plugin-name#2 方式标记插件副本,共用资源文件,通过分隔插件名获取实际的插件名,对应插件目录名及Manifest中的插件标识
*
* @param pluginName 插件名
* @return 实际插件名
*/
private static String getRealPluginName(String pluginName) {
return pluginName.split("#")[0];
}

/**
* 遍历目录下所有jar包,按文件名字典序排列
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,28 +73,29 @@ public static void removePluginVersionCache(String pluginName) {
/**
* 检查名称和版本
*
* @param name 插件名称
* @param pluginName 插件名称
* @param realPluginName 实际插件名,插件名去除副本标记后的所使用的插件
* @param jarFile 插件包
* @return 为真时经过名称和版本校验,为插件包或插件服务包,为假时表示第三方jar包
* @throws IOException 获取manifest文件异常
* @throws SchemaException 传入插件名和从资源文件中检索到的不一致
*/
public static boolean checkSchema(String name, JarFile jarFile) throws IOException {
public static boolean checkSchema(String pluginName, String realPluginName, JarFile jarFile) throws IOException {
final Object nameAttr = JarFileUtils.getManifestAttr(jarFile, PluginConstant.PLUGIN_NAME_KEY);
if (nameAttr == null) {
return false;
}
if (!nameAttr.toString().equals(name)) {
throw new SchemaException(SchemaException.UNEXPECTED_NAME, nameAttr.toString(), name);
if (!nameAttr.toString().equals(realPluginName)) {
throw new SchemaException(SchemaException.UNEXPECTED_NAME, nameAttr.toString(), pluginName);
}
final Object versionAttr = JarFileUtils.getManifestAttr(jarFile, PluginConstant.PLUGIN_VERSION_KEY);
final String givingVersion =
versionAttr == null ? PluginConstant.PLUGIN_DEFAULT_VERSION : versionAttr.toString();
final String expectingVersion = PLUGIN_VERSION_MAP.get(name);
final String expectingVersion = PLUGIN_VERSION_MAP.get(pluginName);
if (expectingVersion == null) {
PLUGIN_VERSION_MAP.put(name, givingVersion);
PLUGIN_VERSION_MAP.put(pluginName, givingVersion);
} else if (!expectingVersion.equals(givingVersion)) {
throw new SchemaException(SchemaException.UNEXPECTED_VERSION, name, givingVersion, expectingVersion);
throw new SchemaException(SchemaException.UNEXPECTED_VERSION, pluginName, givingVersion, expectingVersion);
}
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,28 +63,30 @@ public static void loadPluginConfigs(Plugin plugin) {
ClassLoader classLoader =
plugin.getServiceClassLoader() != null ? plugin.getServiceClassLoader() : plugin.getPluginClassLoader();
for (BaseConfig config : ServiceLoader.load(PluginConfig.class, classLoader)) {
final String typeKey = ConfigKeyUtil.getTypeKey(config.getClass());
final BaseConfig retainedConfig = PLUGIN_CONFIG_MAP.get(typeKey);
Class<? extends BaseConfig> pluginConfigCls = config.getClass();
String pluginConfigKey = ConfigKeyUtil.getCLTypeKey(ConfigKeyUtil.getTypeKey(pluginConfigCls),
pluginConfigCls.getClassLoader());
final BaseConfig retainedConfig = PLUGIN_CONFIG_MAP.get(pluginConfigKey);
if (pluginConfigFile.exists() && pluginConfigFile.isFile()) {
if (retainedConfig == null) {
PLUGIN_CONFIG_MAP.put(typeKey, ConfigManager.doLoad(pluginConfigFile, config));
plugin.getConfigs().add(typeKey);
} else if (retainedConfig.getClass() == config.getClass()) {
PLUGIN_CONFIG_MAP.put(pluginConfigKey, ConfigManager.doLoad(pluginConfigFile, config));
plugin.getConfigs().add(pluginConfigKey);
} else if (retainedConfig.getClass() == pluginConfigCls) {
LOGGER.fine(String.format(Locale.ROOT, "Skip load config [%s] repeatedly. ",
config.getClass().getName()));
pluginConfigCls.getName()));
} else {
LOGGER.warning(String.format(Locale.ROOT, "Type key of %s is %s, same as %s's. ",
config.getClass().getName(), typeKey, retainedConfig.getClass().getName()));
pluginConfigCls.getName(), pluginConfigKey, retainedConfig.getClass().getName()));
}
continue;
}
if (PLUGIN_CONFIG_MAP.containsKey(typeKey)) {
if (PLUGIN_CONFIG_MAP.containsKey(pluginConfigKey)) {
continue;
}

// 不能从文件加载,则为默认配置
PLUGIN_CONFIG_MAP.put(typeKey, config);
plugin.getConfigs().add(typeKey);
PLUGIN_CONFIG_MAP.put(pluginConfigKey, config);
plugin.getConfigs().add(pluginConfigKey);
}
}

Expand All @@ -107,7 +109,8 @@ public static void cleanPluginConfigs(Plugin plugin) {
* @return 插件配置实例
*/
public static <R extends PluginConfig> R getPluginConfig(Class<R> cls) {
return (R) PLUGIN_CONFIG_MAP.get(ConfigKeyUtil.getTypeKey(cls));
String pluginConfigKey = ConfigKeyUtil.getCLTypeKey(ConfigKeyUtil.getTypeKey(cls), cls.getClassLoader());
return (R) PLUGIN_CONFIG_MAP.get(pluginConfigKey);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.huaweicloud.sermant.core.event.collector.FrameworkEventCollector;
import com.huaweicloud.sermant.core.plugin.Plugin;
import com.huaweicloud.sermant.core.service.ServiceManager;
import com.huaweicloud.sermant.core.utils.KeyGenerateUtils;

import java.util.ServiceLoader;
import java.util.logging.Level;
Expand Down Expand Up @@ -50,9 +51,9 @@ public static void initPluginServices(Plugin plugin) {
for (PluginService service : ServiceLoader.load(PluginService.class, classLoader)) {
if (loadService(service, service.getClass(), PluginService.class)) {
try {
String serviceName = service.getClass().getName();
String pluginServiceKey = KeyGenerateUtils.generateClassKeyWithClassLoader(service.getClass());
service.start();
plugin.getServices().add(serviceName);
plugin.getServices().add(pluginServiceKey);
} catch (Exception ex) {
LOGGER.log(Level.SEVERE, "Error occurs while starting plugin service: " + service.getClass(), ex);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import com.huaweicloud.sermant.core.event.EventManager;
import com.huaweicloud.sermant.core.event.collector.FrameworkEventCollector;
import com.huaweicloud.sermant.core.exception.DupServiceException;
import com.huaweicloud.sermant.core.service.send.api.GatewayClient;
import com.huaweicloud.sermant.core.utils.KeyGenerateUtils;
import com.huaweicloud.sermant.core.utils.SpiLoadUtils;

import java.util.ArrayList;
Expand Down Expand Up @@ -121,7 +121,8 @@ public static void initServices() {
* @throws IllegalArgumentException IllegalArgumentException 找不到对应的服务
*/
public static <T extends BaseService> T getService(Class<T> serviceClass) {
final BaseService baseService = SERVICES.get(serviceClass.getName());
String serviceKey = KeyGenerateUtils.generateClassKeyWithClassLoader(serviceClass);
final BaseService baseService = SERVICES.get(serviceKey);
if (baseService != null && serviceClass.isAssignableFrom(baseService.getClass())) {
return (T) baseService;
}
Expand All @@ -141,8 +142,8 @@ protected static boolean loadService(BaseService service, Class<?> serviceCls,
if (serviceCls == null || serviceCls == baseCls || !baseCls.isAssignableFrom(serviceCls)) {
return false;
}
final String serviceName = serviceCls.getName();
final BaseService oldService = SERVICES.get(serviceName);
final String serviceKey = KeyGenerateUtils.generateClassKeyWithClassLoader(serviceCls);
final BaseService oldService = SERVICES.get(serviceKey);
if (oldService != null && oldService.getClass() == service.getClass()) {
return false;
}
Expand All @@ -151,11 +152,11 @@ protected static boolean loadService(BaseService service, Class<?> serviceCls,
SpiLoadUtils.getBetter(oldService, service, new SpiLoadUtils.WeightEqualHandler<BaseService>() {
@Override
public BaseService handle(BaseService source, BaseService target) {
throw new DupServiceException(serviceName);
throw new DupServiceException(serviceKey);
}
});
if (betterService != oldService) {
SERVICES.put(serviceName, service);
SERVICES.put(serviceKey, service);
isLoadSucceed = true;
}
isLoadSucceed |= loadService(service, serviceCls.getSuperclass(), baseCls);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.huaweicloud.sermant.core.utils;

/**
* 生成各类键
*
* @author luanwenfei
* @since 2023-10-19
*/
public class KeyGenerateUtils {
private KeyGenerateUtils() {
}

/**
* 通过Class生成携带类加载器信息的键
*
* @param cls 类
* @return 键
*/
public static String generateClassKeyWithClassLoader(Class<?> cls) {
return cls.getName() + "@" + System.identityHashCode(cls.getClassLoader());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.huawei.dubbo.registry.service.nacos.NacosRegistryService;
import com.huaweicloud.sermant.core.service.BaseService;
import com.huaweicloud.sermant.core.service.ServiceManager;
import com.huaweicloud.sermant.core.utils.KeyGenerateUtils;

/**
* 测试NacosRegistryFactory
Expand All @@ -45,7 +46,8 @@ public NacosRegistryFactoryTest() throws NoSuchFieldException, IllegalAccessExce
Field field = ServiceManager.class.getDeclaredField("SERVICES");
field.setAccessible(true);
Map<String, BaseService> map = (Map<String, BaseService>) field.get(null);
map.put(NacosRegistryService.class.getCanonicalName(), new NacosRegistryService() {
map.put(KeyGenerateUtils.generateClassKeyWithClassLoader(NacosRegistryService.class),
new NacosRegistryService() {

@Override
public void doRegister(Object url) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import com.huaweicloud.sermant.core.service.BaseService;
import com.huaweicloud.sermant.core.service.ServiceManager;
import com.huaweicloud.sermant.core.utils.KeyGenerateUtils;

import com.alibaba.dubbo.common.URL;

Expand All @@ -47,7 +48,7 @@ public RegistryFactoryTest() throws NoSuchFieldException, IllegalAccessException
Field field = ServiceManager.class.getDeclaredField("SERVICES");
field.setAccessible(true);
Map<String, BaseService> map = (Map<String, BaseService>) field.get(null);
map.put(RegistryService.class.getCanonicalName(), new RegistryService() {
map.put(KeyGenerateUtils.generateClassKeyWithClassLoader(RegistryService.class), new RegistryService() {
@Override
public void startRegistration() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@
package com.huawei.registry.service.client;

import com.huawei.registry.config.RegisterConfig;

import com.huawei.registry.config.RegisterServiceCommonConfig;

import com.huaweicloud.sermant.core.config.ConfigManager;
import com.huaweicloud.sermant.core.config.common.BaseConfig;
import com.huaweicloud.sermant.core.config.utils.ConfigKeyUtil;
import com.huaweicloud.sermant.core.plugin.config.PluginConfigManager;

import org.junit.BeforeClass;
Expand Down Expand Up @@ -49,15 +50,18 @@ public static void init() throws IllegalAccessException, NoSuchFieldException, C
removeFinalModify(configMap);

configManagerMap = (Map<String, BaseConfig>) configMap.get(null);
configManagerMap.put("servicecomb.service", new RegisterConfig());
configManagerMap.put("register.service", new RegisterServiceCommonConfig());
configManagerMap.put(ConfigKeyUtil.getCLTypeKey("servicecomb.service", RegisterConfig.class.getClassLoader()),
new RegisterConfig());
configManagerMap.put(
ConfigKeyUtil.getCLTypeKey("register.service", RegisterServiceCommonConfig.class.getClassLoader()),
new RegisterServiceCommonConfig());
}

/**
* 移除final修饰符
*
* @param field 字段
* @throws NoSuchFieldException 无该字段抛出
* @throws NoSuchFieldException 无该字段抛出
* @throws IllegalAccessException 无法拿到该字段抛出
*/
protected static void removeFinalModify(Field field) throws NoSuchFieldException, IllegalAccessException {
Expand Down

0 comments on commit fb279c6

Please sign in to comment.