Skip to content

Commit

Permalink
重构动态模型的租户支持
Browse files Browse the repository at this point in the history
  • Loading branch information
entropy-cloud committed Aug 21, 2024
1 parent 56f5740 commit 574bcf1
Show file tree
Hide file tree
Showing 25 changed files with 272 additions and 208 deletions.
4 changes: 0 additions & 4 deletions nop-biz/src/main/java/io/nop/biz/api/IBizObjectManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@
import io.nop.api.core.beans.ApiResponse;
import io.nop.api.core.exceptions.NopException;
import io.nop.core.context.IServiceContext;
import io.nop.graphql.core.reflection.GraphQLBizModel;

import java.util.Map;
import java.util.Set;

public interface IBizObjectManager {
Expand All @@ -36,6 +34,4 @@ public interface IBizObjectManager {
ApiResponse<?> buildResponse(String locale, Object result, IServiceContext rt);

void clearCache();

void updateDynBizModels(Map<String, GraphQLBizModel> dynBizModels);
}
6 changes: 3 additions & 3 deletions nop-biz/src/main/java/io/nop/biz/impl/BizObjectBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public class BizObjectBuilder {
static final Logger LOG = LoggerFactory.getLogger(BizObjectImpl.class);
private final GraphQLBizModels bizModels;

private final Map<String, GraphQLBizModel> dynBizModels;
private final IDynamicBizModelProvider dynBizModels;
private final TypeRegistry typeRegistry;

private final List<IActionDecoratorCollector> collectors;
Expand All @@ -72,7 +72,7 @@ public class BizObjectBuilder {
private final IBizObjectManager bizObjectManager;

public BizObjectBuilder(IBizObjectManager bizObjectManager, GraphQLBizModels bizModels,
Map<String, GraphQLBizModel> dynBizModels, TypeRegistry typeRegistry,
IDynamicBizModelProvider dynBizModels, TypeRegistry typeRegistry,
List<IActionDecoratorCollector> collectors,
List<IGraphQLBizInitializer> bizInitializers,
IMakerCheckerProvider makerCheckerProvider) {
Expand Down Expand Up @@ -194,7 +194,7 @@ private <T> IBizObjectQueryProcessor<T> buildQueryProcessor(String bizObjName) {
BizObjectImpl loadBizObjFromModel(String bizObjName) {
GraphQLBizModel gqlBizModel = bizModels.getBizModel(bizObjName);
if (gqlBizModel == null && dynBizModels != null)
gqlBizModel = dynBizModels.get(bizObjName);
gqlBizModel = dynBizModels.getBizModel(bizObjName);
if (gqlBizModel == null)
throw new NopException(ERR_BIZ_UNKNOWN_BIZ_OBJ_NAME).param(ARG_BIZ_OBJ_NAME, bizObjName);

Expand Down
38 changes: 14 additions & 24 deletions nop-biz/src/main/java/io/nop/biz/impl/BizObjectManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import io.nop.api.core.beans.ErrorBean;
import io.nop.api.core.beans.FieldSelectionBean;
import io.nop.api.core.exceptions.NopException;
import io.nop.api.core.util.Guard;
import io.nop.api.core.util.ICancellable;
import io.nop.biz.api.IBizObject;
import io.nop.biz.api.IBizObjectManager;
Expand All @@ -25,7 +24,6 @@
import io.nop.core.exceptions.ErrorMessageManager;
import io.nop.core.resource.cache.IResourceLoadingCache;
import io.nop.core.resource.tenant.ResourceTenantManager;
import io.nop.core.resource.tenant.TenantAwareModelReference;
import io.nop.graphql.core.GraphQLConstants;
import io.nop.graphql.core.ast.GraphQLDefinition;
import io.nop.graphql.core.ast.GraphQLDocument;
Expand All @@ -35,9 +33,9 @@
import io.nop.graphql.core.ast.GraphQLOperationType;
import io.nop.graphql.core.ast.GraphQLType;
import io.nop.graphql.core.ast.GraphQLTypeDefinition;
import io.nop.graphql.core.ast._gen._GraphQLTypeDefinition;
import io.nop.graphql.core.biz.IGraphQLBizInitializer;
import io.nop.graphql.core.biz.IGraphQLSchemaInitializer;
import io.nop.graphql.core.reflection.GraphQLBizModel;
import io.nop.graphql.core.reflection.GraphQLBizModels;
import io.nop.graphql.core.schema.IGraphQLSchemaLoader;
import io.nop.graphql.core.schema.TypeRegistry;
Expand Down Expand Up @@ -88,30 +86,21 @@ public class BizObjectManager implements IBizObjectManager, IGraphQLSchemaLoader

private IMakerCheckerProvider makerCheckerProvider;

private TenantAwareModelReference<Map<String, GraphQLBizModel>> dynBizModels = new TenantAwareModelReference<>("biz-gql-model-cache",
(name, tenantId) -> null);

private IDynamicBizModelProvider dynamicBizModelProvider;
private final IResourceLoadingCache<IBizObject> bizObjCache = ResourceTenantManager.instance().makeLoadingCache("biz-object-cache",
this::buildBizObject, null);

private Runnable cleanup;

public void setBizModelBeans(List<Object> bizModelBeans) {
this.bizModelBeans = bizModelBeans;
}

public void updateDynBizModels(Map<String, GraphQLBizModel> dynBizModels) {
Guard.notNull(dynBizModels, "dynBizModels");
Map<String, GraphQLBizModel> oldModels = this.dynBizModels.getModel();
if (oldModels != null) {
boolean checkChanged = bizObjCache.shouldCheckChanged();
for (String bizObjName : oldModels.keySet()) {
// 如果禁用了文件修改检查,则直接清空缓存
if (!checkChanged || !dynBizModels.containsKey(bizObjName)) {
removeCache(bizObjName);
}
}
}

this.dynBizModels.update(dynBizModels);
@Inject
public void setDynamicBizModelProvider(@Nullable IDynamicBizModelProvider dynamicBizModelProvider) {
this.dynamicBizModelProvider = dynamicBizModelProvider;
if (dynamicBizModelProvider != null)
cleanup = dynamicBizModelProvider.addOnChangeListener(this::removeCache);
}

public void setBizInitializers(List<IGraphQLBizInitializer> bizInitializers) {
Expand Down Expand Up @@ -183,12 +172,13 @@ public void destroy() {
if (typeRegistry != null)
typeRegistry.clear();
bizObjCache.clear();
dynBizModels.clear();
if (cleanup != null)
cleanup.run();
}

protected IBizObject buildBizObject(String bizObjName) {
try {
return new BizObjectBuilder(this, bizModels, dynBizModels.getModel(), typeRegistry,
return new BizObjectBuilder(this, bizModels, dynamicBizModelProvider, typeRegistry,
actionDecoratorCollectors, bizInitializers, makerCheckerProvider)
.buildBizObject(bizObjName);
} catch (NopException e) {
Expand Down Expand Up @@ -340,7 +330,7 @@ public Collection<GraphQLTypeDefinition> getTypeDefinitions() {
public GraphQLDocument getGraphQLDocument() {
GraphQLDocument doc = new GraphQLDocument();
List<GraphQLDefinition> defs = new ArrayList<>();
defs.addAll(getTypeDefinitions().stream().map(def -> def.deepClone()).collect(Collectors.toList()));
defs.addAll(getTypeDefinitions().stream().map(_GraphQLTypeDefinition::deepClone).collect(Collectors.toList()));

GraphQLObjectDefinition queryType = getObjDef(GraphQLOperationType.query);
if (queryType != null) {
Expand All @@ -366,7 +356,7 @@ private GraphQLObjectDefinition getObjDef(GraphQLOperationType opType) {
if (fields.isEmpty())
return null;

fields = fields.stream().map(f -> f.deepClone()).collect(Collectors.toList());
fields = fields.stream().map(GraphQLFieldDefinition::deepClone).collect(Collectors.toList());

GraphQLObjectDefinition objDef = new GraphQLObjectDefinition();
objDef.setExtension(true);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.nop.biz.impl;

import io.nop.graphql.core.reflection.GraphQLBizModel;

public interface IDynamicBizModelProvider {

GraphQLBizModel getBizModel(String bizObjName);

Runnable addOnChangeListener(ChangeListener listener);

interface ChangeListener {
void onBizObjRemoved(String bizObjName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class ConfigModelLoader {
public ConfigModel loadConfigModel() {
Map<String, ConfigVarModel> merged = new HashMap<>();

ModuleManager.instance().getEnabledModules().forEach(module -> {
ModuleManager.instance().getEnabledModules(false).forEach(module -> {
Map<String, ConfigVarModel> moduleVars = loadModuleConfigVars(module.getModuleId());
if (moduleVars != null) {
merge(merged, moduleVars);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class ErrorCodeMappingsLoader {
public Map<String, ErrorCodeMapping> loadErrorCodeMappings() {
Map<String, ErrorCodeMapping> merged = new HashMap<>();

ModuleManager.instance().getEnabledModules().forEach(module -> {
ModuleManager.instance().getEnabledModules(false).forEach(module -> {
Map<String, ErrorCodeMapping> moduleErrors = loadModuleErrors(module.getModuleId());
if (moduleErrors != null) {
merge(merged, moduleErrors);
Expand Down
125 changes: 57 additions & 68 deletions nop-core/src/main/java/io/nop/core/module/ModuleManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,33 @@
package io.nop.core.module;

import io.nop.api.core.annotations.core.GlobalInstance;
import io.nop.api.core.config.AppConfig;
import io.nop.api.core.context.ContextProvider;
import io.nop.api.core.util.Guard;
import io.nop.api.core.exceptions.NopException;
import io.nop.commons.util.StringHelper;
import io.nop.core.lang.json.JsonTool;
import io.nop.core.resource.IResource;
import io.nop.core.resource.ResourceHelper;
import io.nop.core.resource.VirtualFileSystem;
import io.nop.core.resource.impl.UnknownResource;
import io.nop.core.resource.tenant.ResourceTenantManager;
import io.nop.core.resource.tenant.TenantAwareModelReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicReference;

import static io.nop.core.CoreConfigs.CFG_MODULE_DISABLED_MODULE_NAMES;
import static io.nop.core.CoreConfigs.CFG_MODULE_ENABLED_MODULE_NAMES;
import static io.nop.core.CoreErrors.ARG_OTHER_PATH;
import static io.nop.core.CoreErrors.ARG_PATH;
import static io.nop.core.CoreErrors.ARG_STD_PATH;
import static io.nop.core.CoreErrors.ERR_RESOURCE_MODULE_PATH_RESOLVE_TO_MULTI_FILE;

@GlobalInstance
public class ModuleManager {
Expand All @@ -41,8 +46,7 @@ public static void registerInstance(ModuleManager instance) {
_instance = instance;
}

private final TenantAwareModelReference<Map<String, ModuleModel>> modules = new TenantAwareModelReference<>("module-model-cache",
(name, tenantId) -> new HashMap<>());
private final AtomicReference<Map<String, ModuleModel>> modules = new AtomicReference<>(new TreeMap<>());


public static ModuleManager instance() {
Expand Down Expand Up @@ -75,54 +79,15 @@ public void discover() {
ModuleModel module = loadModuleById(ResourceHelper.getModuleIdFromModuleName(moduleName));
modules.put(moduleName, module);
}
this.modules.updateStaticModel(modules);
this.modules.set(modules);
}

protected String getTenantId() {
return ContextProvider.currentTenantId();
}

private Map<String, ModuleModel> getModuleMap() {
Map<String, ModuleModel> map = new TreeMap<>();
String tenantId = getTenantId();
if (StringHelper.isEmpty(tenantId) || ResourceTenantManager.instance().isEnableTenantResource()) {
Map<String, ModuleModel> m = this.modules.getStaticModel();
if (m != null)
map.putAll(m);
} else {
Map<String, ModuleModel> m = this.modules.getTenantModel(tenantId);
if (m != null)
map.putAll(m);
}
return map;
}

public synchronized void updateDynModules(Map<String, ModuleModel> dynModules) {
Guard.notNull(dynModules, "dynModules");

Map<String, ModuleModel> modules = getModuleMap();
modules.entrySet().removeIf(entry -> {
if (dynModules.containsKey(entry.getKey()))
return false;
// 删除已经不存在的动态模块
return entry.getValue().isDynamic();
});

for (Map.Entry<String, ModuleModel> entry : dynModules.entrySet()) {
entry.getValue().setDynamic(true);
modules.put(entry.getKey(), entry.getValue());
}

Set<String> disabledModuleNames = CFG_MODULE_DISABLED_MODULE_NAMES.get();
if (disabledModuleNames != null) {
disabledModuleNames.forEach(modules::remove);
}

this.modules.update(modules);
}

public Map<String, ModuleModel> loadModules(Set<String> moduleNames) {
Map<String, ModuleModel> ret = new HashMap<>();
Map<String, ModuleModel> ret = new TreeMap<>();
for (String moduleName : moduleNames) {
String moduleId = ResourceHelper.getModuleIdFromModuleName(moduleName);
IResource resource = VirtualFileSystem.instance().getResource("/" + moduleId + "/_module");
Expand All @@ -134,7 +99,7 @@ public Map<String, ModuleModel> loadModules(Set<String> moduleNames) {
}

public void clear() {
modules.clear();
modules.set(new TreeMap<>());
}

private ModuleModel loadModuleById(String moduleId) {
Expand All @@ -150,33 +115,47 @@ private ModuleModel loadModuleById(String moduleId) {
return module;
}

private Map<String, ModuleModel> getAllModules() {
public Map<String, ModuleModel> getEnabledModuleMap(boolean includeTenant) {
Map<String, ModuleModel> ret = new TreeMap<>();
String tenantId = getTenantId();
Map<String, ModuleModel> map = modules.getStaticModel();
Map<String, ModuleModel> map = modules.get();
if (map != null) {
ret.putAll(map);
}

if (!StringHelper.isEmpty(tenantId) && ResourceTenantManager.instance().isEnableTenantResource()) {
Map<String, ModuleModel> m = modules.getTenantModel(tenantId);
if (m != null) {
ret.putAll(m);
if (includeTenant) {
String tenantId = getTenantId();
if (!StringHelper.isEmpty(tenantId) && ResourceTenantManager.instance().getTenantModuleDiscovery() != null) {
Map<String, ModuleModel> m = ResourceTenantManager.instance().getTenantModuleDiscovery().getEnabledTenantModules();
if (m != null) {
ret.putAll(m);
}
}
}
return ret;
}

public Set<String> getEnabledModuleNames() {
return getAllModules().keySet();
public Collection<ModuleModel> getEnabledModules(boolean includeTenant) {
return getEnabledModuleMap(includeTenant).values();

}

public Set<String> getEnabledModuleNames(boolean includeTenant) {
return getEnabledModuleMap(includeTenant).keySet();
}

public List<IResource> getAllModuleResources(boolean includeTenant, String filePathInModule) {
return getAllModuleResourcesInModules(getEnabledModules(includeTenant), filePathInModule);
}

public List<IResource> findModuleResources(boolean includeTenant, String filePathInModule, String suffix) {
return findModuleResourcesInModules(getEnabledModules(includeTenant), filePathInModule, suffix);
}

public Collection<ModuleModel> getEnabledModules() {
return getAllModules().values();
public IResource getModuleResource(boolean includeTenant, String filePathInModule) {
return getModuleResourceInModules(getEnabledModules(includeTenant), filePathInModule);
}

public List<IResource> getAllModuleResources(String filePathInModule) {
Collection<ModuleModel> modules = getEnabledModules();
public List<IResource> getAllModuleResourcesInModules(Collection<ModuleModel> modules, String filePathInModule) {
List<IResource> ret = new ArrayList<>(modules.size());
if (filePathInModule.startsWith("/"))
filePathInModule = filePathInModule.substring(1);
Expand All @@ -191,20 +170,30 @@ public List<IResource> getAllModuleResources(String filePathInModule) {
return ret;
}

public IResource getModuleResource(String filePathInModule) {
Collection<ModuleModel> modules = getEnabledModules();
public IResource getModuleResourceInModules(Collection<ModuleModel> modules, String filePathInModule) {
IResource resource = null;
for (ModuleModel module : modules) {
String path = StringHelper.appendPath('/' + module.getModuleId(), filePathInModule);
IResource resource = VirtualFileSystem.instance().getResource(path);
if (resource.exists()) {
return resource;
IResource moduleResource = VirtualFileSystem.instance().getResource(path, true);
if (moduleResource != null) {
if (AppConfig.isDebugMode()) {
if (resource != null)
throw new NopException(ERR_RESOURCE_MODULE_PATH_RESOLVE_TO_MULTI_FILE)
.param(ARG_STD_PATH, filePathInModule).param(ARG_PATH, resource.getPath())
.param(ARG_OTHER_PATH, moduleResource.getPath());
resource = moduleResource;
continue;
}
return moduleResource;
}
}
return null;
if (resource != null)
return resource;

return new UnknownResource(filePathInModule);
}

public List<IResource> findModuleResources(String filePathInModule, String suffix) {
Collection<ModuleModel> modules = getEnabledModules();
public List<IResource> findModuleResourcesInModules(Collection<ModuleModel> modules, String filePathInModule, String suffix) {
List<IResource> ret = new ArrayList<>();
for (ModuleModel module : modules) {
String path = StringHelper.appendPath('/' + module.getModuleId(), filePathInModule);
Expand Down
Loading

0 comments on commit 574bcf1

Please sign in to comment.