[3.0] Improve resource cleaning (#9129)
This commit is contained in:
parent
afcd8110c2
commit
7a59b4b875
|
|
@ -568,6 +568,10 @@ class URL implements Serializable {
|
|||
return ScopeModelUtil.getApplicationModel(getScopeModel());
|
||||
}
|
||||
|
||||
public ApplicationModel getApplicationModel() {
|
||||
return ScopeModelUtil.getOrNullApplicationModel(getScopeModel());
|
||||
}
|
||||
|
||||
public ModuleModel getOrDefaultModuleModel() {
|
||||
return ScopeModelUtil.getModuleModel(getScopeModel());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import org.apache.dubbo.common.logger.LoggerFactory;
|
|||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.rpc.model.ScopeModel;
|
||||
import org.apache.dubbo.rpc.model.ScopeModelUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
|
|
@ -66,7 +65,7 @@ public class ConfigurationUtils {
|
|||
* @return
|
||||
*/
|
||||
public static Configuration getSystemConfiguration(ScopeModel scopeModel) {
|
||||
return ScopeModelUtil.getOrDefaultApplicationModel(scopeModel).getModelEnvironment().getSystemConfiguration();
|
||||
return getScopeModelOrDefaultApplicationModel(scopeModel).getModelEnvironment().getSystemConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -76,7 +75,7 @@ public class ConfigurationUtils {
|
|||
*/
|
||||
|
||||
public static Configuration getEnvConfiguration(ScopeModel scopeModel) {
|
||||
return ScopeModelUtil.getOrDefaultApplicationModel(scopeModel).getModelEnvironment().getEnvironmentConfiguration();
|
||||
return getScopeModelOrDefaultApplicationModel(scopeModel).getModelEnvironment().getEnvironmentConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -88,7 +87,7 @@ public class ConfigurationUtils {
|
|||
*/
|
||||
|
||||
public static Configuration getGlobalConfiguration(ScopeModel scopeModel) {
|
||||
return ScopeModelUtil.getOrDefaultApplicationModel(scopeModel).getModelEnvironment().getConfiguration();
|
||||
return getScopeModelOrDefaultApplicationModel(scopeModel).getModelEnvironment().getConfiguration();
|
||||
}
|
||||
|
||||
public static Configuration getDynamicGlobalConfiguration(ScopeModel scopeModel) {
|
||||
|
|
@ -127,12 +126,19 @@ public class ConfigurationUtils {
|
|||
}
|
||||
|
||||
public static String getCachedDynamicProperty(ScopeModel realScopeModel, String key, String defaultValue) {
|
||||
ScopeModel scopeModel = ScopeModelUtil.getOrDefaultApplicationModel(realScopeModel);
|
||||
ScopeModel scopeModel = getScopeModelOrDefaultApplicationModel(realScopeModel);
|
||||
ConfigurationCache configurationCache = scopeModel.getBeanFactory().getBean(ConfigurationCache.class);
|
||||
String value = configurationCache.computeIfAbsent(key, _k -> ConfigurationUtils.getDynamicProperty(scopeModel, _k, ""));
|
||||
return StringUtils.isEmpty(value) ? defaultValue : value;
|
||||
}
|
||||
|
||||
private static ScopeModel getScopeModelOrDefaultApplicationModel(ScopeModel realScopeModel) {
|
||||
if (realScopeModel == null) {
|
||||
return ApplicationModel.defaultModel();
|
||||
}
|
||||
return realScopeModel;
|
||||
}
|
||||
|
||||
public static String getDynamicProperty(ScopeModel scopeModel, String property) {
|
||||
return getDynamicProperty(scopeModel, property, null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,4 +158,8 @@ public abstract class AbstractDeployer<E extends ScopeModel> implements Deployer
|
|||
public boolean isInitialized() {
|
||||
return initialized.get();
|
||||
}
|
||||
|
||||
protected String getIdentifier() {
|
||||
return scopeModel.getDesc();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,5 +89,4 @@ public interface Deployer<E extends ScopeModel> {
|
|||
|
||||
void removeDeployListener(DeployListener<E> listener);
|
||||
|
||||
String getIdentifier();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ public class GlobalResourcesRepository {
|
|||
private volatile static GlobalResourcesRepository instance;
|
||||
private volatile ExecutorService executorService;
|
||||
private final List<Disposable> oneoffDisposables = Collections.synchronizedList(new ArrayList<>());
|
||||
private final List<Disposable> reusedDisposables = Collections.synchronizedList(new ArrayList<>());
|
||||
private static final List<Disposable> globalReusedDisposables = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
private GlobalResourcesRepository() {
|
||||
}
|
||||
|
|
@ -53,6 +53,25 @@ public class GlobalResourcesRepository {
|
|||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a global reused disposable. The disposable will be executed when all dubbo FrameworkModels are destroyed.
|
||||
* Note: the global disposable should be registered in static code, it's reusable and will not be removed when dubbo shutdown.
|
||||
* @param disposable
|
||||
*/
|
||||
public static void registerGlobalDisposable(Disposable disposable) {
|
||||
synchronized (GlobalResourcesRepository.class) {
|
||||
if (!globalReusedDisposables.contains(disposable)) {
|
||||
globalReusedDisposables.add(disposable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeGlobalDisposable(Disposable disposable) {
|
||||
synchronized (GlobalResourcesRepository.class) {
|
||||
this.globalReusedDisposables.remove(disposable);
|
||||
}
|
||||
}
|
||||
|
||||
public static ExecutorService getGlobalExecutorService() {
|
||||
return getInstance().getExecutorService();
|
||||
}
|
||||
|
|
@ -68,7 +87,7 @@ public class GlobalResourcesRepository {
|
|||
return executorService;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
synchronized public void destroy() {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Destroying global resources ...");
|
||||
}
|
||||
|
|
@ -77,16 +96,7 @@ public class GlobalResourcesRepository {
|
|||
executorService = null;
|
||||
}
|
||||
|
||||
// notify disposables
|
||||
// NOTE: don't clear reused disposables for reuse purpose
|
||||
for (Disposable disposable : new ArrayList<>(reusedDisposables)) {
|
||||
try {
|
||||
disposable.destroy();
|
||||
} catch (Exception e) {
|
||||
logger.warn("destroy resources failed: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// call one-off disposables
|
||||
for (Disposable disposable : new ArrayList<>(oneoffDisposables)) {
|
||||
try {
|
||||
disposable.destroy();
|
||||
|
|
@ -97,6 +107,15 @@ public class GlobalResourcesRepository {
|
|||
// clear one-off disposable
|
||||
oneoffDisposables.clear();
|
||||
|
||||
// call global disposable, don't clear globalReusedDisposables for reuse purpose
|
||||
for (Disposable disposable : new ArrayList<>(globalReusedDisposables)) {
|
||||
try {
|
||||
disposable.destroy();
|
||||
} catch (Exception e) {
|
||||
logger.warn("destroy resources failed: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Dubbo is completely destroyed");
|
||||
}
|
||||
|
|
@ -106,24 +125,13 @@ public class GlobalResourcesRepository {
|
|||
* Register a one-off disposable, the disposable is removed automatically on first shutdown.
|
||||
* @param disposable
|
||||
*/
|
||||
public void registerDisposable(Disposable disposable) {
|
||||
this.registerDisposable(disposable, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a disposable
|
||||
* @param disposable
|
||||
* @param reused true - the disposable is keep and reused. false - the disposable is removed automatically on first shutdown
|
||||
*/
|
||||
public void registerDisposable(Disposable disposable, boolean reused) {
|
||||
List<Disposable> disposables = reused ? reusedDisposables : oneoffDisposables;
|
||||
if (!disposables.contains(disposable)) {
|
||||
disposables.add(disposable);
|
||||
synchronized public void registerDisposable(Disposable disposable) {
|
||||
if (!oneoffDisposables.contains(disposable)) {
|
||||
oneoffDisposables.add(disposable);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeDisposable(Disposable disposable) {
|
||||
this.reusedDisposables.remove(disposable);
|
||||
synchronized public void removeDisposable(Disposable disposable) {
|
||||
this.oneoffDisposables.remove(disposable);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ public class ClassLoaderResourceLoader {
|
|||
|
||||
static {
|
||||
// register resources destroy listener
|
||||
GlobalResourcesRepository.getInstance().registerDisposable(()-> destroy(), true);
|
||||
GlobalResourcesRepository.registerGlobalDisposable(()-> destroy());
|
||||
}
|
||||
|
||||
public static Map<ClassLoader, Set<java.net.URL>> loadResources(String fileName, List<ClassLoader> classLoaders) {
|
||||
|
|
@ -62,7 +62,7 @@ public class ClassLoaderResourceLoader {
|
|||
public static Set<java.net.URL> loadResources(String fileName, ClassLoader currentClassLoader) {
|
||||
Map<ClassLoader, Map<String, Set<java.net.URL>>> classLoaderCache;
|
||||
if (classLoaderResourcesCache == null || (classLoaderCache = classLoaderResourcesCache.get()) == null) {
|
||||
synchronized (ConfigUtils.class) {
|
||||
synchronized (ClassLoaderResourceLoader.class) {
|
||||
if (classLoaderResourcesCache == null || (classLoaderCache = classLoaderResourcesCache.get()) == null) {
|
||||
classLoaderCache = new ConcurrentHashMap<>();
|
||||
classLoaderResourcesCache = new SoftReference<>(classLoaderCache);
|
||||
|
|
@ -98,8 +98,8 @@ public class ClassLoaderResourceLoader {
|
|||
}
|
||||
|
||||
public static void destroy() {
|
||||
if (classLoaderResourcesCache != null) {
|
||||
classLoaderResourcesCache.clear();
|
||||
synchronized (ClassLoaderResourceLoader.class) {
|
||||
classLoaderResourcesCache = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import org.apache.dubbo.common.extension.DisableInject;
|
|||
import org.apache.dubbo.common.logger.Logger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.config.AbstractConfig;
|
||||
import org.apache.dubbo.config.ApplicationConfig;
|
||||
import org.apache.dubbo.config.ConfigCenterConfig;
|
||||
|
|
@ -255,7 +256,14 @@ public class ConfigManager extends AbstractConfigManager implements ApplicationE
|
|||
// config centers has bean loaded before starting config center
|
||||
//loadConfigsOfTypeFromProps(ConfigCenterConfig.class);
|
||||
|
||||
refreshAll();
|
||||
|
||||
checkConfigs();
|
||||
|
||||
// set model name
|
||||
if (StringUtils.isBlank(applicationModel.getModelName())) {
|
||||
applicationModel.setModelName(applicationModel.getApplicationName());
|
||||
}
|
||||
}
|
||||
|
||||
private void checkConfigs() {
|
||||
|
|
|
|||
|
|
@ -53,8 +53,6 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
public class ApplicationModel extends ScopeModel {
|
||||
protected static final Logger LOGGER = LoggerFactory.getLogger(ApplicationModel.class);
|
||||
public static final String NAME = "ApplicationModel";
|
||||
private static volatile ApplicationModel defaultInstance;
|
||||
|
||||
private final List<ModuleModel> moduleModels = Collections.synchronizedList(new ArrayList<>());
|
||||
private final List<ModuleModel> pubModuleModels = Collections.synchronizedList(new ArrayList<>());
|
||||
private Environment environment;
|
||||
|
|
@ -84,15 +82,15 @@ public class ApplicationModel extends ScopeModel {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* During destroying the default FrameworkModel, FrameworkModel.defaultModel() or ApplicationModel.defaultModel()
|
||||
* will get an broken model, maybe cause unpredictable problem.
|
||||
* Recommendation: Avoid using the default model as much as possible.
|
||||
* @return the global default ApplicationModel
|
||||
*/
|
||||
public static ApplicationModel defaultModel() {
|
||||
if (defaultInstance == null) {
|
||||
synchronized (ApplicationModel.class) {
|
||||
if (defaultInstance == null) {
|
||||
defaultInstance = new ApplicationModel(FrameworkModel.defaultModel());
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultInstance;
|
||||
// should get from default FrameworkModel, avoid out of sync
|
||||
return FrameworkModel.defaultModel().defaultApplication();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -186,9 +184,8 @@ public class ApplicationModel extends ScopeModel {
|
|||
// only for unit test
|
||||
@Deprecated
|
||||
public static void reset() {
|
||||
if (defaultInstance != null) {
|
||||
defaultInstance.destroy();
|
||||
defaultInstance = null;
|
||||
if (FrameworkModel.defaultModel().getDefaultAppModel() != null) {
|
||||
FrameworkModel.defaultModel().getDefaultAppModel().destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -204,13 +201,10 @@ public class ApplicationModel extends ScopeModel {
|
|||
this.isInternal = isInternal;
|
||||
this.frameworkModel = frameworkModel;
|
||||
frameworkModel.addApplication(this);
|
||||
initialize();
|
||||
// bind to default instance if absent
|
||||
synchronized (ApplicationModel.class) {
|
||||
if (!isInternal && defaultInstance == null) {
|
||||
defaultInstance = this;
|
||||
}
|
||||
if (LOGGER.isInfoEnabled()) {
|
||||
LOGGER.info(getDesc() + " is created");
|
||||
}
|
||||
initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -244,17 +238,7 @@ public class ApplicationModel extends ScopeModel {
|
|||
@Override
|
||||
protected void onDestroy() {
|
||||
// 1. remove from frameworkModel
|
||||
if (defaultInstance == this) {
|
||||
synchronized (ApplicationModel.class) {
|
||||
frameworkModel.removeApplication(this);
|
||||
defaultInstance = null;
|
||||
if (LOGGER.isInfoEnabled()) {
|
||||
LOGGER.info("Reset default Dubbo application[" + getInternalId() + "] to null ...");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
frameworkModel.removeApplication(this);
|
||||
}
|
||||
frameworkModel.removeApplication(this);
|
||||
|
||||
// 2. pre-destroy, set stopping
|
||||
if (deployer != null) {
|
||||
|
|
@ -343,8 +327,9 @@ public class ApplicationModel extends ScopeModel {
|
|||
void addModule(ModuleModel moduleModel, boolean isInternal) {
|
||||
synchronized (moduleLock) {
|
||||
if (!this.moduleModels.contains(moduleModel)) {
|
||||
checkDestroyed();
|
||||
this.moduleModels.add(moduleModel);
|
||||
moduleModel.setInternalName(buildInternalName(ModuleModel.NAME, getInternalId(), moduleIndex.getAndIncrement()));
|
||||
moduleModel.setInternalId(buildInternalId(getInternalId(), moduleIndex.getAndIncrement()));
|
||||
if (!isInternal) {
|
||||
pubModuleModels.add(moduleModel);
|
||||
}
|
||||
|
|
@ -369,6 +354,12 @@ public class ApplicationModel extends ScopeModel {
|
|||
}
|
||||
}
|
||||
|
||||
private void checkDestroyed() {
|
||||
if (isDestroyed()) {
|
||||
throw new IllegalStateException("ApplicationModel is destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
public List<ModuleModel> getModuleModels() {
|
||||
return Collections.unmodifiableList(moduleModels);
|
||||
}
|
||||
|
|
@ -379,9 +370,6 @@ public class ApplicationModel extends ScopeModel {
|
|||
|
||||
public ModuleModel getDefaultModule() {
|
||||
if (defaultModule == null) {
|
||||
if (isDestroyed()) {
|
||||
return null;
|
||||
}
|
||||
synchronized (moduleLock) {
|
||||
if (defaultModule == null) {
|
||||
defaultModule = findDefaultModule();
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Model of dubbo framework, it can be shared with multiple applications.
|
||||
|
|
@ -43,8 +44,12 @@ public class FrameworkModel extends ScopeModel {
|
|||
// internal app index is 0, default app index is 1
|
||||
private final AtomicLong appIndex = new AtomicLong(0);
|
||||
|
||||
private static Object globalLock = new Object();
|
||||
|
||||
private volatile static FrameworkModel defaultInstance;
|
||||
|
||||
private volatile ApplicationModel defaultAppModel;
|
||||
|
||||
private static List<FrameworkModel> allInstances = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
private List<ApplicationModel> applicationModels = Collections.synchronizedList(new ArrayList<>());
|
||||
|
|
@ -55,17 +60,26 @@ public class FrameworkModel extends ScopeModel {
|
|||
|
||||
private ApplicationModel internalApplicationModel;
|
||||
|
||||
private Object instLock = new Object();
|
||||
|
||||
public FrameworkModel() {
|
||||
super(null, ExtensionScope.FRAMEWORK);
|
||||
this.setInternalId(index.getAndIncrement()+"");
|
||||
// register FrameworkModel instance early
|
||||
synchronized (globalLock) {
|
||||
allInstances.add(this);
|
||||
resetDefaultFrameworkModel();
|
||||
}
|
||||
if (LOGGER.isInfoEnabled()) {
|
||||
LOGGER.info(getDesc() + " is created");
|
||||
}
|
||||
initialize();
|
||||
this.setInternalName(buildInternalName(NAME, null, index.getAndIncrement()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize() {
|
||||
super.initialize();
|
||||
serviceRepository = new FrameworkServiceRepository(this);
|
||||
allInstances.add(this);
|
||||
|
||||
ExtensionLoader<ScopeModelInitializer> initializerExtensionLoader = this.getExtensionLoader(ScopeModelInitializer.class);
|
||||
Set<ScopeModelInitializer> initializers = initializerExtensionLoader.getSupportedExtensionInstances();
|
||||
|
|
@ -81,45 +95,73 @@ public class FrameworkModel extends ScopeModel {
|
|||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
// destroy all application model
|
||||
for (ApplicationModel applicationModel : new ArrayList<>(applicationModels)) {
|
||||
applicationModel.destroy();
|
||||
if (defaultInstance == this) {
|
||||
// NOTE: During destroying the default FrameworkModel, FrameworkModel.defaultModel() or ApplicationModel.defaultModel()
|
||||
// will get an broken model, maybe cause unpredictable problem.
|
||||
if (LOGGER.isInfoEnabled()) {
|
||||
LOGGER.info("Destroying default framework model: " + getDesc());
|
||||
}
|
||||
}
|
||||
synchronized (instLock) {
|
||||
if (LOGGER.isInfoEnabled()) {
|
||||
LOGGER.info(getDesc() + " is destroying ...");
|
||||
}
|
||||
|
||||
if (LOGGER.isInfoEnabled()) {
|
||||
LOGGER.info("Dubbo framework[" + getInternalId() + "] is destroying ...");
|
||||
}
|
||||
synchronized (FrameworkModel.class) {
|
||||
allInstances.remove(this);
|
||||
if (defaultInstance == this) {
|
||||
defaultInstance = null;
|
||||
if (LOGGER.isInfoEnabled()) {
|
||||
LOGGER.info("Reset default Dubbo framework[" + getInternalId() + "] to null ...");
|
||||
}
|
||||
// destroy all application model
|
||||
for (ApplicationModel applicationModel : new ArrayList<>(applicationModels)) {
|
||||
applicationModel.destroy();
|
||||
}
|
||||
|
||||
// check whether all application models are destroyed
|
||||
checkApplicationDestroy();
|
||||
|
||||
// notify destroy and clean framework resources
|
||||
// see org.apache.dubbo.config.deploy.FrameworkModelCleaner
|
||||
notifyDestroy();
|
||||
checkApplicationDestroy();
|
||||
|
||||
if (LOGGER.isInfoEnabled()) {
|
||||
LOGGER.info(getDesc() + " is destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
// notify destroy and clean framework resources
|
||||
// see org.apache.dubbo.config.deploy.FrameworkModelCleaner
|
||||
notifyDestroy();
|
||||
|
||||
if (LOGGER.isInfoEnabled()) {
|
||||
LOGGER.info("Dubbo framework[" + getInternalId() + "] is destroyed");
|
||||
// remove from allInstances and reset default FrameworkModel
|
||||
synchronized (globalLock) {
|
||||
allInstances.remove(this);
|
||||
resetDefaultFrameworkModel();
|
||||
}
|
||||
|
||||
// if all FrameworkModels are destroyed, clean global static resources, shutdown dubbo completely
|
||||
if (allInstances.isEmpty()) {
|
||||
destroyGlobalResources();
|
||||
destroyGlobalResources();
|
||||
}
|
||||
|
||||
private void checkApplicationDestroy() {
|
||||
if (applicationModels.size() > 0) {
|
||||
List<String> remainApplications = applicationModels.stream()
|
||||
.map(model -> model.getDesc())
|
||||
.collect(Collectors.toList());
|
||||
throw new IllegalStateException("Not all application models are completely destroyed, remaining " +
|
||||
remainApplications.size() + " application models may be created during destruction: " + remainApplications);
|
||||
}
|
||||
}
|
||||
|
||||
private void destroyGlobalResources() {
|
||||
GlobalResourcesRepository.getInstance().destroy();
|
||||
synchronized (globalLock) {
|
||||
if (allInstances.isEmpty()) {
|
||||
GlobalResourcesRepository.getInstance().destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* During destroying the default FrameworkModel, FrameworkModel.defaultModel() or ApplicationModel.defaultModel()
|
||||
* will get an broken model, maybe cause unpredictable problem.
|
||||
* Recommendation: Avoid using the default model as much as possible.
|
||||
* @return the global default FrameworkModel
|
||||
*/
|
||||
public static FrameworkModel defaultModel() {
|
||||
if (defaultInstance == null) {
|
||||
synchronized (FrameworkModel.class) {
|
||||
synchronized (globalLock) {
|
||||
if (defaultInstance == null) {
|
||||
defaultInstance = new FrameworkModel();
|
||||
}
|
||||
|
|
@ -128,10 +170,17 @@ public class FrameworkModel extends ScopeModel {
|
|||
return defaultInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all framework model instances
|
||||
* @return
|
||||
*/
|
||||
public static List<FrameworkModel> getAllInstances() {
|
||||
return Collections.unmodifiableList(allInstances);
|
||||
return Collections.unmodifiableList(new ArrayList<>(allInstances));
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy all framework model instances, shutdown dubbo engine completely.
|
||||
*/
|
||||
public static void destroyAll() {
|
||||
for (FrameworkModel frameworkModel : new ArrayList<>(allInstances)) {
|
||||
frameworkModel.destroy();
|
||||
|
|
@ -142,29 +191,108 @@ public class FrameworkModel extends ScopeModel {
|
|||
return new ApplicationModel(this);
|
||||
}
|
||||
|
||||
synchronized void addApplication(ApplicationModel applicationModel) {
|
||||
if (!this.applicationModels.contains(applicationModel)) {
|
||||
this.applicationModels.add(applicationModel);
|
||||
if (!applicationModel.isInternal()) {
|
||||
this.pubApplicationModels.add(applicationModel);
|
||||
/**
|
||||
* Get or create default application model
|
||||
* @return
|
||||
*/
|
||||
public ApplicationModel defaultApplication() {
|
||||
if (defaultAppModel == null) {
|
||||
checkDestroyed();
|
||||
synchronized(instLock){
|
||||
resetDefaultAppModel();
|
||||
if (defaultAppModel == null) {
|
||||
defaultAppModel = newApplication();
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultAppModel;
|
||||
}
|
||||
|
||||
ApplicationModel getDefaultAppModel() {
|
||||
return defaultAppModel;
|
||||
}
|
||||
|
||||
void addApplication(ApplicationModel applicationModel) {
|
||||
// can not add new application if it's destroying
|
||||
checkDestroyed();
|
||||
synchronized (instLock){
|
||||
if (!this.applicationModels.contains(applicationModel)) {
|
||||
applicationModel.setInternalId(buildInternalId(getInternalId(), appIndex.getAndIncrement()));
|
||||
this.applicationModels.add(applicationModel);
|
||||
if (!applicationModel.isInternal()) {
|
||||
this.pubApplicationModels.add(applicationModel);
|
||||
}
|
||||
resetDefaultAppModel();
|
||||
}
|
||||
applicationModel.setInternalName(buildInternalName(ApplicationModel.NAME, getInternalId(), appIndex.getAndIncrement()));
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void removeApplication(ApplicationModel model) {
|
||||
this.applicationModels.remove(model);
|
||||
if (!model.isInternal()) {
|
||||
this.pubApplicationModels.remove(model);
|
||||
void removeApplication(ApplicationModel model) {
|
||||
synchronized (instLock) {
|
||||
this.applicationModels.remove(model);
|
||||
if (!model.isInternal()) {
|
||||
this.pubApplicationModels.remove(model);
|
||||
}
|
||||
resetDefaultAppModel();
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void tryDestroy() {
|
||||
if (pubApplicationModels.size() == 0) {
|
||||
destroy();
|
||||
void tryDestroy() {
|
||||
synchronized (instLock) {
|
||||
if (pubApplicationModels.size() == 0) {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkDestroyed() {
|
||||
if (isDestroyed()) {
|
||||
throw new IllegalStateException("FrameworkModel is destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
private void resetDefaultAppModel() {
|
||||
synchronized (instLock) {
|
||||
if (this.defaultAppModel != null && !this.defaultAppModel.isDestroyed()) {
|
||||
return;
|
||||
}
|
||||
ApplicationModel oldDefaultAppModel = this.defaultAppModel;
|
||||
if (pubApplicationModels.size() > 0) {
|
||||
this.defaultAppModel = pubApplicationModels.get(0);
|
||||
} else {
|
||||
this.defaultAppModel = null;
|
||||
}
|
||||
if (defaultInstance == this && oldDefaultAppModel != this.defaultAppModel) {
|
||||
if (LOGGER.isInfoEnabled()) {
|
||||
LOGGER.info("Reset global default application from " + safeGetModelDesc(oldDefaultAppModel) + " to " + safeGetModelDesc(this.defaultAppModel));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void resetDefaultFrameworkModel() {
|
||||
synchronized (globalLock) {
|
||||
if (defaultInstance != null && !defaultInstance.isDestroyed()) {
|
||||
return;
|
||||
}
|
||||
FrameworkModel oldDefaultFrameworkModel = defaultInstance;
|
||||
if (allInstances.size() > 0) {
|
||||
defaultInstance = allInstances.get(0);
|
||||
} else {
|
||||
defaultInstance = null;
|
||||
}
|
||||
if (oldDefaultFrameworkModel != defaultInstance) {
|
||||
if (LOGGER.isInfoEnabled()) {
|
||||
LOGGER.info("Reset global default framework from " + safeGetModelDesc(oldDefaultFrameworkModel) + " to " + safeGetModelDesc(defaultInstance));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String safeGetModelDesc(ScopeModel scopeModel) {
|
||||
return scopeModel != null ? scopeModel.getDesc() : null;
|
||||
}
|
||||
|
||||
public List<ApplicationModel> getApplicationModels() {
|
||||
return Collections.unmodifiableList(pubApplicationModels);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,10 @@ public class ModuleModel extends ScopeModel {
|
|||
Assert.notNull(applicationModel, "ApplicationModel can not be null");
|
||||
this.applicationModel = applicationModel;
|
||||
applicationModel.addModule(this, isInternal);
|
||||
if (LOGGER.isInfoEnabled()) {
|
||||
LOGGER.info(getDesc() + " is created");
|
||||
}
|
||||
|
||||
initialize();
|
||||
Assert.notNull(serviceRepository, "ModuleServiceRepository can not be null");
|
||||
Assert.notNull(moduleConfigManager, "ModuleConfigManager can not be null");
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import org.apache.dubbo.common.extension.ExtensionScope;
|
|||
import org.apache.dubbo.common.logger.Logger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.utils.ConcurrentHashSet;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
|
@ -38,25 +39,27 @@ public abstract class ScopeModel implements ExtensionAccessor {
|
|||
protected static final Logger LOGGER = LoggerFactory.getLogger(ScopeModel.class);
|
||||
|
||||
/**
|
||||
* The internal name is used to represent the hierarchy of the model tree, such as:
|
||||
* The internal id is used to represent the hierarchy of the model tree, such as:
|
||||
* <ol>
|
||||
* <li>FrameworkModel-1</li>
|
||||
* <li>1</li>
|
||||
* FrameworkModel (index=1)
|
||||
* <li>ApplicationModel-1.2</li>
|
||||
* <li>1.2</li>
|
||||
* FrameworkModel (index=1) -> ApplicationModel (index=2)
|
||||
* <li>ModuleModel-1.2.0</li>
|
||||
* <li>1.2.0</li>
|
||||
* FrameworkModel (index=1) -> ApplicationModel (index=2) -> ModuleModel (index=0, internal module)
|
||||
* <li>ModuleModel-1.2.1</li>
|
||||
* <li>1.2.1</li>
|
||||
* FrameworkModel (index=1) -> ApplicationModel (index=2) -> ModuleModel (index=1, first user module)
|
||||
* </ol>
|
||||
*/
|
||||
private String internalName;
|
||||
private String internalId;
|
||||
|
||||
/**
|
||||
* Public Model Name, can be set from user
|
||||
*/
|
||||
private String modelName;
|
||||
|
||||
private String desc;
|
||||
|
||||
private Set<ClassLoader> classLoaders;
|
||||
|
||||
private final ScopeModel parent;
|
||||
|
|
@ -164,15 +167,6 @@ public abstract class ScopeModel implements ExtensionAccessor {
|
|||
return parent;
|
||||
}
|
||||
|
||||
public String getInternalName() {
|
||||
return internalName;
|
||||
}
|
||||
|
||||
protected void setInternalName(String internalName) {
|
||||
this.internalName = internalName;
|
||||
this.modelName = internalName;
|
||||
}
|
||||
|
||||
public void addClassLoader(ClassLoader classLoader) {
|
||||
this.classLoaders.add(classLoader);
|
||||
if (parent != null) {
|
||||
|
|
@ -202,21 +196,21 @@ public abstract class ScopeModel implements ExtensionAccessor {
|
|||
public abstract Environment getModelEnvironment();
|
||||
|
||||
public String getInternalId() {
|
||||
// XxxModule-1.1
|
||||
if (this.internalName == null) {
|
||||
return null;
|
||||
}
|
||||
return this.internalName.substring(this.internalName.indexOf('-') + 1);
|
||||
return this.internalId;
|
||||
}
|
||||
|
||||
protected String buildInternalName(String type, String parentInternalId, long childIndex) {
|
||||
// FrameworkModel-1
|
||||
// ApplicationModel-1.1
|
||||
// ModuleModel-1.1.1
|
||||
if (parentInternalId != null) {
|
||||
return type + "-" + parentInternalId + "." + childIndex;
|
||||
void setInternalId(String internalId) {
|
||||
this.internalId = internalId;
|
||||
}
|
||||
|
||||
protected String buildInternalId(String parentInternalId, long childIndex) {
|
||||
// FrameworkModel 1
|
||||
// ApplicationModel 1.1
|
||||
// ModuleModel 1.1.1
|
||||
if (StringUtils.hasText(parentInternalId)) {
|
||||
return parentInternalId + "." + childIndex;
|
||||
} else {
|
||||
return type + "-" + childIndex;
|
||||
return "" + childIndex;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -226,5 +220,57 @@ public abstract class ScopeModel implements ExtensionAccessor {
|
|||
|
||||
public void setModelName(String modelName) {
|
||||
this.modelName = modelName;
|
||||
this.desc = buildDesc();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the describe string of this scope model
|
||||
*/
|
||||
public String getDesc() {
|
||||
if (this.desc == null) {
|
||||
this.desc = buildDesc();
|
||||
}
|
||||
return this.desc;
|
||||
}
|
||||
|
||||
private String buildDesc() {
|
||||
// Dubbo Framework[1]
|
||||
// Dubbo Application[1.1](appName)
|
||||
// Dubbo Module[1.1.1](appName/moduleName)
|
||||
String type = this.getClass().getSimpleName().replace("Model", "");
|
||||
String desc = "Dubbo " + type + "[" + this.getInternalId() + "]";
|
||||
|
||||
// append model name path
|
||||
String modelNamePath = this.getModelNamePath();
|
||||
if (StringUtils.hasText(modelNamePath)) {
|
||||
desc += "(" + modelNamePath + ")";
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
||||
private String getModelNamePath() {
|
||||
if (this instanceof ApplicationModel) {
|
||||
return safeGetAppName((ApplicationModel) this);
|
||||
} else if (this instanceof ModuleModel) {
|
||||
String modelName = this.getModelName();
|
||||
if (StringUtils.hasText(modelName)) {
|
||||
// appName/moduleName
|
||||
return safeGetAppName(((ModuleModel) this).getApplicationModel()) + "/" + modelName;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String safeGetAppName(ApplicationModel applicationModel) {
|
||||
String modelName = applicationModel.getModelName();
|
||||
if (StringUtils.isBlank(modelName)) {
|
||||
modelName = "unknown"; // unknown application
|
||||
}
|
||||
return modelName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getDesc();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,9 +56,20 @@ public class ScopeModelUtil {
|
|||
}
|
||||
|
||||
public static ApplicationModel getApplicationModel(ScopeModel scopeModel) {
|
||||
return getOrDefaultApplicationModel(scopeModel);
|
||||
}
|
||||
|
||||
public static ApplicationModel getOrDefaultApplicationModel(ScopeModel scopeModel) {
|
||||
if (scopeModel == null) {
|
||||
return ApplicationModel.defaultModel();
|
||||
}
|
||||
return getOrNullApplicationModel(scopeModel);
|
||||
}
|
||||
|
||||
public static ApplicationModel getOrNullApplicationModel(ScopeModel scopeModel) {
|
||||
if (scopeModel == null) {
|
||||
return null;
|
||||
}
|
||||
if (scopeModel instanceof ApplicationModel) {
|
||||
return (ApplicationModel) scopeModel;
|
||||
} else if (scopeModel instanceof ModuleModel) {
|
||||
|
|
@ -69,13 +80,6 @@ public class ScopeModelUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public static ScopeModel getOrDefaultApplicationModel(ScopeModel scopeModel) {
|
||||
if (scopeModel == null) {
|
||||
return ApplicationModel.defaultModel();
|
||||
}
|
||||
return scopeModel;
|
||||
}
|
||||
|
||||
public static FrameworkModel getFrameworkModel(ScopeModel scopeModel) {
|
||||
if (scopeModel == null) {
|
||||
return FrameworkModel.defaultModel();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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 org.apache.dubbo.rpc.model;
|
||||
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ScopeModelTest {
|
||||
|
||||
@Test
|
||||
public void testCreateOnDestroy() throws InterruptedException {
|
||||
FrameworkModel.destroyAll();
|
||||
|
||||
FrameworkModel frameworkModel = new FrameworkModel();
|
||||
ApplicationModel applicationModel1 = frameworkModel.newApplication();
|
||||
ApplicationModel applicationModel2 = frameworkModel.newApplication();
|
||||
|
||||
List<Throwable> errors = new ArrayList<>();
|
||||
applicationModel1.addDestroyListener(scopeModel -> {
|
||||
try {
|
||||
try {
|
||||
applicationModel1.getDefaultModule();
|
||||
Assertions.fail("Cannot create new module after application model destroyed");
|
||||
} catch (Exception e) {
|
||||
Assertions.assertEquals("ApplicationModel is destroyed", e.getMessage(), StringUtils.toString(e));
|
||||
}
|
||||
|
||||
try {
|
||||
applicationModel1.newModule();
|
||||
Assertions.fail("Cannot create new module after application model destroyed");
|
||||
} catch (Exception e) {
|
||||
Assertions.assertEquals("ApplicationModel is destroyed", e.getMessage(), StringUtils.toString(e));
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
errors.add(e);
|
||||
}
|
||||
});
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
frameworkModel.addDestroyListener(scopeModel -> {
|
||||
try {
|
||||
try {
|
||||
frameworkModel.defaultApplication();
|
||||
Assertions.fail("Cannot create new application after framework model destroyed");
|
||||
} catch (Exception e) {
|
||||
Assertions.assertEquals("FrameworkModel is destroyed", e.getMessage(), StringUtils.toString(e));
|
||||
}
|
||||
|
||||
try {
|
||||
frameworkModel.newApplication();
|
||||
Assertions.fail("Cannot create new application after framework model destroyed");
|
||||
} catch (Exception e) {
|
||||
Assertions.assertEquals("FrameworkModel is destroyed", e.getMessage(), StringUtils.toString(e));
|
||||
}
|
||||
|
||||
try {
|
||||
ApplicationModel.defaultModel();
|
||||
Assertions.fail("Cannot create new application after framework model destroyed");
|
||||
} catch (Exception e) {
|
||||
Assertions.assertEquals("FrameworkModel is destroyed", e.getMessage(), StringUtils.toString(e));
|
||||
}
|
||||
|
||||
try {
|
||||
FrameworkModel.defaultModel().defaultApplication();
|
||||
Assertions.fail("Cannot create new application after framework model destroyed");
|
||||
} catch (Exception e) {
|
||||
Assertions.assertEquals("FrameworkModel is destroyed", e.getMessage(), StringUtils.toString(e));
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
errors.add(ex);
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
// destroy frameworkModel
|
||||
frameworkModel.destroy();
|
||||
latch.await();
|
||||
|
||||
String errorMsg = null;
|
||||
for (Throwable throwable : errors) {
|
||||
errorMsg = StringUtils.toString(throwable);
|
||||
errorMsg += "\n";
|
||||
}
|
||||
Assertions.assertEquals(0, errors.size(), "Error occurred while destroy FrameworkModel: "+ errorMsg);
|
||||
|
||||
// destroy all FrameworkModel
|
||||
FrameworkModel.destroyAll();
|
||||
List<String> remainFrameworks = FrameworkModel.getAllInstances().stream().map(m -> m.getDesc()).collect(Collectors.toList());
|
||||
Assertions.assertEquals(0, FrameworkModel.getAllInstances().size(), "FrameworkModel is not completely destroyed: " + remainFrameworks);
|
||||
}
|
||||
}
|
||||
|
|
@ -27,8 +27,15 @@
|
|||
<param name="ConversionPattern" value="%d %p [%c:%M] - %m%n"/>
|
||||
</layout>
|
||||
</appender>
|
||||
|
||||
<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
|
||||
<layout class="org.apache.log4j.PatternLayout">
|
||||
<param name="ConversionPattern" value="[%d{dd/MM/yy HH:mm:ss:SSS z}] %t %5p %c{2}: %m%n"/>
|
||||
</layout>
|
||||
</appender>
|
||||
<root>
|
||||
<level value="DEBUG"/>
|
||||
<level value="INFO"/>
|
||||
<appender-ref ref="dubbo"/>
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</root>
|
||||
</log4j:configuration>
|
||||
</log4j:configuration>
|
||||
|
|
|
|||
|
|
@ -114,7 +114,6 @@ public class DefaultApplicationDeployer extends AbstractDeployer<ApplicationMode
|
|||
private volatile MetadataServiceExporter metadataServiceExporter;
|
||||
|
||||
private ScheduledFuture<?> asyncMetadataFuture;
|
||||
private String identifier;
|
||||
private volatile CompletableFuture startFuture;
|
||||
private DubboShutdownHook dubboShutdownHook;
|
||||
private Object stateLock = new Object();
|
||||
|
|
@ -238,6 +237,11 @@ public class DefaultApplicationDeployer extends AbstractDeployer<ApplicationMode
|
|||
// load application config
|
||||
configManager.loadConfigsOfTypeFromProps(ApplicationConfig.class);
|
||||
|
||||
// try set model name
|
||||
if (StringUtils.isBlank(applicationModel.getModelName())) {
|
||||
applicationModel.setModelName(applicationModel.tryGetApplicationName());
|
||||
}
|
||||
|
||||
// load config centers
|
||||
configManager.loadConfigsOfTypeFromProps(ConfigCenterConfig.class);
|
||||
|
||||
|
|
@ -273,8 +277,6 @@ public class DefaultApplicationDeployer extends AbstractDeployer<ApplicationMode
|
|||
}
|
||||
environment.setDynamicConfiguration(compositeDynamicConfiguration);
|
||||
}
|
||||
|
||||
configManager.refreshAll();
|
||||
}
|
||||
|
||||
private void startMetadataCenter() {
|
||||
|
|
@ -1081,16 +1083,4 @@ public class DefaultApplicationDeployer extends AbstractDeployer<ApplicationMode
|
|||
return configManager.getApplicationOrElseThrow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
if (identifier == null) {
|
||||
identifier = "Dubbo application[" + applicationModel.getInternalId() + "]";
|
||||
if (applicationModel.getModelName() != null
|
||||
&& !StringUtils.isEquals(applicationModel.getModelName(), applicationModel.getInternalName())) {
|
||||
identifier += "(" + applicationModel.getModelName() + ")";
|
||||
}
|
||||
}
|
||||
return identifier;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import org.apache.dubbo.common.deploy.ModuleDeployer;
|
|||
import org.apache.dubbo.common.logger.Logger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.config.ConsumerConfig;
|
||||
import org.apache.dubbo.config.ModuleConfig;
|
||||
import org.apache.dubbo.config.ProviderConfig;
|
||||
|
|
@ -66,7 +65,6 @@ public class DefaultModuleDeployer extends AbstractDeployer<ModuleModel> impleme
|
|||
private final ModuleConfigManager configManager;
|
||||
|
||||
private final SimpleReferenceCache referenceCache;
|
||||
private String identifier;
|
||||
|
||||
private ApplicationDeployer applicationDeployer;
|
||||
private CompletableFuture startFuture;
|
||||
|
|
@ -436,17 +434,6 @@ public class DefaultModuleDeployer extends AbstractDeployer<ModuleModel> impleme
|
|||
.isPresent();
|
||||
}
|
||||
|
||||
public String getIdentifier() {
|
||||
if (identifier == null) {
|
||||
identifier = "Dubbo module[" + moduleModel.getInternalId() + "]";
|
||||
if (moduleModel.getModelName() != null
|
||||
&& !StringUtils.isEquals(moduleModel.getModelName(), moduleModel.getInternalName())) {
|
||||
identifier += "(" + moduleModel.getModelName() + ")";
|
||||
}
|
||||
}
|
||||
return identifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReferenceCache getReferenceCache() {
|
||||
return referenceCache;
|
||||
|
|
|
|||
|
|
@ -209,6 +209,7 @@ public class ServiceConfigTest {
|
|||
delayService.addServiceListener(new ServiceListener() {
|
||||
@Override
|
||||
public void exported(ServiceConfig sc) {
|
||||
assertEquals(delayService, sc);
|
||||
assertThat(delayService.getExportedUrls(), hasSize(1));
|
||||
latch.countDown();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,6 +129,8 @@ public class DubboDeployApplicationListener implements ApplicationListener<Appli
|
|||
} catch (Exception e) {
|
||||
logger.error("An error occurred when stop dubbo module: " + e.getMessage(), e);
|
||||
}
|
||||
// remove context bind cache
|
||||
DubboSpringInitializer.remove(event.getApplicationContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -16,13 +16,18 @@
|
|||
*/
|
||||
package org.apache.dubbo.config.spring.context;
|
||||
|
||||
import org.apache.dubbo.common.logger.Logger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.config.spring.util.DubboBeanUtils;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.rpc.model.FrameworkModel;
|
||||
import org.apache.dubbo.rpc.model.ModuleModel;
|
||||
import org.apache.dubbo.rpc.model.ScopeModel;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
|
@ -33,6 +38,8 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
*/
|
||||
public class DubboSpringInitializer {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DubboSpringInitializer.class);
|
||||
|
||||
private static Map<BeanDefinitionRegistry, DubboSpringInitContext> contextMap = new ConcurrentHashMap<>();
|
||||
|
||||
private DubboSpringInitializer() {
|
||||
|
|
@ -40,6 +47,7 @@ public class DubboSpringInitializer {
|
|||
|
||||
public static void initialize(BeanDefinitionRegistry registry) {
|
||||
|
||||
// Spring ApplicationContext may not ready at this moment (e.g. load from xml), so use registry as key
|
||||
if (contextMap.putIfAbsent(registry, new DubboSpringInitContext()) != null) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -54,6 +62,39 @@ public class DubboSpringInitializer {
|
|||
initContext(context, registry, beanFactory);
|
||||
}
|
||||
|
||||
public static boolean remove(BeanDefinitionRegistry registry) {
|
||||
return contextMap.remove(registry) != null;
|
||||
}
|
||||
|
||||
public static boolean remove(ApplicationContext springContext) {
|
||||
for (Map.Entry<BeanDefinitionRegistry, DubboSpringInitContext> entry : contextMap.entrySet()) {
|
||||
DubboSpringInitContext initContext = entry.getValue();
|
||||
if (initContext.getApplicationContext() == springContext ||
|
||||
initContext.getBeanFactory() == springContext.getAutowireCapableBeanFactory() ||
|
||||
initContext.getRegistry() == springContext.getAutowireCapableBeanFactory()
|
||||
) {
|
||||
DubboSpringInitContext context = contextMap.remove(entry.getKey());
|
||||
logger.info("Unbind " + safeGetModelDesc(context.getModuleModel()) + " from spring container: " +
|
||||
ObjectUtils.identityToString(entry.getKey()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static Map<BeanDefinitionRegistry, DubboSpringInitContext> getContextMap() {
|
||||
return contextMap;
|
||||
}
|
||||
|
||||
static DubboSpringInitContext findBySpringContext(ApplicationContext applicationContext) {
|
||||
for (Map.Entry<BeanDefinitionRegistry, DubboSpringInitContext> entry : contextMap.entrySet()) {
|
||||
DubboSpringInitContext initContext = entry.getValue();
|
||||
if (initContext.getApplicationContext() == applicationContext) {
|
||||
return initContext;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void initContext(DubboSpringInitContext context, BeanDefinitionRegistry registry,
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
|
|
@ -63,21 +104,28 @@ public class DubboSpringInitializer {
|
|||
// customize context, you can change the bind module model via DubboSpringInitCustomizer SPI
|
||||
customize(context);
|
||||
|
||||
// init ApplicationModel
|
||||
ApplicationModel applicationModel = context.getApplicationModel();
|
||||
if (applicationModel == null) {
|
||||
// init ModuleModel
|
||||
ModuleModel moduleModel = context.getModuleModel();
|
||||
if (moduleModel == null) {
|
||||
ApplicationModel applicationModel;
|
||||
if (findContextForApplication(ApplicationModel.defaultModel()) == null) {
|
||||
// first spring context use default application instance
|
||||
applicationModel = ApplicationModel.defaultModel();
|
||||
logger.info("Use default application: " + safeGetModelDesc(applicationModel));
|
||||
} else {
|
||||
// create an new application instance for later spring context
|
||||
applicationModel = FrameworkModel.defaultModel().newApplication();
|
||||
logger.info("Create new application: " + safeGetModelDesc(applicationModel));
|
||||
}
|
||||
|
||||
// init ModuleModel
|
||||
ModuleModel moduleModel = applicationModel.getDefaultModule();
|
||||
moduleModel = applicationModel.getDefaultModule();
|
||||
context.setModuleModel(moduleModel);
|
||||
logger.info("Use default module model of target application: " + safeGetModelDesc(moduleModel));
|
||||
} else {
|
||||
logger.info("Use module model from customizer: " + safeGetModelDesc(moduleModel));
|
||||
}
|
||||
logger.info("Bind " + safeGetModelDesc(moduleModel) + " to spring container: " + ObjectUtils.identityToString(registry));
|
||||
|
||||
// set module attributes
|
||||
if (context.getModuleAttributes().size() > 0) {
|
||||
|
|
@ -94,6 +142,10 @@ public class DubboSpringInitializer {
|
|||
DubboBeanUtils.registerCommonBeans(registry);
|
||||
}
|
||||
|
||||
private static String safeGetModelDesc(ScopeModel scopeModel) {
|
||||
return scopeModel != null ? scopeModel.getDesc() : null;
|
||||
}
|
||||
|
||||
private static ConfigurableListableBeanFactory findBeanFactory(BeanDefinitionRegistry registry) {
|
||||
ConfigurableListableBeanFactory beanFactory = null;
|
||||
if (registry instanceof ConfigurableListableBeanFactory) {
|
||||
|
|
|
|||
|
|
@ -14,13 +14,14 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.config.spring;
|
||||
package org.apache.dubbo.config.spring.context;
|
||||
|
||||
import org.apache.dubbo.common.deploy.ApplicationDeployer;
|
||||
import org.apache.dubbo.common.deploy.DeployState;
|
||||
import org.apache.dubbo.common.deploy.ModuleDeployer;
|
||||
import org.apache.dubbo.config.bootstrap.DubboBootstrap;
|
||||
import org.apache.dubbo.config.spring.context.DubboSpringInitCustomizerHolder;
|
||||
import org.apache.dubbo.config.spring.DubboStateListener;
|
||||
import org.apache.dubbo.config.spring.SysProps;
|
||||
import org.apache.dubbo.rpc.model.ModuleModel;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
|
@ -58,6 +59,7 @@ public class KeepRunningOnSpringClosedTest {
|
|||
Assertions.assertEquals(DeployState.STARTED, applicationDeployer.getState());
|
||||
Assertions.assertEquals(true, applicationDeployer.isStarted());
|
||||
Assertions.assertEquals(false, applicationDeployer.isStopped());
|
||||
Assertions.assertNotNull(DubboSpringInitializer.findBySpringContext(providerContext));
|
||||
|
||||
// close spring context
|
||||
providerContext.close();
|
||||
|
|
@ -66,6 +68,7 @@ public class KeepRunningOnSpringClosedTest {
|
|||
Assertions.assertEquals(DeployState.STARTED, applicationDeployer.getState());
|
||||
Assertions.assertEquals(true, applicationDeployer.isStarted());
|
||||
Assertions.assertEquals(false, applicationDeployer.isStopped());
|
||||
Assertions.assertNull(DubboSpringInitializer.findBySpringContext(providerContext));
|
||||
} finally {
|
||||
DubboBootstrap.getInstance().stop();
|
||||
SysProps.clear();
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
</appender>
|
||||
|
||||
<logger name="org.apache.dubbo.config" additivity="false">
|
||||
<level value="WARN"/>
|
||||
<level value="INFO"/>
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</logger>
|
||||
|
||||
|
|
|
|||
|
|
@ -508,6 +508,11 @@ public class InstanceAddressURL extends URL {
|
|||
return instance.getOrDefaultApplicationModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationModel getApplicationModel() {
|
||||
return instance.getApplicationModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServiceModel getServiceModel() {
|
||||
return RpcContext.getServiceContext().getConsumerUrl().getServiceModel();
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import org.apache.dubbo.common.utils.StringUtils;
|
|||
import org.apache.dubbo.metadata.MetadataInfo;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.apache.dubbo.rpc.model.FrameworkModel;
|
||||
import org.apache.dubbo.rpc.model.ModuleModel;
|
||||
import org.apache.dubbo.rpc.model.ScopeModel;
|
||||
import org.apache.dubbo.rpc.model.ServiceModel;
|
||||
|
||||
|
|
@ -294,6 +295,16 @@ public class OverrideInstanceAddressURL extends InstanceAddressURL {
|
|||
return originUrl.getOrDefaultApplicationModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationModel getApplicationModel() {
|
||||
return originUrl.getApplicationModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleModel getOrDefaultModuleModel() {
|
||||
return originUrl.getOrDefaultModuleModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServiceModel getServiceModel() {
|
||||
return originUrl.getServiceModel();
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import org.apache.dubbo.common.serialize.ObjectOutput;
|
|||
import org.apache.dubbo.common.serialize.Serialization;
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.remoting.Constants;
|
||||
import org.apache.dubbo.rpc.model.FrameworkModel;
|
||||
import org.apache.dubbo.rpc.model.FrameworkServiceRepository;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
|
@ -50,9 +51,10 @@ public class CodecSupport {
|
|||
private static final ThreadLocal<byte[]> TL_BUFFER = ThreadLocal.withInitial(() -> new byte[1024]);
|
||||
|
||||
static {
|
||||
Set<String> supportedExtensions = ExtensionLoader.getExtensionLoader(Serialization.class).getSupportedExtensions();
|
||||
ExtensionLoader<Serialization> extensionLoader = FrameworkModel.defaultModel().getExtensionLoader(Serialization.class);
|
||||
Set<String> supportedExtensions = extensionLoader.getSupportedExtensions();
|
||||
for (String name : supportedExtensions) {
|
||||
Serialization serialization = ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(name);
|
||||
Serialization serialization = extensionLoader.getExtension(name);
|
||||
byte idByte = serialization.getContentTypeId();
|
||||
if (ID_SERIALIZATION_MAP.containsKey(idByte)) {
|
||||
logger.error("Serialization extension " + serialization.getClass().getName()
|
||||
|
|
|
|||
|
|
@ -132,17 +132,19 @@ public class WrappedChannelHandler implements ChannelHandlerDelegate {
|
|||
* @return
|
||||
*/
|
||||
public ExecutorService getSharedExecutorService() {
|
||||
// Application may be destroyed before channel disconnected, avoid create new application model
|
||||
// see https://github.com/apache/dubbo/issues/9127
|
||||
if (url.getApplicationModel() == null || url.getApplicationModel().isDestroyed()) {
|
||||
return GlobalResourcesRepository.getGlobalExecutorService();
|
||||
}
|
||||
|
||||
// note: url.getOrDefaultApplicationModel() may create new application model
|
||||
ApplicationModel applicationModel = url.getOrDefaultApplicationModel();
|
||||
ExecutorRepository executorRepository =
|
||||
applicationModel.getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
|
||||
ExecutorService executor = executorRepository.getExecutor(url);
|
||||
if (executor == null) {
|
||||
// if application is destroyed, use global executor service
|
||||
if (applicationModel.isDestroyed()) {
|
||||
executor = GlobalResourcesRepository.getGlobalExecutorService();
|
||||
}else {
|
||||
executor = executorRepository.createExecutorIfAbsent(url);
|
||||
}
|
||||
executor = executorRepository.createExecutorIfAbsent(url);
|
||||
}
|
||||
return executor;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import org.apache.dubbo.remoting.RemotingException;
|
|||
import org.apache.dubbo.remoting.exchange.Request;
|
||||
import org.apache.dubbo.remoting.exchange.Response;
|
||||
import org.apache.dubbo.remoting.transport.dispatcher.connection.ConnectionOrderedChannelHandler;
|
||||
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
|
|
@ -35,6 +35,7 @@ public class ConnectChannelHandlerTest extends WrappedChannelHandlerTest {
|
|||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
url = url.setScopeModel(ApplicationModel.defaultModel());
|
||||
handler = new ConnectionOrderedChannelHandler(new BizChannelHandler(true), url);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import org.apache.dubbo.remoting.exchange.Request;
|
|||
import org.apache.dubbo.remoting.exchange.Response;
|
||||
import org.apache.dubbo.remoting.exchange.support.DefaultFuture;
|
||||
import org.apache.dubbo.remoting.transport.dispatcher.WrappedChannelHandler;
|
||||
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
|
@ -43,6 +43,7 @@ public class WrappedChannelHandlerTest {
|
|||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
url = url.setScopeModel(ApplicationModel.defaultModel());
|
||||
handler = new WrappedChannelHandler(new BizChannelHandler(true), url);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import org.apache.dubbo.common.utils.NetUtils;
|
|||
import org.apache.dubbo.remoting.Channel;
|
||||
import org.apache.dubbo.remoting.ChannelHandler;
|
||||
import org.apache.dubbo.remoting.RemotingException;
|
||||
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
|
@ -51,8 +51,12 @@ public class ThreadNameTest {
|
|||
@BeforeEach
|
||||
public void before() throws Exception {
|
||||
int port = NetUtils.getAvailablePort(20880 + new Random().nextInt(10000));
|
||||
serverURL = URL.valueOf("telnet://localhost?side=provider").setPort(port);
|
||||
clientURL = URL.valueOf("telnet://localhost?side=consumer").setPort(port);
|
||||
serverURL = URL.valueOf("telnet://localhost?side=provider")
|
||||
.setPort(port)
|
||||
.setScopeModel(ApplicationModel.defaultModel());
|
||||
clientURL = URL.valueOf("telnet://localhost?side=consumer")
|
||||
.setPort(port)
|
||||
.setScopeModel(ApplicationModel.defaultModel());
|
||||
|
||||
serverHandler = new ThreadNameVerifyHandler(serverRegex, false, serverLatch);
|
||||
clientHandler = new ThreadNameVerifyHandler(clientRegex, true, clientLatch);
|
||||
|
|
|
|||
|
|
@ -30,10 +30,12 @@ import java.lang.reflect.Type;
|
|||
*/
|
||||
public class Hessian2ObjectInput implements ObjectInput, Cleanable {
|
||||
private final Hessian2Input mH2i;
|
||||
private final Hessian2FactoryInitializer hessian2FactoryInitializer;
|
||||
|
||||
public Hessian2ObjectInput(InputStream is) {
|
||||
mH2i = new Hessian2Input(is);
|
||||
mH2i.setSerializerFactory(Hessian2FactoryInitializer.getInstance().getSerializerFactory());
|
||||
hessian2FactoryInitializer = Hessian2FactoryInitializer.getInstance();
|
||||
mH2i.setSerializerFactory(hessian2FactoryInitializer.getSerializerFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -84,7 +86,7 @@ public class Hessian2ObjectInput implements ObjectInput, Cleanable {
|
|||
@Override
|
||||
public Object readObject() throws IOException {
|
||||
if (!mH2i.getSerializerFactory().getClassLoader().equals(Thread.currentThread().getContextClassLoader())) {
|
||||
mH2i.setSerializerFactory(Hessian2FactoryInitializer.getInstance().getSerializerFactory());
|
||||
mH2i.setSerializerFactory(hessian2FactoryInitializer.getSerializerFactory());
|
||||
}
|
||||
return mH2i.readObject();
|
||||
}
|
||||
|
|
@ -94,7 +96,7 @@ public class Hessian2ObjectInput implements ObjectInput, Cleanable {
|
|||
public <T> T readObject(Class<T> cls) throws IOException,
|
||||
ClassNotFoundException {
|
||||
if (!mH2i.getSerializerFactory().getClassLoader().equals(Thread.currentThread().getContextClassLoader())) {
|
||||
mH2i.setSerializerFactory(Hessian2FactoryInitializer.getInstance().getSerializerFactory());
|
||||
mH2i.setSerializerFactory(hessian2FactoryInitializer.getSerializerFactory());
|
||||
}
|
||||
return (T) mH2i.readObject(cls);
|
||||
}
|
||||
|
|
@ -102,7 +104,7 @@ public class Hessian2ObjectInput implements ObjectInput, Cleanable {
|
|||
@Override
|
||||
public <T> T readObject(Class<T> cls, Type type) throws IOException, ClassNotFoundException {
|
||||
if (!mH2i.getSerializerFactory().getClassLoader().equals(Thread.currentThread().getContextClassLoader())) {
|
||||
mH2i.setSerializerFactory(Hessian2FactoryInitializer.getInstance().getSerializerFactory());
|
||||
mH2i.setSerializerFactory(hessian2FactoryInitializer.getSerializerFactory());
|
||||
}
|
||||
return readObject(cls);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,23 +16,23 @@
|
|||
*/
|
||||
package org.apache.dubbo.common.serialize.hessian2.dubbo;
|
||||
|
||||
import com.alibaba.com.caucho.hessian.io.SerializerFactory;
|
||||
import org.apache.dubbo.common.extension.ExtensionLoader;
|
||||
import org.apache.dubbo.common.extension.ExtensionScope;
|
||||
import org.apache.dubbo.common.extension.SPI;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
|
||||
import com.alibaba.com.caucho.hessian.io.SerializerFactory;
|
||||
import org.apache.dubbo.rpc.model.FrameworkModel;
|
||||
|
||||
@SPI(value = "default", scope = ExtensionScope.FRAMEWORK)
|
||||
public interface Hessian2FactoryInitializer {
|
||||
String WHITELIST = "dubbo.application.hessian2.whitelist";
|
||||
String ALLOW = "dubbo.application.hessian2.allow";
|
||||
String DENY = "dubbo.application.hessian2.deny";
|
||||
ExtensionLoader<Hessian2FactoryInitializer> loader = ExtensionLoader.getExtensionLoader(Hessian2FactoryInitializer.class);
|
||||
|
||||
SerializerFactory getSerializerFactory();
|
||||
|
||||
static Hessian2FactoryInitializer getInstance() {
|
||||
ExtensionLoader<Hessian2FactoryInitializer> loader = FrameworkModel.defaultModel().getExtensionLoader(Hessian2FactoryInitializer.class);
|
||||
String whitelist = System.getProperty(WHITELIST);
|
||||
if (StringUtils.isNotEmpty(whitelist)) {
|
||||
return loader.getExtension("whitelist");
|
||||
|
|
|
|||
|
|
@ -196,14 +196,14 @@ public class DubboTestChecker implements TestExecutionListener {
|
|||
|
||||
@Override
|
||||
public void executionStarted(TestIdentifier testIdentifier) {
|
||||
// TestSource testSource = testIdentifier.getSource().orElse(null);
|
||||
// if (testSource instanceof ClassSource) {
|
||||
TestSource testSource = testIdentifier.getSource().orElse(null);
|
||||
if (testSource instanceof ClassSource) {
|
||||
// ClassSource source = (ClassSource) testSource;
|
||||
// log("Run test class: " + source.getClassName());
|
||||
// } else if (testSource instanceof MethodSource) {
|
||||
// MethodSource source = (MethodSource) testSource;
|
||||
// log("Run test method: " + source.getClassName() + "#" + source.getMethodName());
|
||||
// }
|
||||
} else if (testSource instanceof MethodSource) {
|
||||
MethodSource source = (MethodSource) testSource;
|
||||
log("Run test method: " + source.getClassName() + "#" + source.getMethodName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
Loading…
Reference in New Issue