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 3e9d0a473..39e2c1e5d 100644 Binary files a/nop-dyn/model/nop-dyn.orm.xlsx and b/nop-dyn/model/nop-dyn.orm.xlsx differ 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());