From 574bcf18e1de66e0de4fc75841b8661daf54830f Mon Sep 17 00:00:00 2001 From: canonical Date: Wed, 21 Aug 2024 23:25:37 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E5=8A=A8=E6=80=81=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E7=9A=84=E7=A7=9F=E6=88=B7=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/nop/biz/api/IBizObjectManager.java | 4 - .../io/nop/biz/impl/BizObjectBuilder.java | 6 +- .../io/nop/biz/impl/BizObjectManager.java | 38 ++---- .../biz/impl/IDynamicBizModelProvider.java | 14 ++ .../nop/config/model/ConfigModelLoader.java | 2 +- .../exceptions/ErrorCodeMappingsLoader.java | 2 +- .../io/nop/core/module/ModuleManager.java | 125 ++++++++---------- .../version/ResourceVersionedModelStore.java | 4 +- .../store/ModuleNamespaceHandler.java | 31 +---- .../tenant/ITenantModuleDiscovery.java | 4 - .../tenant/ResourceTenantManager.java | 40 ++++-- nop-dyn/model/nop-dyn.orm.xlsx | Bin 74116 -> 81526 bytes .../dao/model/DynEntityMetaToOrmModel.java | 10 +- .../nop/dyn/service/codegen/DynCodeGen.java | 58 ++++++-- .../service/codegen/InMemoryCodeCache.java | 93 +++++++++---- .../dyn/model/NopDynModule/NopDynModule.xbiz | 4 +- .../core/reflection/GraphQLBizModel.java | 10 ++ .../core/reflection/GraphQLBizModels.java | 4 +- .../ioc/loader/AppBeanContainerLoader.java | 2 +- .../io/nop/orm/model/OrmModelConstants.java | 4 + .../nop/orm/model/loader/OrmModelLoader.java | 11 +- .../factory/XplOrmInterceptorFactoryBean.java | 2 +- .../io/nop/orm/sql_lib/SqlLibManager.java | 2 +- .../nop/web/page/DynamicWebFileProvider.java | 8 +- .../java/io/nop/web/page/PageProvider.java | 2 +- 25 files changed, 272 insertions(+), 208 deletions(-) create mode 100644 nop-biz/src/main/java/io/nop/biz/impl/IDynamicBizModelProvider.java diff --git a/nop-biz/src/main/java/io/nop/biz/api/IBizObjectManager.java b/nop-biz/src/main/java/io/nop/biz/api/IBizObjectManager.java index d2809774e..c04f699e7 100644 --- a/nop-biz/src/main/java/io/nop/biz/api/IBizObjectManager.java +++ b/nop-biz/src/main/java/io/nop/biz/api/IBizObjectManager.java @@ -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 { @@ -36,6 +34,4 @@ public interface IBizObjectManager { ApiResponse buildResponse(String locale, Object result, IServiceContext rt); void clearCache(); - - void updateDynBizModels(Map dynBizModels); } \ No newline at end of file diff --git a/nop-biz/src/main/java/io/nop/biz/impl/BizObjectBuilder.java b/nop-biz/src/main/java/io/nop/biz/impl/BizObjectBuilder.java index 6d934d8f8..6f164fbd9 100644 --- a/nop-biz/src/main/java/io/nop/biz/impl/BizObjectBuilder.java +++ b/nop-biz/src/main/java/io/nop/biz/impl/BizObjectBuilder.java @@ -61,7 +61,7 @@ public class BizObjectBuilder { static final Logger LOG = LoggerFactory.getLogger(BizObjectImpl.class); private final GraphQLBizModels bizModels; - private final Map dynBizModels; + private final IDynamicBizModelProvider dynBizModels; private final TypeRegistry typeRegistry; private final List collectors; @@ -72,7 +72,7 @@ public class BizObjectBuilder { private final IBizObjectManager bizObjectManager; public BizObjectBuilder(IBizObjectManager bizObjectManager, GraphQLBizModels bizModels, - Map dynBizModels, TypeRegistry typeRegistry, + IDynamicBizModelProvider dynBizModels, TypeRegistry typeRegistry, List collectors, List bizInitializers, IMakerCheckerProvider makerCheckerProvider) { @@ -194,7 +194,7 @@ private IBizObjectQueryProcessor 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); diff --git a/nop-biz/src/main/java/io/nop/biz/impl/BizObjectManager.java b/nop-biz/src/main/java/io/nop/biz/impl/BizObjectManager.java index 1ae9af753..bfe6b24eb 100644 --- a/nop-biz/src/main/java/io/nop/biz/impl/BizObjectManager.java +++ b/nop-biz/src/main/java/io/nop/biz/impl/BizObjectManager.java @@ -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; @@ -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; @@ -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; @@ -88,30 +86,21 @@ public class BizObjectManager implements IBizObjectManager, IGraphQLSchemaLoader private IMakerCheckerProvider makerCheckerProvider; - private TenantAwareModelReference> dynBizModels = new TenantAwareModelReference<>("biz-gql-model-cache", - (name, tenantId) -> null); - + private IDynamicBizModelProvider dynamicBizModelProvider; private final IResourceLoadingCache bizObjCache = ResourceTenantManager.instance().makeLoadingCache("biz-object-cache", this::buildBizObject, null); + private Runnable cleanup; + public void setBizModelBeans(List bizModelBeans) { this.bizModelBeans = bizModelBeans; } - public void updateDynBizModels(Map dynBizModels) { - Guard.notNull(dynBizModels, "dynBizModels"); - Map 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 bizInitializers) { @@ -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) { @@ -340,7 +330,7 @@ public Collection getTypeDefinitions() { public GraphQLDocument getGraphQLDocument() { GraphQLDocument doc = new GraphQLDocument(); List 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) { @@ -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); diff --git a/nop-biz/src/main/java/io/nop/biz/impl/IDynamicBizModelProvider.java b/nop-biz/src/main/java/io/nop/biz/impl/IDynamicBizModelProvider.java new file mode 100644 index 000000000..4c76b5cde --- /dev/null +++ b/nop-biz/src/main/java/io/nop/biz/impl/IDynamicBizModelProvider.java @@ -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); + } +} diff --git a/nop-config/src/main/java/io/nop/config/model/ConfigModelLoader.java b/nop-config/src/main/java/io/nop/config/model/ConfigModelLoader.java index a92689c63..951df1d54 100644 --- a/nop-config/src/main/java/io/nop/config/model/ConfigModelLoader.java +++ b/nop-config/src/main/java/io/nop/config/model/ConfigModelLoader.java @@ -24,7 +24,7 @@ public class ConfigModelLoader { public ConfigModel loadConfigModel() { Map merged = new HashMap<>(); - ModuleManager.instance().getEnabledModules().forEach(module -> { + ModuleManager.instance().getEnabledModules(false).forEach(module -> { Map moduleVars = loadModuleConfigVars(module.getModuleId()); if (moduleVars != null) { merge(merged, moduleVars); diff --git a/nop-core/src/main/java/io/nop/core/exceptions/ErrorCodeMappingsLoader.java b/nop-core/src/main/java/io/nop/core/exceptions/ErrorCodeMappingsLoader.java index d5945e402..18bb12da7 100644 --- a/nop-core/src/main/java/io/nop/core/exceptions/ErrorCodeMappingsLoader.java +++ b/nop-core/src/main/java/io/nop/core/exceptions/ErrorCodeMappingsLoader.java @@ -25,7 +25,7 @@ public class ErrorCodeMappingsLoader { public Map loadErrorCodeMappings() { Map merged = new HashMap<>(); - ModuleManager.instance().getEnabledModules().forEach(module -> { + ModuleManager.instance().getEnabledModules(false).forEach(module -> { Map moduleErrors = loadModuleErrors(module.getModuleId()); if (moduleErrors != null) { merge(merged, moduleErrors); diff --git a/nop-core/src/main/java/io/nop/core/module/ModuleManager.java b/nop-core/src/main/java/io/nop/core/module/ModuleManager.java index 83a40a6b1..6480d4c16 100644 --- a/nop-core/src/main/java/io/nop/core/module/ModuleManager.java +++ b/nop-core/src/main/java/io/nop/core/module/ModuleManager.java @@ -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 { @@ -41,8 +46,7 @@ public static void registerInstance(ModuleManager instance) { _instance = instance; } - private final TenantAwareModelReference> modules = new TenantAwareModelReference<>("module-model-cache", - (name, tenantId) -> new HashMap<>()); + private final AtomicReference> modules = new AtomicReference<>(new TreeMap<>()); public static ModuleManager instance() { @@ -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 getModuleMap() { - Map map = new TreeMap<>(); - String tenantId = getTenantId(); - if (StringHelper.isEmpty(tenantId) || ResourceTenantManager.instance().isEnableTenantResource()) { - Map m = this.modules.getStaticModel(); - if (m != null) - map.putAll(m); - } else { - Map m = this.modules.getTenantModel(tenantId); - if (m != null) - map.putAll(m); - } - return map; - } - - public synchronized void updateDynModules(Map dynModules) { - Guard.notNull(dynModules, "dynModules"); - - Map modules = getModuleMap(); - modules.entrySet().removeIf(entry -> { - if (dynModules.containsKey(entry.getKey())) - return false; - // 删除已经不存在的动态模块 - return entry.getValue().isDynamic(); - }); - - for (Map.Entry entry : dynModules.entrySet()) { - entry.getValue().setDynamic(true); - modules.put(entry.getKey(), entry.getValue()); - } - - Set disabledModuleNames = CFG_MODULE_DISABLED_MODULE_NAMES.get(); - if (disabledModuleNames != null) { - disabledModuleNames.forEach(modules::remove); - } - - this.modules.update(modules); - } - public Map loadModules(Set moduleNames) { - Map ret = new HashMap<>(); + Map ret = new TreeMap<>(); for (String moduleName : moduleNames) { String moduleId = ResourceHelper.getModuleIdFromModuleName(moduleName); IResource resource = VirtualFileSystem.instance().getResource("/" + moduleId + "/_module"); @@ -134,7 +99,7 @@ public Map loadModules(Set moduleNames) { } public void clear() { - modules.clear(); + modules.set(new TreeMap<>()); } private ModuleModel loadModuleById(String moduleId) { @@ -150,33 +115,47 @@ private ModuleModel loadModuleById(String moduleId) { return module; } - private Map getAllModules() { + public Map getEnabledModuleMap(boolean includeTenant) { Map ret = new TreeMap<>(); - String tenantId = getTenantId(); - Map map = modules.getStaticModel(); + Map map = modules.get(); if (map != null) { ret.putAll(map); } - if (!StringHelper.isEmpty(tenantId) && ResourceTenantManager.instance().isEnableTenantResource()) { - Map m = modules.getTenantModel(tenantId); - if (m != null) { - ret.putAll(m); + if (includeTenant) { + String tenantId = getTenantId(); + if (!StringHelper.isEmpty(tenantId) && ResourceTenantManager.instance().getTenantModuleDiscovery() != null) { + Map m = ResourceTenantManager.instance().getTenantModuleDiscovery().getEnabledTenantModules(); + if (m != null) { + ret.putAll(m); + } } } return ret; } - public Set getEnabledModuleNames() { - return getAllModules().keySet(); + public Collection getEnabledModules(boolean includeTenant) { + return getEnabledModuleMap(includeTenant).values(); + + } + + public Set getEnabledModuleNames(boolean includeTenant) { + return getEnabledModuleMap(includeTenant).keySet(); + } + + public List getAllModuleResources(boolean includeTenant, String filePathInModule) { + return getAllModuleResourcesInModules(getEnabledModules(includeTenant), filePathInModule); + } + + public List findModuleResources(boolean includeTenant, String filePathInModule, String suffix) { + return findModuleResourcesInModules(getEnabledModules(includeTenant), filePathInModule, suffix); } - public Collection getEnabledModules() { - return getAllModules().values(); + public IResource getModuleResource(boolean includeTenant, String filePathInModule) { + return getModuleResourceInModules(getEnabledModules(includeTenant), filePathInModule); } - public List getAllModuleResources(String filePathInModule) { - Collection modules = getEnabledModules(); + public List getAllModuleResourcesInModules(Collection modules, String filePathInModule) { List ret = new ArrayList<>(modules.size()); if (filePathInModule.startsWith("/")) filePathInModule = filePathInModule.substring(1); @@ -191,20 +170,30 @@ public List getAllModuleResources(String filePathInModule) { return ret; } - public IResource getModuleResource(String filePathInModule) { - Collection modules = getEnabledModules(); + public IResource getModuleResourceInModules(Collection 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 findModuleResources(String filePathInModule, String suffix) { - Collection modules = getEnabledModules(); + public List findModuleResourcesInModules(Collection modules, String filePathInModule, String suffix) { List ret = new ArrayList<>(); for (ModuleModel module : modules) { String path = StringHelper.appendPath('/' + module.getModuleId(), filePathInModule); diff --git a/nop-core/src/main/java/io/nop/core/resource/component/version/ResourceVersionedModelStore.java b/nop-core/src/main/java/io/nop/core/resource/component/version/ResourceVersionedModelStore.java index 85e4c8ecd..7c57ee44d 100644 --- a/nop-core/src/main/java/io/nop/core/resource/component/version/ResourceVersionedModelStore.java +++ b/nop-core/src/main/java/io/nop/core/resource/component/version/ResourceVersionedModelStore.java @@ -34,7 +34,7 @@ public void setFileType(String fileType) { @Override public Long getLatestVersion(String modelName) { - List resources = ModuleManager.instance().findModuleResources(basePath + modelName, "." + fileType); + List resources = ModuleManager.instance().findModuleResources(true, basePath + modelName, "." + fileType); Collections.sort(resources, Comparator.comparing(IResource::getName)); if (resources.isEmpty()) return null; @@ -44,7 +44,7 @@ public Long getLatestVersion(String modelName) { @Override public List getAllVersions(String modelName) { - List resources = ModuleManager.instance().findModuleResources(basePath + modelName, "." + fileType); + List resources = ModuleManager.instance().findModuleResources(true, basePath + modelName, "." + fileType); Collections.sort(resources, Comparator.comparing(IResource::getName)); if (resources.isEmpty()) return Collections.emptyList(); diff --git a/nop-core/src/main/java/io/nop/core/resource/store/ModuleNamespaceHandler.java b/nop-core/src/main/java/io/nop/core/resource/store/ModuleNamespaceHandler.java index 7259a8199..4f80ba6fa 100644 --- a/nop-core/src/main/java/io/nop/core/resource/store/ModuleNamespaceHandler.java +++ b/nop-core/src/main/java/io/nop/core/resource/store/ModuleNamespaceHandler.java @@ -7,21 +7,12 @@ */ package io.nop.core.resource.store; -import io.nop.api.core.config.AppConfig; -import io.nop.api.core.exceptions.NopException; import io.nop.core.module.ModuleManager; -import io.nop.core.module.ModuleModel; import io.nop.core.resource.IResource; import io.nop.core.resource.IResourceNamespaceHandler; import io.nop.core.resource.IResourceStore; import io.nop.core.resource.ResourceConstants; import io.nop.core.resource.ResourceHelper; -import io.nop.core.resource.impl.UnknownResource; - -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; public class ModuleNamespaceHandler implements IResourceNamespaceHandler { public static final ModuleNamespaceHandler INSTANCE = new ModuleNamespaceHandler(); @@ -36,26 +27,6 @@ public IResource getResource(String vPath, IResourceStore locator) { String path = ResourceHelper.removeNamespace(vPath, getNamespace()); ResourceHelper.checkNormalVirtualPath(path); - IResource resource = null; - for (ModuleModel module : ModuleManager.instance().getEnabledModules()) { - String fullPath = "/" + module.getModuleId() + path; - IResource moduleResource = locator.getResource(fullPath, 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, vPath).param(ARG_PATH, resource.getPath()) - .param(ARG_OTHER_PATH, resource.getPath()); - resource = moduleResource; - continue; - } - return moduleResource; - } - } - - if (resource != null) - return resource; - - return new UnknownResource(vPath); + return ModuleManager.instance().getModuleResource(true, path); } } \ No newline at end of file diff --git a/nop-core/src/main/java/io/nop/core/resource/tenant/ITenantModuleDiscovery.java b/nop-core/src/main/java/io/nop/core/resource/tenant/ITenantModuleDiscovery.java index cb122c2aa..d4f06a95c 100644 --- a/nop-core/src/main/java/io/nop/core/resource/tenant/ITenantModuleDiscovery.java +++ b/nop-core/src/main/java/io/nop/core/resource/tenant/ITenantModuleDiscovery.java @@ -12,8 +12,4 @@ default boolean isEnabledTenantModule(String moduleName) { return getEnabledTenantModules().containsKey(moduleName); } - /** - * 检查在租户模块中是否定义了业务对象。如果业务对象存在,则生成相关资源文件 - */ - boolean checkBizObjExists(String bizObjName); } diff --git a/nop-core/src/main/java/io/nop/core/resource/tenant/ResourceTenantManager.java b/nop-core/src/main/java/io/nop/core/resource/tenant/ResourceTenantManager.java index 151b644d3..7c0822ca0 100644 --- a/nop-core/src/main/java/io/nop/core/resource/tenant/ResourceTenantManager.java +++ b/nop-core/src/main/java/io/nop/core/resource/tenant/ResourceTenantManager.java @@ -3,6 +3,7 @@ import io.nop.api.core.annotations.core.GlobalInstance; import io.nop.api.core.config.IConfigReference; import io.nop.api.core.exceptions.NopException; +import io.nop.api.core.util.FutureHelper; import io.nop.api.core.util.Guard; import io.nop.api.core.util.ICancellable; import io.nop.commons.cache.GlobalCacheRegistry; @@ -26,6 +27,7 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; @@ -64,7 +66,7 @@ public static boolean supportTenant(String path) { return instance().isSupportTenant(path); } - private final Map tenantCleanups = new ConcurrentHashMap<>(); + private final Map tenantInitializations = new ConcurrentHashMap<>(); private final List tenantInitializers = new CopyOnWriteArrayList<>(); @@ -72,13 +74,14 @@ public static boolean supportTenant(String path) { private ITenantModuleDiscovery tenantModuleDiscovery; - class TenantCleanup { + class TenantInitialization { private final String tenantId; ICancellable cleanup; - volatile AtomicInteger state = new AtomicInteger(); + final AtomicInteger state = new AtomicInteger(); + final CompletableFuture ready = new CompletableFuture<>(); - public TenantCleanup(String tenantId) { + public TenantInitialization(String tenantId) { this.tenantId = tenantId; } @@ -95,12 +98,19 @@ public void init() { cancellable.appendOnCancelTask(initializer.initializeTenant(tenantId)); } this.cleanup = cancellable; + ready.complete(null); } catch (Exception e) { + ready.completeExceptionally(e); cancellable.cancel(); throw NopException.adapt(e); } } } + + public void awaitReady() { + init(); + FutureHelper.syncGet(ready); + } } public void addTenantInitializer(IResourceTenantInitializer tenantInitializer) { @@ -145,28 +155,27 @@ public boolean isSupportTenant(String resourcePath) { String moduleName = ResourceHelper.getModuleName(resourcePath); if (StringHelper.isEmpty(moduleName)) return false; - if (tenantModuleDiscovery != null) - return tenantModuleDiscovery.isEnabledTenantModule(moduleName); - return false; + // nop资源不支持租户缓存 + return moduleName.startsWith("nop-"); } public void reset() { - tenantCleanups.clear(); + tenantInitializations.clear(); } public boolean isTenantUsed(String tenantId) { - return tenantCleanups.containsKey(tenantId); + return tenantInitializations.containsKey(tenantId); } public Set getUsedTenants() { - return new TreeSet<>(tenantCleanups.keySet()); + return new TreeSet<>(tenantInitializations.keySet()); } /** * 销毁租户所占用的资源 */ public void clearForTenant(String tenantId) { - TenantCleanup cleanup = tenantCleanups.remove(tenantId); + TenantInitialization cleanup = tenantInitializations.remove(tenantId); if (cleanup != null) cleanup.cancel(); GlobalCacheRegistry.instance().clearForTenant(tenantId); @@ -182,8 +191,13 @@ public void clearAllTenants() { * 第一次执行时会自动触发租户的初始化函数 */ public void useTenant(String tenantId) { - TenantCleanup cleanup = tenantCleanups.computeIfAbsent(tenantId, TenantCleanup::new); - cleanup.init(); + TenantInitialization tenant = tenantInitializations.computeIfAbsent(tenantId, TenantInitialization::new); + tenant.init(); + } + + public void awaitTenantReady(String tenantId) { + TenantInitialization tenant = tenantInitializations.computeIfAbsent(tenantId, TenantInitialization::new); + tenant.awaitReady(); } public IResourceLoadingCache makeLoadingCache(String name, diff --git a/nop-dyn/model/nop-dyn.orm.xlsx b/nop-dyn/model/nop-dyn.orm.xlsx index 3e9d0a4736f9ec361e44d56aa5eb2ace1500f266..39e2c1e5d08f59d546a3955e564012f3b360b4e7 100644 GIT binary patch delta 64022 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 delta 56832 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 diff --git a/nop-dyn/nop-dyn-dao/src/main/java/io/nop/dyn/dao/model/DynEntityMetaToOrmModel.java b/nop-dyn/nop-dyn-dao/src/main/java/io/nop/dyn/dao/model/DynEntityMetaToOrmModel.java index 70fcd7279..043c1cca7 100644 --- a/nop-dyn/nop-dyn-dao/src/main/java/io/nop/dyn/dao/model/DynEntityMetaToOrmModel.java +++ b/nop-dyn/nop-dyn-dao/src/main/java/io/nop/dyn/dao/model/DynEntityMetaToOrmModel.java @@ -311,7 +311,7 @@ protected void buildVirtualEntityModel(OrmEntityModel entityModel, NopDynEntityM objTypeCol.setTagSet(TagsHelper.add(objTypeCol.getTagSet(), OrmModelConstants.TAG_NOT_PUB)); entityMeta.getPropMetas().forEach(propMeta -> { - if (propMeta.getDynPropMapping() != null && !propMeta.getPropName().equals(propMeta.getDynPropMapping())) { + if (propMeta.getDynPropMapping() != null) { IColumnModel col = dynEntityModel.getColumn(propMeta.getDynPropMapping(), true); if (col == null) throw new NopException(ERR_DYN_VIRTUAL_ENTITY_PROP_MAPPING_NOT_VALID) @@ -320,13 +320,17 @@ protected void buildVirtualEntityModel(OrmEntityModel entityModel, NopDynEntityM .param(ARG_PROP_MAPPING, propMeta.getDynPropMapping()); OrmColumnModel baseCol = ((OrmColumnModel) col).cloneInstance(); entityModel.addColumn(baseCol); - entityModel.addAlias(toAliasModel(propMeta)); + if(!propMeta.getPropName().equals(propMeta.getDynPropMapping())) { + // 列本身声明为内部字段,对外暴露的只有alias + baseCol.setTagSet(TagsHelper.add(baseCol.getTagSet(), OrmModelConstants.TAG_SYS)); + entityModel.addAlias(toAliasModel(propMeta)); + } } else { if (STD_PROPS.contains(propMeta.getPropName())) return; OrmAliasModel propModel = toAliasModel(propMeta); - propModel.setTagSet(TagsHelper.add(propModel.getTagSet(), OrmModelConstants.TAG_EAGER)); + propModel.setTagSet(TagsHelper.merge(propModel.getTagSet(), Arrays.asList(OrmModelConstants.TAG_EDIT, OrmModelConstants.TAG_EAGER))); propModel.setPropPath(buildVirtualPropPath(propModel)); entityModel.addAlias(propModel); // if (propMeta.getRefEntityName() != null) { diff --git a/nop-dyn/nop-dyn-service/src/main/java/io/nop/dyn/service/codegen/DynCodeGen.java b/nop-dyn/nop-dyn-service/src/main/java/io/nop/dyn/service/codegen/DynCodeGen.java index c5c7d70cf..0cb9de121 100644 --- a/nop-dyn/nop-dyn-service/src/main/java/io/nop/dyn/service/codegen/DynCodeGen.java +++ b/nop-dyn/nop-dyn-service/src/main/java/io/nop/dyn/service/codegen/DynCodeGen.java @@ -11,10 +11,13 @@ import io.nop.api.core.annotations.orm.SingleSession; import io.nop.api.core.context.ContextProvider; import io.nop.biz.api.IBizObjectManager; +import io.nop.biz.impl.IDynamicBizModelProvider; import io.nop.commons.cache.ICache; import io.nop.commons.cache.LocalCache; import io.nop.commons.util.StringHelper; +import io.nop.core.module.ModuleModel; import io.nop.core.resource.tenant.IResourceTenantInitializer; +import io.nop.core.resource.tenant.ITenantModuleDiscovery; import io.nop.core.resource.tenant.ResourceTenantManager; import io.nop.dao.api.IDaoProvider; import io.nop.dao.api.IEntityDao; @@ -23,7 +26,9 @@ import io.nop.dyn.dao.entity.NopDynAppModule; import io.nop.dyn.dao.entity.NopDynEntityMeta; import io.nop.dyn.dao.entity.NopDynModule; +import io.nop.graphql.core.reflection.GraphQLBizModel; import io.nop.orm.IOrmSessionFactory; +import io.nop.orm.IOrmTemplate; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import jakarta.inject.Inject; @@ -32,12 +37,13 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; import static io.nop.commons.cache.CacheConfig.newConfig; import static io.nop.core.CoreConfigs.CFG_COMPONENT_RESOURCE_CACHE_TENANT_CACHE_CONTAINER_SIZE; import static io.nop.dyn.service.NopDynConfigs.CFG_DYN_GEN_CODE_WHEN_INIT; -public class DynCodeGen implements IResourceTenantInitializer { +public class DynCodeGen implements IResourceTenantInitializer, IDynamicBizModelProvider, ITenantModuleDiscovery { @Inject IDaoProvider daoProvider; @@ -47,12 +53,17 @@ public class DynCodeGen implements IResourceTenantInitializer { @Inject IBizObjectManager bizObjectManager; + @Inject + IOrmTemplate ormTemplate; + @InjectValue("@cfg:nop.dyn.gen-web-files|true") boolean genWebFiles; @InjectValue("@cfg:nop.dyn.format-gen-code|true") boolean formatGenCode; + private boolean registerTenant; + private final InMemoryCodeCache codeCache = new InMemoryCodeCache(); private final ICache tenantCache = LocalCache.newCache("gen-code-cache", newConfig(CFG_COMPONENT_RESOURCE_CACHE_TENANT_CACHE_CONTAINER_SIZE.get()), this::initTenantCache); @@ -65,13 +76,18 @@ public void init() { generateForAllModules(); if (useTenant) { + registerTenant = true; ResourceTenantManager.instance().addTenantInitializer(this); + ResourceTenantManager.instance().setTenantModuleDiscovery(this); } } @PreDestroy public void destroy() { - ResourceTenantManager.instance().removeTenantInitializer(this); + if (registerTenant) { + ResourceTenantManager.instance().removeTenantInitializer(this); + ResourceTenantManager.instance().setTenantModuleDiscovery(null); + } codeCache.clear(); tenantCache.clear(); } @@ -83,6 +99,7 @@ public boolean isUseTenant() { private InMemoryCodeCache initTenantCache(String tenantId) { InMemoryCodeCache cache = new InMemoryCodeCache(tenantId); generateForAllModules(cache); + cache.reloadModel(ormSessionFactory,bizObjectManager); return cache; } @@ -93,6 +110,11 @@ public InMemoryCodeCache getCodeCache() { return tenantCache.get(tenantId); } + @Override + public Map getEnabledTenantModules() { + return getCodeCache().getEnabledModules(); + } + @Override public Runnable initializeTenant(String tenantId) { InMemoryCodeCache cache = getCodeCache(); @@ -135,17 +157,19 @@ public synchronized void generateBizModel(NopDynEntityMeta module) { } protected void generateForAllModules(InMemoryCodeCache cache) { - IEntityDao dao = daoProvider.daoFor(NopDynModule.class); - NopDynModule example = new NopDynModule(); - example.setStatus(NopDynDaoConstants.MODULE_STATUS_PUBLISHED); - List list = dao.findAllByExample(example); + ormTemplate.runInSession(() -> { + IEntityDao dao = daoProvider.daoFor(NopDynModule.class); + NopDynModule example = new NopDynModule(); + example.setStatus(NopDynDaoConstants.MODULE_STATUS_PUBLISHED); + List list = dao.findAllByExample(example); - dao.batchLoadProps(list, - Arrays.asList("entityMetas.propMetas.domain", "entityMetas.functionMetas", "entityMetas.relationMetasForEntity1")); + dao.batchLoadProps(list, + Arrays.asList("entityMetas.propMetas.domain", "entityMetas.functionMetas", "entityMetas.relationMetasForEntity1")); - for (NopDynModule module : list) { - cache.generateForModule(genWebFiles, formatGenCode, module); - } + for (NopDynModule module : list) { + cache.generateForModule(genWebFiles, formatGenCode, module); + } + }); } protected void batchLoadModule(NopDynModule module) { @@ -165,10 +189,20 @@ protected void batchLoadApp(NopDynApp app) { } public synchronized void removeDynModule(NopDynModule module) { - getCodeCache().removeDynModule(module); + getCodeCache().removeModule(module.getModuleName()); } public synchronized void reloadModel() { getCodeCache().reloadModel(ormSessionFactory, bizObjectManager); } + + @Override + public GraphQLBizModel getBizModel(String bizObjName) { + return getCodeCache().getBizModel(bizObjName); + } + + @Override + public Runnable addOnChangeListener(ChangeListener listener) { + return getCodeCache().addOnChangeListener(listener); + } } \ No newline at end of file diff --git a/nop-dyn/nop-dyn-service/src/main/java/io/nop/dyn/service/codegen/InMemoryCodeCache.java b/nop-dyn/nop-dyn-service/src/main/java/io/nop/dyn/service/codegen/InMemoryCodeCache.java index e73391754..0d2f98a6a 100644 --- a/nop-dyn/nop-dyn-service/src/main/java/io/nop/dyn/service/codegen/InMemoryCodeCache.java +++ b/nop-dyn/nop-dyn-service/src/main/java/io/nop/dyn/service/codegen/InMemoryCodeCache.java @@ -2,16 +2,17 @@ import io.nop.api.core.config.AppConfig; import io.nop.biz.api.IBizObjectManager; +import io.nop.biz.impl.IDynamicBizModelProvider; import io.nop.codegen.XCodeGenerator; import io.nop.commons.util.StringHelper; import io.nop.core.lang.eval.IEvalScope; -import io.nop.core.module.ModuleManager; import io.nop.core.module.ModuleModel; import io.nop.core.resource.IResource; import io.nop.core.resource.ResourceHelper; import io.nop.core.resource.VirtualFileSystem; import io.nop.core.resource.store.InMemoryResourceStore; import io.nop.core.resource.store.ResourceStoreHelper; +import io.nop.core.resource.tenant.ResourceTenantManager; import io.nop.dyn.dao.entity.NopDynEntityMeta; import io.nop.dyn.dao.entity.NopDynModule; import io.nop.dyn.dao.model.DynEntityMetaToOrmModel; @@ -23,17 +24,29 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +/** + * 最终发布到虚拟文件系统中的是一个合并后的只读副本,避免出现并发更新的情况。作为编辑使用的CodeCache,每次编辑时都使用同步机制避免并发错误。 + *

+ * 处于部署状态时,总是会根据当前数据库定义生成一个对应的模块CodeCache,然后每次编辑一个实体对象,可以只更新针对单个实体的数据文件。 + */ public class InMemoryCodeCache { private final String tenantId; private final Map moduleCoreStores = new ConcurrentHashMap<>(); private final Map moduleWebStores = new ConcurrentHashMap<>(); + private final Map enabledModules = new ConcurrentHashMap<>(); + // moduleName => bizObjName => BizModel - private final Map> moduleDynBizModels = new ConcurrentHashMap<>(); + private final Map dynBizModels = new ConcurrentHashMap<>(); + + private final List changeListeners = new CopyOnWriteArrayList<>(); public Map getModuleCoreStores() { return moduleCoreStores; @@ -43,10 +56,6 @@ public Map getModuleWebStores() { return moduleWebStores; } - public Map> getModuleDynBizModels() { - return moduleDynBizModels; - } - public InMemoryCodeCache(String tenantId) { this.tenantId = tenantId; } @@ -58,7 +67,7 @@ public InMemoryCodeCache() { public void clear() { moduleCoreStores.clear(); moduleWebStores.clear(); - moduleDynBizModels.clear(); + dynBizModels.clear(); } public synchronized void generateForModule(boolean genWebFiles, boolean formatCode, @@ -69,13 +78,39 @@ public synchronized void generateForModule(boolean genWebFiles, boolean formatCo } } + public Map getEnabledModules() { + return enabledModules; + } + + public Map getDynBizModels() { + return dynBizModels; + } + + public GraphQLBizModel getBizModel(String bizObjName) { + return dynBizModels.get(bizObjName); + } + + public Runnable addOnChangeListener(IDynamicBizModelProvider.ChangeListener changeListener) { + this.changeListeners.add(changeListener); + return () -> changeListeners.remove(changeListener); + } + public void removeModule(String moduleName) { - moduleCoreStores.remove(moduleName); + this.enabledModules.remove(moduleName); + removeModuleCoreResources(moduleName); moduleWebStores.remove(moduleName); - moduleDynBizModels.remove(moduleName); + } + + private void removeModuleCoreResources(String moduleName) { + moduleCoreStores.remove(moduleName); + dynBizModels.values().removeIf(model -> { + return moduleName.equals(model.getModuleName()); + }); } protected InMemoryResourceStore genModuleCoreFiles(boolean formatGenCode, NopDynModule module) { + removeModuleCoreResources(module.getModuleName()); + DynEntityMetaToOrmModel trans = new DynEntityMetaToOrmModel(false); OrmModel ormModel = trans.transformModule(module); @@ -87,16 +122,22 @@ protected InMemoryResourceStore genModuleCoreFiles(boolean formatGenCode, NopDyn genModuleBizModels(module); + ModuleModel moduleModel = new ModuleModel(); + moduleModel.setModuleId(module.getModuleId()); + + enabledModules.put(moduleModel.getModuleName(), moduleModel); return store; } protected void genModuleBizModels(NopDynModule module) { - Map bizModels = new HashMap<>(); + Set oldNames = new HashSet<>(dynBizModels.keySet()); + String moduleName = module.getModuleName(); String modulePath = module.getModuleName().replace('-', '/'); for (NopDynEntityMeta entityMeta : module.getEntityMetas()) { String bizObjName = entityMeta.getBizObjName(); GraphQLBizModel bizModel = new GraphQLBizModel(bizObjName); + bizModel.setModuleName(moduleName); String bizPath = "/" + modulePath + "/model/" + bizObjName + "/" + bizObjName + ".xbiz"; bizModel.setBizPath(bizPath); @@ -105,9 +146,13 @@ protected void genModuleBizModels(NopDynModule module) { bizModel.setMetaPath(metaPath); } - bizModels.put(bizObjName, bizModel); + dynBizModels.put(bizObjName, bizModel); + } + + for (String oldName : oldNames) { + if (!dynBizModels.containsKey(oldName)) + this.changeListeners.forEach(listener -> listener.onBizObjRemoved(oldName)); } - moduleDynBizModels.put(module.getModuleName(), bizModels); } protected InMemoryResourceStore genModuleCoreFiles(boolean formatGenCode, NopDynModule dynModule, OrmModel ormModel) { @@ -148,6 +193,8 @@ protected String getTargetPath(String path) { } protected void genModuleWebFiles(boolean formatGenCode, NopDynModule module, InMemoryResourceStore coreStore) { + moduleWebStores.remove(module.getModuleName()); + XCodeGenerator gen = new XCodeGenerator("/nop/templates/dyn-web", getTargetDir()); gen.autoFormat(formatGenCode).forceOverride(true); InMemoryResourceStore store = new InMemoryResourceStore(); @@ -172,13 +219,6 @@ protected void genModuleWebFiles(boolean formatGenCode, NopDynModule module, InM moduleWebStores.put(module.getModuleName(), store); } - - public synchronized void removeDynModule(NopDynModule module) { - this.moduleCoreStores.remove(module.getModuleName()); - this.moduleWebStores.remove(module.getModuleName()); - this.moduleDynBizModels.remove(module.getModuleName()); - } - public synchronized void generateBizModel(NopDynEntityMeta entityMeta) { String moduleName = entityMeta.getModule().getModuleName(); String moduleId = ResourceHelper.getModuleIdFromModuleName(moduleName); @@ -214,23 +254,18 @@ public synchronized void reloadModel(IOrmSessionFactory ormSessionFactory, IBizO this.moduleCoreStores.values().forEach(merged::merge); this.moduleWebStores.values().forEach(merged::merge); - Map bizModels = new HashMap<>(); - this.moduleDynBizModels.values().forEach(bizModels::putAll); - Map dynModules = new HashMap<>(); moduleCoreStores.keySet().forEach(moduleName -> { dynModules.put(moduleName, ModuleModel.forModuleName(moduleName)); }); - moduleDynBizModels.keySet().forEach(moduleName -> { - dynModules.computeIfAbsent(moduleName, ModuleModel::forModuleName); - }); - - VirtualFileSystem.instance().updateInMemoryLayer(merged); - ModuleManager.instance().updateDynModules(dynModules); + if (tenantId != null) { + ResourceTenantManager.instance().updateTenantResourceStore(tenantId, merged); + } else { + VirtualFileSystem.instance().updateInMemoryLayer(merged); + } ormSessionFactory.reloadModel(); - bizObjectManager.updateDynBizModels(bizModels); if (AppConfig.isDebugMode()) { ResourceStoreHelper.dumpStore(merged, "/"); diff --git a/nop-dyn/nop-dyn-service/src/main/resources/_vfs/nop/dyn/model/NopDynModule/NopDynModule.xbiz b/nop-dyn/nop-dyn-service/src/main/resources/_vfs/nop/dyn/model/NopDynModule/NopDynModule.xbiz index b8884935c..d1788d154 100644 --- a/nop-dyn/nop-dyn-service/src/main/resources/_vfs/nop/dyn/model/NopDynModule/NopDynModule.xbiz +++ b/nop-dyn/nop-dyn-service/src/main/resources/_vfs/nop/dyn/model/NopDynModule/NopDynModule.xbiz @@ -28,9 +28,9 @@ entityList.forEach(entity=>{ codeGen.generateForModule(entity); - codeGen.reloadModel(); entity.status = NopDynDaoConstants.APP_STATUS_PUBLISHED; - }) + }); + codeGen.reloadModel(); diff --git a/nop-graphql/nop-graphql-core/src/main/java/io/nop/graphql/core/reflection/GraphQLBizModel.java b/nop-graphql/nop-graphql-core/src/main/java/io/nop/graphql/core/reflection/GraphQLBizModel.java index ff0a21534..d01d45c00 100644 --- a/nop-graphql/nop-graphql-core/src/main/java/io/nop/graphql/core/reflection/GraphQLBizModel.java +++ b/nop-graphql/nop-graphql-core/src/main/java/io/nop/graphql/core/reflection/GraphQLBizModel.java @@ -37,6 +37,7 @@ @DataBean public class GraphQLBizModel { + private String moduleName; private final String bizObjName; private final Map queryActions = new HashMap<>(); private final Map mutationActions = new HashMap<>(); @@ -61,9 +62,18 @@ public GraphQLBizModel cloneInstance() { ret.bizActions.putAll(bizActions); ret.metaPath = metaPath; ret.bizPath = bizPath; + ret.moduleName = moduleName; return ret; } + public String getModuleName() { + return moduleName; + } + + public void setModuleName(String moduleName) { + this.moduleName = moduleName; + } + public String getMetaPath() { return metaPath; } diff --git a/nop-graphql/nop-graphql-core/src/main/java/io/nop/graphql/core/reflection/GraphQLBizModels.java b/nop-graphql/nop-graphql-core/src/main/java/io/nop/graphql/core/reflection/GraphQLBizModels.java index f4622897d..6357a6dd8 100644 --- a/nop-graphql/nop-graphql-core/src/main/java/io/nop/graphql/core/reflection/GraphQLBizModels.java +++ b/nop-graphql/nop-graphql-core/src/main/java/io/nop/graphql/core/reflection/GraphQLBizModels.java @@ -52,7 +52,7 @@ public void build(TypeRegistry typeRegistry, Collection beans) { } private void findBizResources() { - for (IResource resource : ModuleManager.instance().findModuleResources("model", ".xbiz")) { + for (IResource resource : ModuleManager.instance().findModuleResources(false, "model", ".xbiz")) { String fileName = resource.getName(); if (fileName.startsWith("_")) continue; @@ -66,7 +66,7 @@ private void findBizResources() { makeBizModel(bizObjName).setBizPath(resource.getStdPath()); } - for (IResource resource : ModuleManager.instance().findModuleResources("model", ".xmeta")) { + for (IResource resource : ModuleManager.instance().findModuleResources(false,"model", ".xmeta")) { String fileName = resource.getName(); if (fileName.startsWith("_")) continue; diff --git a/nop-ioc/src/main/java/io/nop/ioc/loader/AppBeanContainerLoader.java b/nop-ioc/src/main/java/io/nop/ioc/loader/AppBeanContainerLoader.java index f583ccd14..0a1b80f14 100644 --- a/nop-ioc/src/main/java/io/nop/ioc/loader/AppBeanContainerLoader.java +++ b/nop-ioc/src/main/java/io/nop/ioc/loader/AppBeanContainerLoader.java @@ -124,7 +124,7 @@ void loadBeansFile(IBeanContainerBuilder builder) { } if (CFG_IOC_APP_BEANS_FILE_ENABLED.get()) { - ModuleManager.instance().getEnabledModules().forEach(module -> { + ModuleManager.instance().getEnabledModules(false).forEach(module -> { List resources = getModuleAppResources(module.getModuleId()); for (IResource resource : resources) { builder.addResource(resource); diff --git a/nop-orm-model/src/main/java/io/nop/orm/model/OrmModelConstants.java b/nop-orm-model/src/main/java/io/nop/orm/model/OrmModelConstants.java index 3a38de880..c96008d97 100644 --- a/nop-orm-model/src/main/java/io/nop/orm/model/OrmModelConstants.java +++ b/nop-orm-model/src/main/java/io/nop/orm/model/OrmModelConstants.java @@ -48,6 +48,10 @@ public interface OrmModelConstants { String TAG_EAGER = "eager"; + String TAG_EDIT = "edit"; + + String TAG_SYS = "sys"; + String TAG_CASCADE_DELETE = "cascade-delete"; /** diff --git a/nop-orm-model/src/main/java/io/nop/orm/model/loader/OrmModelLoader.java b/nop-orm-model/src/main/java/io/nop/orm/model/loader/OrmModelLoader.java index 8c1fd6c6e..4e85c2eef 100644 --- a/nop-orm-model/src/main/java/io/nop/orm/model/loader/OrmModelLoader.java +++ b/nop-orm-model/src/main/java/io/nop/orm/model/loader/OrmModelLoader.java @@ -12,6 +12,7 @@ import io.nop.api.core.exceptions.NopException; import io.nop.api.core.util.SourceLocation; import io.nop.core.module.ModuleManager; +import io.nop.core.module.ModuleModel; import io.nop.core.resource.IResource; import io.nop.core.resource.ResourceHelper; import io.nop.core.resource.VirtualFileSystem; @@ -23,6 +24,8 @@ import io.nop.xlang.xdsl.DslModelHelper; import io.nop.xlang.xdsl.DslModelParser; +import java.util.Collection; + public class OrmModelLoader { static final SourceLocation merged_loc = SourceLocation.fromPath("/nop/main/orm/merged-app.orm.xml"); @@ -33,12 +36,16 @@ private OrmModel loadFromResource(IResource resource, boolean ignoreUnknown) { } public OrmModel loadOrmModel() { + return loadOrmModel(ModuleManager.instance().getEnabledModules(false)); + } + + public OrmModel loadOrmModel(Collection modules) { OrmModel model = new OrmModel(); model.setLocation(merged_loc); model.setMerged(true); - ModuleManager.instance().getAllModuleResources("orm/app.orm.xml").forEach(resource -> { - OrmModel moduleModel = loadFromResource(resource,true); + ModuleManager.instance().getAllModuleResourcesInModules(modules, "orm/app.orm.xml").forEach(resource -> { + OrmModel moduleModel = loadFromResource(resource, true); if (moduleModel != null) { merge(model, moduleModel, false); } diff --git a/nop-orm/src/main/java/io/nop/orm/factory/XplOrmInterceptorFactoryBean.java b/nop-orm/src/main/java/io/nop/orm/factory/XplOrmInterceptorFactoryBean.java index 5ebb8afcd..693f3752c 100644 --- a/nop-orm/src/main/java/io/nop/orm/factory/XplOrmInterceptorFactoryBean.java +++ b/nop-orm/src/main/java/io/nop/orm/factory/XplOrmInterceptorFactoryBean.java @@ -37,7 +37,7 @@ private XplOrmInterceptor loadInterceptor(String cacheName) { // event -> entityName -> actions Map>> allActions = new HashMap<>(); - ModuleManager.instance().getEnabledModules().forEach(module -> { + ModuleManager.instance().getEnabledModules(false).forEach(module -> { String path = "/" + module.getModuleId() + "/orm/app.orm-interceptor.xml"; IResource resource = VirtualFileSystem.instance().getResource(path); diff --git a/nop-orm/src/main/java/io/nop/orm/sql_lib/SqlLibManager.java b/nop-orm/src/main/java/io/nop/orm/sql_lib/SqlLibManager.java index 692fcb645..7d37818e6 100644 --- a/nop-orm/src/main/java/io/nop/orm/sql_lib/SqlLibManager.java +++ b/nop-orm/src/main/java/io/nop/orm/sql_lib/SqlLibManager.java @@ -106,7 +106,7 @@ public void delayInit() { } public void checkAllLibValid() { - List resources = ModuleManager.instance().findModuleResources("/sql", OrmConstants.FILE_TYPE_SQL_LIB); + List resources = ModuleManager.instance().findModuleResources(false, "/sql", OrmConstants.FILE_TYPE_SQL_LIB); resources.forEach(resource -> checkLibValid(resource.getStdPath())); } diff --git a/nop-web/src/main/java/io/nop/web/page/DynamicWebFileProvider.java b/nop-web/src/main/java/io/nop/web/page/DynamicWebFileProvider.java index 32b724bd0..81621d5c0 100644 --- a/nop-web/src/main/java/io/nop/web/page/DynamicWebFileProvider.java +++ b/nop-web/src/main/java/io/nop/web/page/DynamicWebFileProvider.java @@ -39,18 +39,18 @@ public void init() { } public void loadAllXjs() { - List resources = ModuleManager.instance().findModuleResources("/pages", WebConstants.FILE_EXT_XJS); + List resources = ModuleManager.instance().findModuleResources(false, "/pages", WebConstants.FILE_EXT_XJS); resources.forEach(resource -> jsLoader.loadText(StringHelper.replaceFileExt(resource.getStdPath(), WebConstants.FILE_EXT_JS))); - resources = ModuleManager.instance().findModuleResources("/js", WebConstants.FILE_EXT_XJS); + resources = ModuleManager.instance().findModuleResources(false, "/js", WebConstants.FILE_EXT_XJS); resources.forEach(resource -> jsLoader.loadText(StringHelper.replaceFileExt(resource.getStdPath(), WebConstants.FILE_EXT_JS))); } public void loadAllXcss() { - List resources = ModuleManager.instance().findModuleResources("/pages", WebConstants.FILE_EXT_XCSS); + List resources = ModuleManager.instance().findModuleResources(false, "/pages", WebConstants.FILE_EXT_XCSS); resources.forEach(resource -> cssLoader.loadText(StringHelper.replaceFileExt(resource.getStdPath(), WebConstants.FILE_EXT_CSS))); - resources = ModuleManager.instance().findModuleResources("/css", WebConstants.FILE_EXT_XCSS); + resources = ModuleManager.instance().findModuleResources(false, "/css", WebConstants.FILE_EXT_XCSS); resources.forEach(resource -> cssLoader.loadText(StringHelper.replaceFileExt(resource.getStdPath(), WebConstants.FILE_EXT_CSS))); } diff --git a/nop-web/src/main/java/io/nop/web/page/PageProvider.java b/nop-web/src/main/java/io/nop/web/page/PageProvider.java index 30f4893b4..33b331309 100644 --- a/nop-web/src/main/java/io/nop/web/page/PageProvider.java +++ b/nop-web/src/main/java/io/nop/web/page/PageProvider.java @@ -150,7 +150,7 @@ public void destroy() { } public void validateAllPages() { - ModuleManager.instance().getEnabledModules().forEach(module -> { + ModuleManager.instance().getEnabledModules(true).forEach(module -> { List pageFiles = VirtualFileSystem.instance().findAll("/" + module.getModuleId(), "pages/*/*.page.yaml"); for (IResource resource : pageFiles) { getPage(resource.getPath(), AppConfig.defaultLocale());