diff --git a/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java b/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java index bc12171306..9600ac0221 100644 --- a/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java +++ b/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java @@ -164,6 +164,7 @@ public class SystemInfoController extends BaseController { } else { infoObject.put("version", "unknown"); } + infoObject.put("type", "CE"); return infoObject; } } diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java index 632ea44940..019b6eb5e8 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java @@ -94,7 +94,7 @@ import java.util.stream.Collectors; public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubscriptionService { private static final int DEFAULT_LIMIT = 100; - private final Map> subscriptionsBySessionId = new ConcurrentHashMap<>(); + private final ConcurrentMap> subscriptionsBySessionId = new ConcurrentHashMap<>(); @Autowired @Lazy @@ -495,7 +495,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc } private TbEntityDataSubCtx createSubCtx(WebSocketSessionRef sessionRef, EntityDataCmd cmd) { - Map sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>()); + Map sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new ConcurrentHashMap<>()); TbEntityDataSubCtx ctx = new TbEntityDataSubCtx(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, sessionRef, cmd.getCmdId(), maxEntitiesPerDataSubscription); if (cmd.getQuery() != null) { @@ -506,7 +506,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc } private TbEntityCountSubCtx createSubCtx(WebSocketSessionRef sessionRef, EntityCountCmd cmd) { - Map sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>()); + Map sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new ConcurrentHashMap<>()); TbEntityCountSubCtx ctx = new TbEntityCountSubCtx(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, sessionRef, cmd.getCmdId()); if (cmd.getQuery() != null) { @@ -518,7 +518,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc private TbAlarmDataSubCtx createSubCtx(WebSocketSessionRef sessionRef, AlarmDataCmd cmd) { - Map sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>()); + Map sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new ConcurrentHashMap<>()); TbAlarmDataSubCtx ctx = new TbAlarmDataSubCtx(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, alarmService, sessionRef, cmd.getCmdId(), maxEntitiesPerAlarmSubscription, maxAlarmQueriesPerRefreshInterval); @@ -528,7 +528,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc } private TbAlarmCountSubCtx createSubCtx(WebSocketSessionRef sessionRef, AlarmCountCmd cmd) { - Map sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>()); + Map sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new ConcurrentHashMap<>()); TbAlarmCountSubCtx ctx = new TbAlarmCountSubCtx(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, alarmService, sessionRef, cmd.getCmdId()); if (cmd.getQuery() != null) { diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java index bbfdd2d44f..1e701c7b5c 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java @@ -85,7 +85,7 @@ import java.util.stream.Collectors; @Service public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionService { - private final ConcurrentMap>> subscriptionsBySessionId = new ConcurrentHashMap<>(); + private final ConcurrentMap>> subscriptionsBySessionId = new ConcurrentHashMap<>(); private final ConcurrentMap subscriptionsByEntityId = new ConcurrentHashMap<>(); private final ConcurrentMap entityUpdates = new ConcurrentHashMap<>(); diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/CaffeineTbTransactionalCache.java b/common/cache/src/main/java/org/thingsboard/server/cache/CaffeineTbTransactionalCache.java index 4ce6571f1c..d2ea960e68 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/CaffeineTbTransactionalCache.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/CaffeineTbTransactionalCache.java @@ -54,11 +54,6 @@ public abstract class CaffeineTbTransactionalCache get(K key, boolean transactionMode) { - return get(key); - } - @Override public void put(K key, V value) { lock.lock(); diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/RedisTbCacheTransaction.java b/common/cache/src/main/java/org/thingsboard/server/cache/RedisTbCacheTransaction.java index 3dcb6e878f..fb852493ce 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/RedisTbCacheTransaction.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/RedisTbCacheTransaction.java @@ -31,7 +31,7 @@ public class RedisTbCacheTransaction get(K key) { - return get(key, false); - } - - @Override - public TbCacheValueWrapper get(K key, boolean transactionMode) { if (!cacheEnabled) { return null; } try (var connection = connectionFactory.getConnection()) { - byte[] rawKey = getRawKey(key); - byte[] rawValue = doGet(connection, rawKey, transactionMode); + byte[] rawValue = doGet(key, connection); if (rawValue == null || rawValue.length == 0) { return null; } else if (Arrays.equals(rawValue, BINARY_NULL_VALUE)) { @@ -114,8 +108,8 @@ public abstract class RedisTbTransactionalCache get(K key); - TbCacheValueWrapper get(K key, boolean transactionMode); - void put(K key, V value); void putIfAbsent(K key, V value); @@ -53,7 +51,7 @@ public interface TbTransactionalCache cacheValueWrapper = get(key, true); + TbCacheValueWrapper cacheValueWrapper = get(key); if (cacheValueWrapper != null) { return cacheValueWrapper.get(); } @@ -66,7 +64,7 @@ public interface TbTransactionalCache R getAndPutInTransaction(K key, Supplier dbCall, Function cacheValueToResult, Function dbValueToCacheValue, boolean cacheNullValue) { - TbCacheValueWrapper cacheValueWrapper = get(key, true); + TbCacheValueWrapper cacheValueWrapper = get(key); if (cacheValueWrapper != null) { V cacheValue = cacheValueWrapper.get(); return cacheValue != null ? cacheValueToResult.apply(cacheValue) : null; @@ -92,7 +90,7 @@ public interface TbTransactionalCache cacheValueWrapper = get(key, true); + TbCacheValueWrapper cacheValueWrapper = get(key); if (cacheValueWrapper != null) { var cacheValue = cacheValueWrapper.get(); return cacheValue == null ? null : cacheValueToResult.apply(cacheValue); diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/VersionedCacheKey.java b/common/cache/src/main/java/org/thingsboard/server/cache/VersionedCacheKey.java new file mode 100644 index 0000000000..f339d9def3 --- /dev/null +++ b/common/cache/src/main/java/org/thingsboard/server/cache/VersionedCacheKey.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.cache; + +import java.io.Serializable; + +public interface VersionedCacheKey extends Serializable { + + default boolean isVersioned() { + return false; + } + +} diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/VersionedCaffeineTbCache.java b/common/cache/src/main/java/org/thingsboard/server/cache/VersionedCaffeineTbCache.java index f9c22ecc32..535fa64cd9 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/VersionedCaffeineTbCache.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/VersionedCaffeineTbCache.java @@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.util.TbPair; import java.io.Serializable; -public abstract class VersionedCaffeineTbCache extends CaffeineTbTransactionalCache implements VersionedTbCache { +public abstract class VersionedCaffeineTbCache extends CaffeineTbTransactionalCache implements VersionedTbCache { public VersionedCaffeineTbCache(CacheManager cacheManager, String cacheName) { super(cacheManager, cacheName); diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/VersionedRedisTbCache.java b/common/cache/src/main/java/org/thingsboard/server/cache/VersionedRedisTbCache.java index 6ef3918a68..e9b64822e9 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/VersionedRedisTbCache.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/VersionedRedisTbCache.java @@ -30,7 +30,7 @@ import java.io.Serializable; import java.util.Arrays; @Slf4j -public abstract class VersionedRedisTbCache extends RedisTbTransactionalCache implements VersionedTbCache { +public abstract class VersionedRedisTbCache extends RedisTbTransactionalCache implements VersionedTbCache { private static final int VERSION_SIZE = 8; private static final int VALUE_END_OFFSET = -1; @@ -79,15 +79,20 @@ public abstract class VersionedRedisTbCache extends TbTransactionalCache { +public interface VersionedTbCache extends TbTransactionalCache { TbCacheValueWrapper get(K key); diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/device/DeviceCacheKey.java b/common/cache/src/main/java/org/thingsboard/server/cache/device/DeviceCacheKey.java index ed6258842d..2a0819851e 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/device/DeviceCacheKey.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/device/DeviceCacheKey.java @@ -19,17 +19,17 @@ import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.RequiredArgsConstructor; +import org.thingsboard.server.cache.VersionedCacheKey; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import java.io.Serial; -import java.io.Serializable; @Getter @EqualsAndHashCode @RequiredArgsConstructor @Builder -public class DeviceCacheKey implements Serializable { +public class DeviceCacheKey implements VersionedCacheKey { @Serial private static final long serialVersionUID = 6366389552842340207L; @@ -61,4 +61,9 @@ public class DeviceCacheKey implements Serializable { } } + @Override + public boolean isVersioned() { + return deviceId != null; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileCacheKey.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileCacheKey.java index 1e91d43a8f..aa3f6ce89f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileCacheKey.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileCacheKey.java @@ -16,14 +16,14 @@ package org.thingsboard.server.dao.asset; import lombok.Data; +import org.thingsboard.server.cache.VersionedCacheKey; import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.TenantId; import java.io.Serial; -import java.io.Serializable; @Data -public class AssetProfileCacheKey implements Serializable { +public class AssetProfileCacheKey implements VersionedCacheKey { @Serial private static final long serialVersionUID = 8220455917177676472L; @@ -63,4 +63,9 @@ public class AssetProfileCacheKey implements Serializable { } } + @Override + public boolean isVersioned() { + return assetProfileId != null; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeCacheKey.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeCacheKey.java index 2cbd444f80..def2c6c45c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeCacheKey.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeCacheKey.java @@ -18,16 +18,16 @@ package org.thingsboard.server.dao.attributes; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; +import org.thingsboard.server.cache.VersionedCacheKey; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.id.EntityId; import java.io.Serial; -import java.io.Serializable; @EqualsAndHashCode @Getter @AllArgsConstructor -public class AttributeCacheKey implements Serializable { +public class AttributeCacheKey implements VersionedCacheKey { @Serial private static final long serialVersionUID = 2013369077925351881L; @@ -41,4 +41,9 @@ public class AttributeCacheKey implements Serializable { return "{" + entityId + "}" + scope + "_" + key; } + @Override + public boolean isVersioned() { + return true; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileCacheKey.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileCacheKey.java index ad886b6a7c..55e8aa66eb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileCacheKey.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileCacheKey.java @@ -16,15 +16,15 @@ package org.thingsboard.server.dao.device; import lombok.Data; +import org.thingsboard.server.cache.VersionedCacheKey; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import java.io.Serial; -import java.io.Serializable; @Data -public class DeviceProfileCacheKey implements Serializable { +public class DeviceProfileCacheKey implements VersionedCacheKey { @Serial private static final long serialVersionUID = 8220455917177676472L; @@ -74,4 +74,9 @@ public class DeviceProfileCacheKey implements Serializable { return tenantId + "_" + name; } + @Override + public boolean isVersioned() { + return deviceProfileId != null; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/CachedVersionedEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/CachedVersionedEntityService.java index 356fafbbff..46045ab928 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/CachedVersionedEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/CachedVersionedEntityService.java @@ -16,12 +16,13 @@ package org.thingsboard.server.dao.entity; import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.server.cache.VersionedCacheKey; import org.thingsboard.server.cache.VersionedTbCache; import org.thingsboard.server.common.data.HasVersion; import java.io.Serializable; -public abstract class CachedVersionedEntityService extends AbstractCachedEntityService { +public abstract class CachedVersionedEntityService extends AbstractCachedEntityService { @Autowired protected VersionedTbCache cache; diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewCacheKey.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewCacheKey.java index 59df403165..deb1d85b78 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewCacheKey.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewCacheKey.java @@ -18,17 +18,17 @@ package org.thingsboard.server.dao.entityview; import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.Getter; +import org.thingsboard.server.cache.VersionedCacheKey; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; import java.io.Serial; -import java.io.Serializable; @Getter @EqualsAndHashCode @Builder -public class EntityViewCacheKey implements Serializable { +public class EntityViewCacheKey implements VersionedCacheKey { @Serial private static final long serialVersionUID = 5986277528222738163L; @@ -68,4 +68,9 @@ public class EntityViewCacheKey implements Serializable { } } + @Override + public boolean isVersioned() { + return entityViewId != null; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/TsLatestCacheKey.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/TsLatestCacheKey.java index 9880f21cc1..593bc55e1e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/TsLatestCacheKey.java +++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/TsLatestCacheKey.java @@ -18,15 +18,15 @@ package org.thingsboard.server.dao.timeseries; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; +import org.thingsboard.server.cache.VersionedCacheKey; import org.thingsboard.server.common.data.id.EntityId; import java.io.Serial; -import java.io.Serializable; @EqualsAndHashCode @Getter @AllArgsConstructor -public class TsLatestCacheKey implements Serializable { +public class TsLatestCacheKey implements VersionedCacheKey { @Serial private static final long serialVersionUID = 2024369077925351881L; @@ -39,4 +39,9 @@ public class TsLatestCacheKey implements Serializable { return "{" + entityId + "}" + key; } + @Override + public boolean isVersioned() { + return true; + } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java index f8ec81abcf..d7476bf08e 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java @@ -135,7 +135,6 @@ public class TbHttpClient { this.webClient = WebClient.builder() .clientConnector(new ReactorClientHttpConnector(httpClient)) - .defaultHeader(HttpHeaders.CONNECTION, "close") //In previous realization this header was present! (Added for hotfix "Connection reset") .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize( (config.getMaxInMemoryBufferSizeInKb() > 0 ? config.getMaxInMemoryBufferSizeInKb() : 256) * 1024)) .build(); diff --git a/ui-ngx/src/app/core/services/dynamic-component-factory.service.ts b/ui-ngx/src/app/core/services/dynamic-component-factory.service.ts index d18797a1be..1c7150bac3 100644 --- a/ui-ngx/src/app/core/services/dynamic-component-factory.service.ts +++ b/ui-ngx/src/app/core/services/dynamic-component-factory.service.ts @@ -14,49 +14,17 @@ /// limitations under the License. /// -import { - Compiler, - Component, - Injectable, - Injector, - NgModule, - NgModuleRef, - OnDestroy, - Type, ɵNG_COMP_DEF, - ɵresetCompiledComponents -} from '@angular/core'; +import { Component, Injectable, Type, ɵComponentDef, ɵNG_COMP_DEF } from '@angular/core'; import { from, Observable, of } from 'rxjs'; import { CommonModule } from '@angular/common'; import { mergeMap } from 'rxjs/operators'; -@NgModule() -export abstract class DynamicComponentModule implements OnDestroy { - - // eslint-disable-next-line @angular-eslint/contextual-lifecycle - ngOnDestroy(): void { - } - -} - -interface DynamicComponentData { - componentType: Type; - componentModuleRef: NgModuleRef; -} - -interface DynamicComponentModuleData { - moduleRef: NgModuleRef; - moduleType: Type; -} - @Injectable({ providedIn: 'root' }) export class DynamicComponentFactoryService { - private dynamicComponentModulesMap = new Map, DynamicComponentModuleData>(); - - constructor(private compiler: Compiler, - private injector: Injector) { + constructor() { } public createDynamicComponent( @@ -64,59 +32,38 @@ export class DynamicComponentFactoryService { template: string, modules?: Type[], preserveWhitespaces?: boolean, - styles?: string[]): Observable> { + styles?: string[]): Observable> { return from(import('@angular/compiler')).pipe( mergeMap(() => { - const comp = this._createDynamicComponent(componentType, template, preserveWhitespaces, styles); - let moduleImports: Type[] = [CommonModule]; + let componentImports: Type[] = [CommonModule]; if (modules) { - moduleImports = [...moduleImports, ...modules]; + componentImports = [...componentImports, ...modules]; } - // noinspection AngularInvalidImportedOrDeclaredSymbol - const dynamicComponentInstanceModule = NgModule({ - declarations: [comp], - imports: moduleImports - })(class DynamicComponentInstanceModule extends DynamicComponentModule {}); - - const module = this.compiler.compileModuleSync(dynamicComponentInstanceModule); - let moduleRef: NgModuleRef; - try { - moduleRef = module.create(this.injector); - // eslint-disable-next-line - comp[ɵNG_COMP_DEF]; - } catch (e) { - this.compiler.clearCacheFor(module.moduleType); - ɵresetCompiledComponents(); - throw e; - } - this.dynamicComponentModulesMap.set(comp, { - moduleRef, - moduleType: module.moduleType - }); - return of( { - componentType: comp, - componentModuleRef: moduleRef - }); + const comp = this.createAndCompileDynamicComponent(componentType, template, componentImports, preserveWhitespaces, styles); + return of(comp.type); }) ); } - public destroyDynamicComponent(componentType: Type) { - const moduleData = this.dynamicComponentModulesMap.get(componentType); - if (moduleData) { - moduleData.moduleRef.destroy(); - this.compiler.clearCacheFor(moduleData.moduleType); - this.dynamicComponentModulesMap.delete(componentType); - } + public destroyDynamicComponent(_componentType: Type) { + } + + public getComponentDef(componentType: Type): ɵComponentDef { + return componentType[ɵNG_COMP_DEF]; } - private _createDynamicComponent(componentType: Type, template: string, preserveWhitespaces?: boolean, styles?: string[]): Type { + private createAndCompileDynamicComponent(componentType: Type, template: string, imports: Type[], + preserveWhitespaces?: boolean, styles?: string[]): ɵComponentDef { // noinspection AngularMissingOrInvalidDeclarationInModule - return Component({ + const comp = Component({ template, + imports, preserveWhitespaces, - styles + styles, + standalone: true })(componentType); + // Trigger component compilation + return comp[ɵNG_COMP_DEF]; } } diff --git a/ui-ngx/src/app/core/services/resources.service.ts b/ui-ngx/src/app/core/services/resources.service.ts index 449e950a65..013c3e5d72 100644 --- a/ui-ngx/src/app/core/services/resources.service.ts +++ b/ui-ngx/src/app/core/services/resources.service.ts @@ -21,7 +21,7 @@ import { Injectable, Injector, ModuleWithComponentFactories, - Type + Type, ɵNG_MOD_DEF } from '@angular/core'; import { DOCUMENT } from '@angular/common'; import { forkJoin, Observable, ReplaySubject, throwError } from 'rxjs'; @@ -50,7 +50,6 @@ export class ResourcesService { private loadedJsonResources: { [url: string]: ReplaySubject } = {}; private loadedResources: { [url: string]: ReplaySubject } = {}; - private loadedModules: { [url: string]: ReplaySubject[]> } = {}; private loadedModulesAndFactories: { [url: string]: ReplaySubject } = {}; private anchor = this.document.getElementsByTagName('head')[0] || this.document.getElementsByTagName('body')[0]; @@ -201,71 +200,6 @@ export class ResourcesService { ); } - public loadModules(resourceId: string | TbResourceId, modulesMap: IModulesMap): Observable[]> { - const url = this.getDownloadUrl(resourceId); - if (this.loadedModules[url]) { - return this.loadedModules[url].asObservable(); - } - modulesMap.init(); - const meta = this.getMetaInfo(resourceId); - const subject = new ReplaySubject[]>(); - this.loadedModules[url] = subject; - import('@angular/compiler').then( - () => { - System.import(url, undefined, meta).then( - (module) => { - try { - let modules; - try { - modules = this.extractNgModules(module); - } catch (e) { - console.error(e); - } - if (modules && modules.length) { - const tasks: Promise>[] = []; - for (const m of modules) { - tasks.push(this.compiler.compileModuleAndAllComponentsAsync(m)); - } - forkJoin(tasks).subscribe((compiled) => { - try { - for (const c of compiled) { - c.ngModuleFactory.create(this.injector); - } - this.loadedModules[url].next(modules); - this.loadedModules[url].complete(); - } catch (e) { - this.loadedModules[url].error(new Error(`Unable to init module from url: ${url}`)); - } - }, - (e) => { - this.loadedModules[url].error(new Error(`Unable to compile module from url: ${url}`)); - }); - } else { - this.loadedModules[url].error(new Error(`Module '${url}' doesn't have default export or not NgModule!`)); - } - } catch (e) { - this.loadedModules[url].error(new Error(`Unable to load module from url: ${url}`)); - } - }, - (e) => { - this.loadedModules[url].error(new Error(`Unable to load module from url: ${url}`)); - console.error(`Unable to load module from url: ${url}`, e); - } - ); - } - ); - return subject.asObservable().pipe( - tap({ - next: () => System.delete(url), - error: () => { - delete this.loadedModulesAndFactories[url]; - System.delete(url); - }, - complete: () => System.delete(url) - }) - ); - } - private extractNgModules(module: any, modules: Type[] = []): Type[] { try { let potentialModules = [module]; @@ -274,7 +208,7 @@ export class ResourcesService { while (potentialModules.length && currentScanDepth < 10) { const newPotentialModules = []; for (const potentialModule of potentialModules) { - if (potentialModule && ('ɵmod' in potentialModule)) { + if (potentialModule && (ɵNG_MOD_DEF in potentialModule)) { modules.push(potentialModule); } else { for (const k of Object.keys(potentialModule)) { @@ -349,7 +283,6 @@ export class ResourcesService { } private clearModulesCache() { - this.loadedModules = {}; this.loadedModulesAndFactories = {}; } } diff --git a/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog-container.component.ts b/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog-container.component.ts index 089297427f..889a19a5aa 100644 --- a/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog-container.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog-container.component.ts @@ -17,11 +17,12 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { Component, - ComponentFactory, - ComponentRef, HostBinding, + ComponentRef, + HostBinding, Inject, - Injector, NgModuleRef, - OnDestroy, Type, + Injector, + OnDestroy, + Type, ViewContainerRef } from '@angular/core'; import { DialogComponent } from '@shared/components/dialog.component'; @@ -35,13 +36,11 @@ import { } from '@home/components/widget/dialog/custom-dialog.component'; import { DialogService } from '@core/services/dialog.service'; import { TranslateService } from '@ngx-translate/core'; -import { DynamicComponentModule } from '@core/services/dynamic-component-factory.service'; export interface CustomDialogContainerData { controller: (instance: CustomDialogComponent) => void; data?: any; customComponentType: Type; - customComponentModuleRef: NgModuleRef; } @Component({ @@ -80,7 +79,7 @@ export class CustomDialogContainerComponent extends DialogComponent { + mergeMap((componentType) => { const dialogData: CustomDialogContainerData = { controller, - customComponentType: componentData.componentType, - customComponentModuleRef: componentData.componentModuleRef, + customComponentType: componentType, data }; let dialogConfig: MatDialogConfig = { @@ -79,7 +78,7 @@ export class CustomDialogService { CustomDialogContainerComponent, dialogConfig).afterClosed().pipe( tap(() => { - this.dynamicComponentFactoryService.destroyDynamicComponent(componentData.componentType); + this.dynamicComponentFactoryService.destroyDynamicComponent(componentType); }) ); } diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts index 508d07ea0d..8e1e6f1b3a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts @@ -381,9 +381,8 @@ export class WidgetComponentService { widgetInfo.templateHtml, resolvedModules.modules ).pipe( - map((componentData) => { - widgetInfo.componentType = componentData.componentType; - widgetInfo.componentModuleRef = componentData.componentModuleRef; + map((componentType) => { + widgetInfo.componentType = componentType; return null; }), catchError(e => { diff --git a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts index 76845a11ef..5bd4910af7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts @@ -760,7 +760,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI try { this.dynamicWidgetComponentRef = this.widgetContentContainer.createComponent(this.widgetInfo.componentType, - {index: 0, injector, ngModuleRef: this.widgetInfo.componentModuleRef}); + {index: 0, injector}); this.cd.detectChanges(); } catch (e) { if (this.dynamicWidgetComponentRef) { diff --git a/ui-ngx/src/app/modules/home/models/widget-component.models.ts b/ui-ngx/src/app/modules/home/models/widget-component.models.ts index 5a1a28472e..673bc21edb 100644 --- a/ui-ngx/src/app/modules/home/models/widget-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/widget-component.models.ts @@ -47,7 +47,7 @@ import { WidgetActionsApi, WidgetSubscriptionApi } from '@core/api/widget-api.models'; -import { ChangeDetectorRef, Injector, NgModuleRef, NgZone, Type } from '@angular/core'; +import { ChangeDetectorRef, Injector, NgZone, Type } from '@angular/core'; import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { RafService } from '@core/services/raf.service'; import { WidgetTypeId } from '@shared/models/id/widget-type-id'; @@ -101,7 +101,6 @@ import { AlarmQuery, AlarmSearchStatus, AlarmStatus } from '@app/shared/models/a import { ImagePipe, MillisecondsToTimeStringPipe, TelemetrySubscriber } from '@app/shared/public-api'; import { UserId } from '@shared/models/id/user-id'; import { UserSettingsService } from '@core/http/user-settings.service'; -import { DynamicComponentModule } from '@core/services/dynamic-component-factory.service'; import { DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; import { UtilsService } from '@core/services/utils.service'; @@ -549,7 +548,6 @@ export interface WidgetInfo extends WidgetTypeDescriptor, WidgetControllerDescri description?: string; tags?: string[]; componentType?: Type; - componentModuleRef?: NgModuleRef; } export interface WidgetConfigComponentData { diff --git a/ui-ngx/src/app/shared/components/markdown.component.ts b/ui-ngx/src/app/shared/components/markdown.component.ts index 069bca2b2d..d890817d04 100644 --- a/ui-ngx/src/app/shared/components/markdown.component.ts +++ b/ui-ngx/src/app/shared/components/markdown.component.ts @@ -22,7 +22,8 @@ import { EventEmitter, Inject, Injector, - Input, NgZone, + Input, + NgZone, OnChanges, Output, Renderer2, @@ -132,7 +133,8 @@ export class TbMarkdownComponent implements OnChanges { let readyObservable: Observable; if (this.applyDefaultMarkdownStyle) { if (!defaultMarkdownStyle) { - defaultMarkdownStyle = deepClone(TbMarkdownComponent['ɵcmp'].styles)[0].replace(/\[_nghost\-%COMP%\]/g, '') + const compDef = this.dynamicComponentFactoryService.getComponentDef(TbMarkdownComponent); + defaultMarkdownStyle = deepClone(compDef.styles[0]).replace(/\[_nghost\-%COMP%\]/g, '') .replace(/\[_ngcontent\-%COMP%\]/g, ''); } styles.push(defaultMarkdownStyle); @@ -161,13 +163,13 @@ export class TbMarkdownComponent implements OnChanges { template, compileModules, true, styles - ).subscribe((componentData) => { - this.tbMarkdownInstanceComponentType = componentData.componentType; + ).subscribe((componentType) => { + this.tbMarkdownInstanceComponentType = componentType; const injector: Injector = Injector.create({providers: [], parent: this.markdownContainer.injector}); try { this.tbMarkdownInstanceComponentRef = this.markdownContainer.createComponent(this.tbMarkdownInstanceComponentType, - {index: 0, injector, ngModuleRef: componentData.componentModuleRef}); + {index: 0, injector}); if (this.context) { for (const propName of Object.keys(this.context)) { this.tbMarkdownInstanceComponentRef.instance[propName] = this.context[propName];