Fixing lazy Instance Removing.

This commit is contained in:
Martin Karkowski 2020-10-15 11:38:59 +02:00
parent fe55fdf973
commit d1df3a60ea
6 changed files with 177 additions and 40 deletions

View File

@ -1,5 +1,5 @@
import { getSingleton } from "../helpers/singletonMethod";
import { nopeObservable } from "../observables/nopeObservable";
import { NopeObservable } from "../observables/nopeObservable";
import { INopeRpcDispatcherOptions } from "../types/communication.interface";
import { nopeDispatcher } from "./nopeDispatcher";
@ -10,7 +10,7 @@ import { nopeDispatcher } from "./nopeDispatcher";
export function getRpcDispatcher(options: INopeRpcDispatcherOptions) {
const container = getSingleton('nopeBackendDispatcher.instance', () => {
return new nopeDispatcher(options, () => new nopeObservable());
return new nopeDispatcher(options, () => new NopeObservable());
});
return container.instance

View File

@ -2,12 +2,12 @@
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-08-25 23:27:28
* @modify date 2020-10-13 12:46:56
* @modify date 2020-10-14 18:18:08
* @desc [description]
*/
import { getSingleton } from "../helpers/singletonMethod";
import { nopeObservable } from "../observables/nopeObservable";
import { NopeObservable } from "../observables/nopeObservable";
import { INopeRpcDispatcherOptions } from "../types/communication.interface";
import { getRpcDispatcher } from "./getDispatcher";
import { IExportFunctionToDispatcherParameters, IExportMethodToDispatcherParameters, IExportPropertyToDispatcherParameters } from "./nopeDispatcherDecorators";
@ -26,7 +26,7 @@ export function getLinkedDispatcher(options: INopeRpcDispatcherOptions) {
}>(),
parameters: new Map<string, {
uri: string,
item: nopeObservable<any>,
item: NopeObservable<any>,
options: IExportPropertyToDispatcherParameters
}>(),
functions: new Map<string, {

View File

@ -2,7 +2,7 @@
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-10-12 18:52:00
* @modify date 2020-10-13 13:02:32
* @modify date 2020-10-15 09:20:41
* @desc [description]
*/
@ -10,7 +10,7 @@ import { inject, injectable } from 'inversify';
import { Logger } from "winston";
import { generateId } from '../helpers/idMethods';
import { DISPATCHER_OPTIONS, OBSERVABLE_FACTORY } from '../symbols/identifiers';
import { IAvailableInstanceGenerators, IAvailableServicesMsg, IAvailableTopicsMsg, ICallOptions, ICommunicationInterface, IExternalEventMsg, IInstanceCreation, IInstanceDescription, INopeRpcDispatcherOptions, IRequestTaskMsg, IResponseTaskMsg } from '../types/communication.interface';
import { IAvailableInstanceGenerators, IAvailableServicesMsg, IAvailableTopicsMsg, ICallOptions, ICommunicationInterface, IExternalEventMsg, IInstanceCreation, IInstanceDescription, IInstanceRemoval, INopeRpcDispatcherOptions, IRequestTaskMsg, IResponseTaskMsg } from '../types/communication.interface';
import { IGenerateRemoteInstanceCallback, IGenerateRemoteInstanceForOtherDispatcherCallback, INopeDispatcher } from '../types/nopeDispatcher.interface';
import { INopeModule } from '../types/nopeModule.interface';
import { INopeObservable, IPipe } from '../types/nopeObservable.interface';
@ -38,8 +38,8 @@ export class nopeDispatcher implements INopeDispatcher {
protected _definedFunctions: Map<string, (...args) => Promise<any>>;
protected _remotlyCalledFunctions: Set<string>;
protected _communicatorCallbacks: Map<string, {
registeredId: string,
type: 'request' | 'response'
registeredId: string,
type: 'request' | 'response',
cb: (data) => any
}>;
protected _communicator: ICommunicationInterface;
@ -119,7 +119,6 @@ export class nopeDispatcher implements INopeDispatcher {
this._init();
}
provideInstanceGeneratorForExternalDispatchers<I extends INopeModule>(identifier: string, cb: IGenerateRemoteInstanceForOtherDispatcherCallback<I>) {
const _this = this;
const _cb = this.registerFunction(async (data: IInstanceCreation) => {
@ -133,19 +132,50 @@ export class nopeDispatcher implements INopeDispatcher {
// Initialize the instance with the parameters.
await _instance.init(...data.params);
_this.registerFunction(() => _instance.dispose(), {
deleteAfterCalling: true,
// A Function is registered, taking care of removing
// an instances, if it isnt needed any more.
_this.registerFunction(async (_data: IInstanceRemoval) => {
if (_this._instances.get(data.identifier)?.usedBy) {
const idx = _this._instances.get(data.identifier).usedBy.indexOf(_data.dispatcherID);
if (idx > -1) {
_this._instances.get(data.identifier).usedBy.splice(idx,1);
}
if ( _this._instances.get(data.identifier).usedBy.length == 0) {
// Remove the Instance.
await _instance.dispose();
// Delete the Entry.
_this._instances.delete(data.identifier);
// Remove the Function itself
_this.unregisterFunction('instance_dispose_' + data.identifier)
}
}
}, {
deleteAfterCalling: false,
id: 'instance_dispose_' + data.identifier
});
// Store the Instance.
_this._instances.set(data.identifier, _instance);
_this._instances.set(data.identifier, {
instance: _instance,
usedBy: [ data.dispatcherID ]
});
} else {
// If an Element exists => Add the Element.
_this._instances.get(data.identifier).usedBy.push(data.dispatcherID);
}
// Define the Response.
const response: IInstanceDescription = {
description: _this._instances.get(data.identifier).toDescription(),
description: _this._instances.get(data.identifier).instance.toDescription(),
type: data.type,
}
// Send the Response
return response
}, {
id: 'generateInstance_' + identifier,
@ -169,22 +199,47 @@ export class nopeDispatcher implements INopeDispatcher {
protected _internalGenerators: Map<string, IGenerateRemoteInstanceCallback<INopeModule>>;
protected _externalGenerators: Map<string, IGenerateRemoteInstanceCallback<INopeModule>>;
protected _instances: Map<string, INopeModule>;
protected _instances: Map<string, {
instance: INopeModule,
usedBy: Array<string>,
}>;
public async generateInstance<I extends INopeModule>(description: IInstanceCreation): Promise<I> {
if (this._instances.has(description.identifier)) {
return this._instances.get(description.identifier) as I;
public async generateInstance<I extends INopeModule>(description: Partial<IInstanceCreation>): Promise<I> {
const _defDescription: IInstanceCreation = {
dispatcherID: this.id,
identifier: 'error',
params: [],
type: 'unkown'
}
const _description = Object.assign(_defDescription, description, { dispatcherID: this.id }) as IInstanceCreation;
if (_defDescription.type === 'unkown' || _description.identifier === 'error') {
throw Error('Please Provide at least a "type" and "identifier" in the paremeters');
}
if (this._instances.has(_description.identifier)) {
// Add the Dispatcher to the Element:
this._instances.get(_description.identifier).usedBy.push(_description.dispatcherID);
// Return the Instance.
return this._instances.get(_description.identifier).instance as I;
}
try {
if (this._internalGenerators.has(description.type)) {
const result = await this.performCall<IInstanceDescription>('generateInstance_' + description.type, [description], {
if (this._internalGenerators.has(_description.type)) {
const result = await this.performCall<IInstanceDescription>('generateInstance_' + _description.type, [_description], {
paramsHasNoCallback: true,
});
const instance = await this._internalGenerators.get(description.type)(this, result.description) as I;
// Create the Instance
const instance = await this._internalGenerators.get(_description.type)(this, result.description) as I;
this._instances.set(description.identifier, instance);
// Store the Instances.
this._instances.set(_description.identifier, {
instance,
usedBy: [_description.dispatcherID]
});
return instance;
}
@ -200,12 +255,50 @@ export class nopeDispatcher implements INopeDispatcher {
throw new Error("No Dynamic Interface provided");
}
public async deleteInstance<I extends INopeModule>(instance: I): Promise<boolean> {
// Remove the Item:
this._instances.delete(instance.identifier);
// Dispose the Instance.
await instance.dispose();
return true;
public async deleteInstance<I extends INopeModule>(instance: I | string): Promise<boolean> {
// Block to find the instance.
// Based on the property (string or instance)
// the corresponding instance object has to be select.
let _instance: { instance: INopeModule; usedBy: Array<string>; };
if (typeof instance === 'string'){
_instance = this._instances.get(instance);
} else {
for (const data of this._instances.values()){
if (instance == data.instance){
_instance = data;
break;
}
}
}
// if the instance has been found => delete the instance.
if (_instance) {
_instance.usedBy.pop();
if (_instance.usedBy.length === 0){
const params: IInstanceRemoval = {
dispatcherID: this.id,
identifier: _instance.instance.identifier
}
// Call the corresponding Dispose Function for the "real" instance
// All other elements are just accessors.
await this.performCall('instance_dispose_' + _instance.instance.identifier, [params]);
// Delete the Identifier
this._instances.delete(_instance.instance.identifier);
// Dispose the Handler;
await _instance.instance.dispose();
}
return true;
}
return false;
}
/**
@ -529,7 +622,6 @@ export class nopeDispatcher implements INopeDispatcher {
}
}
/**
* Function, used to subscribe to results.
*
@ -1202,12 +1294,16 @@ export class nopeDispatcher implements INopeDispatcher {
// Dispose all Instances.
for (const [name, instance] of this._instances.entries()) {
instance.dispose().catch(e => {
// Remove the Instance.
this.deleteInstance(name).catch(e => {
if (_this._logger) {
_this._logger.error('Failed Removing Instance "' + name + '"');
_this._logger.error(e)
}
});
}
}
this._instances = new Map();

View File

@ -1,5 +1,5 @@
import { getSingleton } from "../helpers/singletonMethod";
import { nopeObservable } from "../observables/nopeObservable";
import { NopeObservable } from "../observables/nopeObservable";
// Symbols for the Property Registery:
const _registeredDispatcherMethods_ = Symbol('_registeredDispatcherMethods_');
@ -37,7 +37,7 @@ const container = getSingleton('nopeBackendDispatcher.container', () => {
}>(),
parameters: new Map<string, {
uri: string,
item: nopeObservable<any>,
item: NopeObservable<any>,
options: IExportPropertyToDispatcherParameters
}>(),
functions: new Map<string, {

View File

@ -2,7 +2,7 @@
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-10-12 18:31:54
* @modify date 2020-10-13 12:44:45
* @modify date 2020-10-14 07:52:52
* @desc [description]
*/
@ -194,9 +194,14 @@ export interface IInstanceDescription {
export interface IInstanceCreation {
type: string,
identifier: string,
params: any
params: any,
dispatcherID: string,
}
export interface IInstanceRemoval {
identifier: string,
dispatcherID: string,
}
export type IAvailableTopicsMsg = {
/**

View File

@ -96,6 +96,10 @@ export class HelloWorldModule extends NopeBaseModule implements IHelloWorlModule
topic: 'testProp'
})
}
async dispose(){
console.log('Deleting Module')
}
}
const communicator = new EventLayer(
@ -103,13 +107,14 @@ const communicator = new EventLayer(
'generic'
);
const local = new nopeDispatcher({ communicator }, () => new NopeObservable());
const remote = getLinkedDispatcher({ communicator });
const remote_01 = new nopeDispatcher({ communicator }, () => new NopeObservable());
const remote_02 = new nopeDispatcher({ communicator }, () => new NopeObservable());
const srv = getLinkedDispatcher({ communicator });
const main = async () => {
remote.provideInstanceGeneratorForExternalDispatchers('helloworld', async (dispather, identifier) => {
srv.provideInstanceGeneratorForExternalDispatchers('helloworld', async (dispather, identifier) => {
const mod = new HelloWorldModule(dispather);
mod.identifier = identifier;
@ -117,7 +122,7 @@ const main = async () => {
return mod;
});
local.registerInternalInstanceGenerator('helloworld', async (dispather, description) => {
remote_01.registerInternalInstanceGenerator('helloworld', async (dispather, description) => {
const mod = new NopeGenericModule(dispather, () => new NopeObservable());
await mod.fromDescription(description, 'overwrite');
await mod.init();
@ -126,10 +131,25 @@ const main = async () => {
return mod;
});
const instance_01 = await local.generateInstance<IHelloWorlModule>({
remote_02.registerInternalInstanceGenerator('helloworld', async (dispather, description) => {
const mod = new NopeGenericModule(dispather, () => new NopeObservable());
await mod.fromDescription(description, 'overwrite');
await mod.init();
console.log('Created HelloWorldModule-Wrapper with name', description.identifier);
return mod;
});
const instance_01 = await remote_01.generateInstance<IHelloWorlModule>({
identifier: 'instance_01',
params: ['hello', 'world'],
type: 'helloworld'
type: 'helloworld',
});
const copy_of_instance_01 = await remote_01.generateInstance<IHelloWorlModule>({
identifier: 'instance_01',
params: ['hello', 'world'],
type: 'helloworld',
});
// await instance_01.init();
@ -149,6 +169,22 @@ const main = async () => {
console.log('Got Update by subscribing');
console.log('Updating Variable by updateTestProp Function');
await instance_01.updateTestProp();
console.log('Creating new instance on other remote');
const instance_02 = await remote_02.generateInstance<IHelloWorlModule>({
identifier: 'instance_01',
params: ['hello', 'world'],
type: 'helloworld',
});
console.log('Delte Copies etc');
remote_01.deleteInstance(instance_01);
remote_01.deleteInstance(copy_of_instance_01);
console.log('Delete Instance 2')
remote_02.deleteInstance(instance_02);
await sleep(100);
}