aotrautils-srv 0.0.1057 → 0.0.1059

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
 
2
2
 
3
- /*utils COMMONS library associated with aotra version : «1_29072022-2359 (13/10/2024-22:46:19)»*/
3
+ /*utils COMMONS library associated with aotra version : «1_29072022-2359 (19/02/2025-23:50:43)»*/
4
4
  /*-----------------------------------------------------------------------------*/
5
5
 
6
6
 
@@ -1551,6 +1551,13 @@ aotest.profile=function(rootObject,methodName,visited=[]){
1551
1551
 
1552
1552
 
1553
1553
 
1554
+ //================================================================
1555
+ //================= Tjread control utility methods =================
1556
+ //================================================================
1557
+
1558
+
1559
+ window.sleep = (millis) => new Promise( resolve => setTimeout(resolve, millis) );
1560
+
1554
1561
 
1555
1562
 
1556
1563
  //================================================================
@@ -1988,6 +1995,15 @@ window.compare=function(value1,value2){
1988
1995
  if(value1==value2) return 0;
1989
1996
  }
1990
1997
 
1998
+ window.pushInArrayAsQueue=function(array, maxSize, item){
1999
+ if(maxSize<=getArraySize(array)){
2000
+ array.shift();
2001
+ }
2002
+ array.push(item);
2003
+ return item;
2004
+ }
2005
+
2006
+
1991
2007
 
1992
2008
  /*KNOWN LIMITATIONS : You will have an incomplete browsing loop if there are the string values "break" or "continue" in your array or associative array !*/
1993
2009
  // CAUTION : Only use «return foreach» syntax in client code loops with SINGLE-LEVEL nested loops ONLY !
@@ -1998,7 +2014,7 @@ window.foreach=function(arrayOfValues,doOnEachFunction,
1998
2014
  // -1: if(a<b)
1999
2015
  // 1: if(a>b)
2000
2016
  // for instance this is a valid javascript compare function : function(a, b){return a-b} so (100,20) returns positive result and (20,100) returns negative result !
2001
- /*OPTIONAL*/compareFunction=null){
2017
+ /*OPTIONAL*/compareFunction=null, sortKeys=false){
2002
2018
 
2003
2019
  // TODO : FIXME : Add the possibility to iterate over a string characters ?
2004
2020
 
@@ -2054,7 +2070,8 @@ window.foreach=function(arrayOfValues,doOnEachFunction,
2054
2070
 
2055
2071
  // Else : associative array
2056
2072
 
2057
- var associativeArray=arrayOfValues;
2073
+ let keysToIterateOn=[];
2074
+ const associativeArray=arrayOfValues;
2058
2075
  if(compareFunction){
2059
2076
 
2060
2077
  // if(!associativeArray.hasOwnProperty){
@@ -2075,24 +2092,26 @@ window.foreach=function(arrayOfValues,doOnEachFunction,
2075
2092
  }
2076
2093
  sortedKeysAndValues.sort(compareFunction);
2077
2094
 
2078
- associativeArray={};
2095
+ // OLD : associativeArray={};
2079
2096
  for(var i=0;i<sortedKeysAndValues.length;i++){
2080
2097
  var item=sortedKeysAndValues[i];
2081
- associativeArray[item.key]=item.value;
2098
+ // OLD : associativeArray[item.key]=item.value;
2099
+ keysToIterateOn.push(item.key);
2082
2100
  }
2083
2101
 
2102
+ }else{
2103
+ keysToIterateOn=Object.keys(associativeArray);
2084
2104
  }
2085
2105
 
2086
- //if(!associativeArray.hasOwnProperty){
2087
- // // TRACE
2088
- // console.log("WARN : Object of type «"+(typeof(associativeArray))+"» has no method hasOwnProperty() method, cannot perform foreach ! ",associativeArray);
2089
- // return null;
2090
- //}
2091
-
2106
+ if(!compareFunction && sortKeys){
2107
+ keysToIterateOn=keysToIterateOn.sort();
2108
+ }
2109
+
2092
2110
  var cnt=0;
2093
- for(var key in associativeArray){
2111
+ for(var key of keysToIterateOn){
2094
2112
  if(associativeArray.hasOwnProperty && !associativeArray.hasOwnProperty(key)) continue;
2095
2113
  var item=associativeArray[key];
2114
+
2096
2115
  if(!compareFunction){ // If we have a compare function, then array is already filtered !
2097
2116
  if(filterFunction && !filterFunction(item,key)) continue;
2098
2117
  }
@@ -2108,6 +2127,8 @@ window.foreach=function(arrayOfValues,doOnEachFunction,
2108
2127
  }// else continue looping...
2109
2128
  cnt++;
2110
2129
  }
2130
+
2131
+
2111
2132
 
2112
2133
  //
2113
2134
  return null;
@@ -2124,7 +2145,7 @@ window.getAt=function(associativeOrNormalArray,index,returnKey=false){
2124
2145
  return foreach(associativeOrNormalArray,(item, key)=>{
2125
2146
  if(index<=i) return returnKey?key:item;
2126
2147
  i++;
2127
- });
2148
+ },null,null,returnKey);
2128
2149
  }
2129
2150
 
2130
2151
  window.getFirst=function(associativeOrNormalArray){
@@ -2856,24 +2877,46 @@ Math.averageInArray=function(arrayOfValues){
2856
2877
  return Math.sumInArray(arrayOfValues) / arrayOfValues.length;
2857
2878
  };
2858
2879
 
2859
- Math.maxInArray=function(arrayOfValues){
2880
+ Math.maxInArray=function(arrayOfValues, returnKey=false){
2860
2881
  if(empty(arrayOfValues))
2861
2882
  return null;
2862
- var max=arrayOfValues[0];
2863
- for (var i=0; i<arrayOfValues.length; i++)
2864
- if(max<arrayOfValues[i])
2865
- max=arrayOfValues[i];
2866
- return max;
2883
+
2884
+ // var max=arrayOfValues[0];
2885
+ // for (var i=0; i<arrayOfValues.length; i++)
2886
+ // if(max<arrayOfValues[i])
2887
+ // max=arrayOfValues[i];
2888
+
2889
+ let returnedKey=getKeyAt(arrayOfValues,0);
2890
+ let max=getAt(arrayOfValues,0);
2891
+ foreach(arrayOfValues,(item, key)=>{
2892
+ if(max<item){
2893
+ returnedKey=key;
2894
+ max=item;
2895
+ }
2896
+ },null,null,true);
2897
+
2898
+ return (returnKey?returnedKey:max);
2867
2899
  };
2868
2900
 
2869
- Math.minInArray=function(arrayOfValues){
2901
+ Math.minInArray=function(arrayOfValues, returnKey=false){
2870
2902
  if(empty(arrayOfValues))
2871
2903
  return null;
2872
- var min=arrayOfValues[0];
2873
- for (var i=0; i<arrayOfValues.length; i++)
2874
- if(arrayOfValues[i]<min)
2875
- min=arrayOfValues[i];
2876
- return min;
2904
+
2905
+ // var min=arrayOfValues[0];
2906
+ // for (var i=0; i<arrayOfValues.length; i++)
2907
+ // if(arrayOfValues[i]<min)
2908
+ // min=arrayOfValues[i];
2909
+
2910
+ let returnedKey=getKeyAt(arrayOfValues,0);
2911
+ let min=getAt(arrayOfValues,0);
2912
+ foreach(arrayOfValues,(item, key)=>{
2913
+ if(item<min){
2914
+ returnedKey=key;
2915
+ min=item;
2916
+ }
2917
+ },null,null,true);
2918
+
2919
+ return (returnKey?returnedKey:min);
2877
2920
  };
2878
2921
 
2879
2922
 
@@ -5248,6 +5291,25 @@ window.stringifyObject=function(objectToStringify){
5248
5291
  }
5249
5292
 
5250
5293
 
5294
+ window.splitURL=(urlOrigin)=>{
5295
+ let protocol=null;
5296
+ let hostAndPort=urlOrigin;
5297
+ if(contains(urlOrigin,"://")){
5298
+ const hostAndPortSplits=urlOrigin.split("://");
5299
+ protocol=hostAndPortSplits[0];
5300
+ hostAndPort=hostAndPortSplits[1];
5301
+ }
5302
+ let host=null, port=null;
5303
+ if(contains(hostAndPort,":")){
5304
+ const splits=hostAndPort.split(":");
5305
+ host=splits[0];
5306
+ port=splits[1];
5307
+ }else{
5308
+ host=hostAndPort;
5309
+ }
5310
+ const isSecure=(protocol && contains(protocol,"s"));
5311
+ return {protocol:protocol, host:host, port:port, isSecure:isSecure};
5312
+ };
5251
5313
 
5252
5314
 
5253
5315
 
@@ -5259,7 +5321,7 @@ AOTRAUTILS_LIB_IS_LOADED=true;
5259
5321
 
5260
5322
 
5261
5323
 
5262
- /*utils AI library associated with aotra version : «1_29072022-2359 (13/10/2024-22:46:19)»*/
5324
+ /*utils AI library associated with aotra version : «1_29072022-2359 (19/02/2025-23:50:43)»*/
5263
5325
  /*-----------------------------------------------------------------------------*/
5264
5326
 
5265
5327
 
@@ -5403,7 +5465,7 @@ getOpenAIAPIClient=(modelName, apiURL, agentRole, defaultPrompt)=>{
5403
5465
 
5404
5466
 
5405
5467
 
5406
- /*utils SERVER library associated with aotra version : «1_29072022-2359 (13/10/2024-22:46:19)»*/
5468
+ /*utils SERVER library associated with aotra version : «1_29072022-2359 (19/02/2025-23:50:43)»*/
5407
5469
  /*-----------------------------------------------------------------------------*/
5408
5470
 
5409
5471
 
@@ -5581,12 +5643,12 @@ getServerParams=function(portParam=null, certPathParam=null, keyPathParam=null,
5581
5643
  // https=require("https");
5582
5644
  // fs=require("fs");
5583
5645
 
5584
- if(!https){
5646
+ if(typeof(https)==="undefined"){
5585
5647
  // TRACE
5586
5648
  console.log("WARN : Could not find the nodejs dependency «https», aborting SSL setup.");
5587
5649
  return null;
5588
5650
  }
5589
- if(!fs){
5651
+ if(typeof(fs)==="undefined"){
5590
5652
  // TRACE
5591
5653
  console.log("WARN : Could not find the nodejs dependency «fs», aborting SSL setup.");
5592
5654
  return null;
@@ -5629,7 +5691,17 @@ getServerParams=function(portParam=null, certPathParam=null, keyPathParam=null,
5629
5691
  return result;
5630
5692
  }
5631
5693
 
5632
-
5694
+ getConsoleParam=function(index=0, argsOffset=0){
5695
+ let result=null;
5696
+ if(!process){
5697
+ throw new Error("ERROR : Cannot extract console parameter in this context !");
5698
+ }
5699
+ process.argv.forEach((val, i)=>{
5700
+ if(i<=argsOffset+1) return;
5701
+ else if(i==argsOffset+index+1) result=val;
5702
+ });
5703
+ return result;
5704
+ }
5633
5705
 
5634
5706
 
5635
5707
 
@@ -5697,6 +5769,7 @@ WebsocketImplementation={
5697
5769
  if(typeof(WebSocket)==="undefined"){
5698
5770
  // TRACE
5699
5771
  console.log("«ws» SERVER library not called yet, calling it now.");
5772
+
5700
5773
  WebSocket=require("ws");
5701
5774
  if(typeof(WebSocket)==="undefined"){
5702
5775
  // TRACE
@@ -5798,7 +5871,7 @@ WebsocketImplementation={
5798
5871
  receive:(channelNameParam, doOnIncomingMessage, clientsRoomsTag=null)=>{
5799
5872
 
5800
5873
  // DBG
5801
- lognow("(SERVER) Registering receive for «"+channelNameParam+"»...",doOnIncomingMessage);
5874
+ lognow("(SERVER) Registering receive for «"+channelNameParam+"»...");
5802
5875
 
5803
5876
 
5804
5877
 
@@ -5836,10 +5909,11 @@ WebsocketImplementation={
5836
5909
 
5837
5910
  if(!isClientInRoom) return;
5838
5911
 
5839
- // DBG
5840
- lognow("(SERVER) doOnIncomingMessage:",doOnIncomingMessage);
5841
- if(doOnIncomingMessage) doOnIncomingMessage(dataWrapped.data, clientSocketParam);
5842
-
5912
+ if(doOnIncomingMessage){
5913
+ // DBG
5914
+ lognow("(SERVER) doOnIncomingMessage:");
5915
+ doOnIncomingMessage(dataWrapped.data, clientSocketParam);
5916
+ }
5843
5917
 
5844
5918
  },
5845
5919
  };
@@ -6097,7 +6171,7 @@ WebsocketImplementation={
6097
6171
  return nodeServerInstance;
6098
6172
  },
6099
6173
 
6100
-
6174
+ // DO NOT USE DIRECTLY, USE INSTEAD initClient(...) (this function uses connectToServer(...)) !
6101
6175
  // NODE / BROWSER CLIENT CONNECTS TO SERVER MAIN ENTRYPOINT:
6102
6176
  connectToServer:(serverURL, port, isSecure=false, timeout)=>{
6103
6177
 
@@ -6725,7 +6799,7 @@ launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /
6725
6799
  server.onFinalize((serverParam)=>{
6726
6800
 
6727
6801
  // DBG
6728
- lognow("onFinalize!!!!!");
6802
+ lognow("onFinalize() server");
6729
6803
 
6730
6804
  if(doOnFinalizeServer) doOnFinalizeServer(serverParam);
6731
6805
  });
@@ -6863,7 +6937,7 @@ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFina
6863
6937
  // Eventual encryption options :
6864
6938
  let sslOptions=null;
6865
6939
  if(!isForceUnsecure){
6866
- if(!fs){
6940
+ if(typeof(fs)==="undefined"){
6867
6941
  // TRACE
6868
6942
  lognow("ERROR : «fs» node subsystem not present, cannot access files. Aborting SSL configuration of server.");
6869
6943
  }else{
@@ -6914,7 +6988,7 @@ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFina
6914
6988
  //
6915
6989
  // const server=initNodeServerInfrastructureWrapper(
6916
6990
  // // On each client connection :
6917
- //// (serverParam, clientSocketParam)=>{},
6991
+ // // (serverParam, clientSocketParam)=>{},
6918
6992
  // null,
6919
6993
  // // On server finalization :
6920
6994
  // (serverParam)=>{
@@ -6947,15 +7021,15 @@ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFina
6947
7021
  // },portParam,certPathParam,keyPathParam);
6948
7022
  //
6949
7023
  //
6950
- //// const doOnConnect=(serverParam, clientSocketParam)=>{
6951
- //// };
6952
- //// const doOnFinalizeServer=(serverParam)=>{
6953
- //// /*DO NOTHING*/
6954
- //// };
6955
- //// const server={};
6956
- //// server.start=(port=6080)=>{
6957
- //// server.httpServer=launchNodeHTTPServer(port, doOnConnect, doOnFinalizeServer, sslOptions);
6958
- //// };
7024
+ // // const doOnConnect=(serverParam, clientSocketParam)=>{
7025
+ // // };
7026
+ // // const doOnFinalizeServer=(serverParam)=>{
7027
+ // // /*DO NOTHING*/
7028
+ // // };
7029
+ // // const server={};
7030
+ // // server.start=(port=6080)=>{
7031
+ // // server.httpServer=launchNodeHTTPServer(port, doOnConnect, doOnFinalizeServer, sslOptions);
7032
+ // // };
6959
7033
  //
6960
7034
  // return server;
6961
7035
  //}
@@ -7299,23 +7373,47 @@ appendGetParameters=function(apiURL, namedArgs){
7299
7373
 
7300
7374
  //*********************************** AUTO-ORGANIZING REAL-TIME AORTAC CLUSTERIZATION (AORTAC) *********************************** */
7301
7375
 
7376
+ AORTAC_OUTCOMING_SERVERS_CONNECTION_GLOBAL_TIMEOUT=30000;
7377
+ REQUESTS_IDS_HISTORY_SIZE=10;
7378
+
7302
7379
  class AORTACNode{
7303
- constructor(selfOrigin="127.0.0.1:40000",outcomingNodesOrigins=[], model, controller, sslConfig={/*OPTIONAL*/port:null,/*OPTIONAL*/certPath:null,/*OPTIONAL*/keyPath:null}){
7380
+
7381
+ constructor(nodeId, selfOrigin="127.0.0.1:40000",outcomingNodesOrigins=[], model, controller, allowAnonymousNodes=true, sslConfig={/*OPTIONAL*/certPath:null,/*OPTIONAL*/keyPath:null}){
7382
+
7383
+ this.nodeId=nodeId;
7304
7384
  this.selfOrigin=selfOrigin;
7385
+ this.sslConfig=sslConfig;
7305
7386
  this.outcomingNodesOrigins=outcomingNodesOrigins;
7387
+ this.allowAnonymousNodes=allowAnonymousNodes;
7306
7388
 
7307
- this.model=model; // must implement function
7308
- this.controller=controller; // Must implement function
7309
- this.serverId=getUUID();
7389
+ this.model=model; // must implement functions
7390
+ this.controller=controller; // Must implement functions
7310
7391
 
7311
7392
  this.server=null;
7312
7393
  this.clients={};
7313
7394
  this.incomingServers={};
7314
7395
  this.outcomingServers={};
7396
+ this.authorizedNodesIds=[this.nodeId];
7315
7397
 
7398
+ this.listeners={"protocol":[],"cluster":[],"neighbors":[],"inputs":[]};
7399
+
7400
+ this.executedRequestIdsHistory=[];
7401
+
7402
+ this.isConnectedToCluster=false;
7403
+
7404
+ this.modelObjectsDirectory={};
7405
+ this.objectsIndexesForClients={};
7406
+
7407
+ this.addProtocolListeners();
7408
+
7409
+ // TRACE
7410
+ lognow(`AORTAC node created with id ${this.nodeId}.`);
7316
7411
  }
7317
7412
 
7318
7413
  start(){
7414
+
7415
+ const splits=splitURL(this.selfOrigin);
7416
+ let port=nonull(splits.port,"40000");
7319
7417
 
7320
7418
  const self=this;
7321
7419
  this.server = initNodeServerInfrastructureWrapper(
@@ -7323,62 +7421,819 @@ class AORTACNode{
7323
7421
  null,
7324
7422
  // On client finalization :
7325
7423
  function(server){
7326
-
7327
7424
  self.server=server;
7328
7425
 
7329
- server.receive("protocol", (message, clientSocket)=>{
7330
-
7331
- // TRACE
7332
- console.log("INFO : SERVER : Client has sent a protocol message: ", message);
7333
-
7334
- // ============== OTHER SERVER / CLIENT REGISTRATION PHASE ==============
7335
-
7336
- if(message.type==="registerOtherServer"){
7337
- const incomingServerId=message.serverId;
7426
+ // Listeners handling ;
7427
+ foreach(Object.keys(self.listeners), channelName=>{
7428
+ self.server.receive(channelName, (message, clientSocket)=>{
7338
7429
 
7339
- self.incomingServers[incomingServerId]={clientSocket:clientSocket};
7340
-
7341
- }else if(message.type==="registerClient"){
7342
- const clientId=message.clientId;
7430
+ // INCOMING NODES LISTENERS HOOK :
7431
+ // TRACE
7432
+ console.log("INFO : SERVER : Client or incoming node has sent a "+channelName+" message: ", message);
7433
+ foreach(self.listeners[channelName], listener => {
7434
+ listener.execute(self, message, self.server, clientSocket);
7435
+ },(listener)=>(message.type===listener.messageType));
7343
7436
 
7344
- self.clients[incomingServerId]={clientSocket:clientSocket};
7345
- }
7346
-
7437
+ });
7347
7438
  });
7439
+
7440
+ // OLD : self.connectToOutcomingServers(self, server).then((server)=>{ self.doOnConnectedToCluster(); });
7441
+
7442
+
7443
+ }, port, this.sslConfig.certPath, this.sslConfig.keyPath);
7444
+
7445
+ this.server.serverManager.start();
7446
+
7447
+ // TRACE
7448
+ lognow(`AORTAC node started with id ${this.nodeId}.`);
7449
+
7348
7450
 
7451
+ return this;
7452
+ }
7453
+
7454
+
7455
+ connect(){
7456
+
7457
+ const self=this;
7458
+ self.connectToOutcomingServers(self).then((server)=>{ self.doOnConnectedToCluster(); });
7349
7459
 
7460
+ return this;
7461
+ }
7462
+
7463
+
7464
+
7465
+
7466
+ /*private*/addProtocolListeners(){
7467
+
7468
+ const self=this;
7469
+ // ============== OTHER SERVER / CLIENT REGISTRATION PHASE ==============
7470
+ // - OTHER SERVER REGISTRATION PHASE :
7471
+ this.listeners["protocol"].push({
7472
+ messageType:"request.register.server.incoming",
7473
+ execute:(self, message, server, clientSocket)=>{
7474
+ const incomingServerNodeId=message.nodeId;
7475
+
7476
+ // TRACE
7477
+ lognow(` (${self.nodeId}) Receiving registering request from node ${incomingServerNodeId}...`);
7478
+
7479
+ if(!self.allowAnonymousNodes && self.isInAuthorizedNodes(incomingServerNodeId)){
7480
+ // TRACE
7481
+ lognow("WARN : Cannot accept incoming server with node id «"+incomingServerNodeId+"» because it's not in the authorized nodes list.");
7482
+ return;
7483
+ }
7484
+ if(!contains(self.authorizedNodesIds,incomingServerNodeId)) self.authorizedNodesIds.push(incomingServerNodeId);
7485
+
7486
+ // TRACE
7487
+ lognow(` (${self.nodeId}) Adding registering node ${incomingServerNodeId} to incoming servers...`);
7488
+
7489
+ self.incomingServers[incomingServerNodeId]={clientSocket:clientSocket};
7490
+
7491
+ const welcomeResponse={
7492
+ nodeId:self.nodeId, // MUST BE SELF ! ELSE THE REGISTERING CLIENT WON'T KNOW WHICH ID IT HAS BEEN REGISTERED TO !
7493
+ type:"response.register.server.incoming",
7494
+ authorizedNodesIds:self.authorizedNodesIds
7495
+ };
7496
+ server.send("protocol", welcomeResponse, null, clientSocket);
7497
+
7498
+ // TRACE
7499
+ lognow(` (${self.nodeId}) Sent registering response to node ${incomingServerNodeId}.`);
7500
+ }
7501
+ });
7502
+
7503
+ // - CLIENT REGISTRATION PHASE : (for clients to the server, NOT INCOMING NODES !)
7504
+ this.handleClients();
7505
+
7506
+ }
7507
+
7508
+ /*private*/handleClients(){
7509
+ const self=this;
7510
+
7511
+ // --------- PROTOCOL HANDLING ---------
7512
+
7513
+ this.listeners["protocol"].push({
7514
+ messageType:"request.register.client",
7515
+ execute:(self, message, server, clientSocket)=>{
7516
+ const clientId=message.clientId;
7517
+
7518
+ self.clients[clientId]={clientSocket:clientSocket};
7519
+
7520
+ // TRACE
7521
+ lognow(` (${self.nodeId}) Receiving non-cluster client registration request from client ${clientId}.`);
7522
+
7523
+ const welcomeClientResponse={
7524
+ nodeId:self.nodeId, // MUST BE SELF ! ELSE THE REGISTERING CLIENT WON'T KNOW WHICH ID IT HAS BEEN REGISTERED TO !
7525
+ type:"response.register.client",
7526
+ // If necessary we also send them our model part :
7527
+ partialModelString:(message.isReferenceNode?null:JSON.stringifyDecycle(self.model)),
7528
+ isReferenceNode:(!!message.isReferenceNode),
7529
+ };
7530
+ server.send("protocol", welcomeClientResponse, null, clientSocket);
7531
+ }
7532
+ });
7533
+
7534
+ this.listeners["protocol"].push({
7535
+ messageType:"request.unregister.client",
7536
+ execute:(self, message, server, clientSocket)=>{
7537
+ const clientId=message.clientId;
7538
+
7539
+ delete self.clients[clientId];
7540
+
7541
+ // TRACE
7542
+ lognow(` (${self.nodeId}) Receiving non-cluster client unregistration request from client ${clientId}.`);
7543
+
7544
+ const unwelcomeClientResponse={
7545
+ nodeId:self.nodeId, // MUST BE SELF ! ELSE THE REGISTERING CLIENT WON'T KNOW WHICH ID IT HAS BEEN REGISTERED TO !
7546
+ type:"response.unregister.client",
7547
+ };
7548
+ server.send("protocol", unwelcomeClientResponse, null, clientSocket);
7549
+ }
7550
+ });
7551
+
7552
+
7553
+ this.listeners["protocol"].push({
7554
+ messageType:"request.interrogation.client",
7555
+ execute:(self, message, server, clientSocket)=>{
7556
+ const clientId=message.clientId;
7557
+
7558
+ // TRACE
7559
+ lognow(` (${self.nodeId}) Receiving non-cluster client interrogation request from client ${clientId}.`);
7560
+
7561
+ const clientBoundaries=message.boundaries;
7562
+
7563
+ // We ask the whole cluster to find the requested objects :
7564
+ const modelSeekObjectsRequest={
7565
+ type:"request.model.seekObjects",
7566
+ originatingClientId:clientId,
7567
+ clientBoundaries:clientBoundaries,
7568
+ };
7569
+ this.propagateToCluster(modelSeekObjectsRequest);
7570
+
7571
+ self.objectsIndexesForClients[clientId]={};
7572
+
7573
+ }
7574
+ });
7575
+
7576
+ // --------- INPUTS HANDLING ---------
7577
+
7578
+ this.listeners["inputs"].push({
7579
+ messageType:"request.inputs.client",
7580
+ execute:(self, message, server, clientSocket)=>{
7581
+ const clientId=message.clientId;
7582
+
7583
+ // TRACE
7584
+ lognow(` (${self.nodeId}) Receiving non-cluster client inputs request from client ${clientId}.`);
7585
+
7586
+ const clientInputs=message.inputs;
7587
+ const clientSubBoundaries=message.subBoundaries;
7588
+
7589
+
7590
+ // We let the controller interpret these inputs :
7591
+ const modifiedObjects=self.controller.interpretInputs(clientInputs, clientSubBoundaries);
7592
+
7593
+
7594
+ }
7595
+ });
7596
+
7597
+ }
7598
+
7599
+
7600
+ /*private*/addPropagationForClientsListeners(){
7601
+
7602
+ const self=this;
7603
+
7604
+ // ------------------------------------- CLIENTS PROTOCOL HANDLING -------------------------------------
7605
+ // Adding propagation listener :
7606
+ this.setPropagation("cluster","request.model.seekObjects",(self, message, server, clientSocket)=>{
7607
+
7608
+ // If we receive this request from cluster, then we try to find the requested objects
7609
+ const relayNodeid=message.nodeId;
7610
+ const originatingClientId=message.originatingClientId;
7611
+ const clientBoundaries=message.clientBoundaries
7612
+ const objectsIds=self.model.getObjectsIdsWithinBoundaries(clientBoundaries);
7613
+
7614
+ // If server has not the seeked objects, it answers with an empty array.
7615
+
7616
+ // TRACE
7617
+ lognow(`(${self.nodeId}) Node receiving a seek objects request from relay node ${message.nodeId}...objectsIds=`,objectsIds);
7618
+
7619
+ // TRACE
7620
+ lognow(`(${self.nodeId}) Sending objects ids to relay node ${relayNodeid}...`);
7621
+
7622
+ const modelObjectsSeekResponse={
7623
+ type:"response.model.seekObject",
7624
+ originatingClientId:originatingClientId,
7625
+ clientBoundaries:clientBoundaries,
7626
+ objectsIds:objectsIds,
7627
+ nodeServerInfo:self.selfOrigin
7628
+ };
7629
+ this.propagateToCluster(modelObjectsSeekResponse, relayNodeid/*destination node*/);
7630
+
7631
+ });
7632
+
7633
+ // Adding propagation listener :
7634
+ this.setPropagation("cluster","response.model.seekObject",(self, message, server, clientSocket)=>{
7635
+
7636
+ // Here the relay server gathers the information from the other nodes, and sends it back to client ;
7637
+
7638
+ const referenceNodeId=self.nodeId;
7639
+ const originatingClientId=message.originatingClientId;
7640
+ const originatingNodeId=message.nodeId;
7641
+ const clientBoundaries=message.clientBoundaries;
7642
+ const objectsIds=message.objectsIds;
7643
+ const nodeServerInfo=message.nodeServerInfo;
7644
+
7645
+ // TRACE
7646
+ lognow(`(${self.nodeId}) Node receiving a seek objects request from relay node ${message.nodeId}...objectsIds=`,objectsIds);
7647
+
7648
+ // Here the currently answering node fills its information :
7649
+ const objectsIndexForClient=self.objectsIndexesForClients[originatingClientId];
7650
+ objectsIndexForClient[originatingNodeId]={nodeServerInfo:nodeServerInfo, objectsIds:objectsIds};
7651
+
7652
+
7653
+ // We check if all known nodes have answered :
7654
+ if(getArraySize(self.authorizedNodesIds)-1/*Because we exclude the reference node*/<=getArraySize(objectsIndexForClient)){
7655
+
7656
+ // The reference node also answers to the request to its direct client :
7657
+ const objectsIdsFromReferenceNode=self.model.getObjectsIdsWithinBoundaries(clientBoundaries);
7658
+ const referenceNodeServerInfo=self.selfOrigin;
7659
+ objectsIndexForClient[referenceNodeId]={nodeServerInfo:referenceNodeServerInfo, objectsIds:objectsIdsFromReferenceNode};
7660
+
7661
+ const client=self.clients[originatingClientId];
7662
+ const clientSocket=client.clientSocket;
7663
+
7664
+ // TRACE
7665
+ lognow(`(${self.nodeId}) Sending objects ids to the client ${originatingClientId}...objectsIndexForClient=`,objectsIndexForClient);
7666
+
7667
+ // We filter all the index entries with an empty objetcs ids array :
7668
+ const objectsIndexForClientWithoutEmptyObjects={};
7669
+ foreach(objectsIndexForClient,(nodeInfoAndObjectsIds, serverNodeId)=>{
7670
+ objectsIndexForClientWithoutEmptyObjects[serverNodeId]=nodeInfoAndObjectsIds;
7671
+ },(nodeInfoAndObjectsIds,serverNodeId)=>(!empty(nodeInfoAndObjectsIds.objectsIds)));
7672
+
7673
+ // We send the final result information (objects ids) to client :
7674
+ const interrogationClientResponse={
7675
+ nodeId:self.nodeId,
7676
+ type:"response.interrogation.client",
7677
+ serversNodesIdsForModelObjectsForClient:objectsIndexForClientWithoutEmptyObjects
7678
+ };
7679
+ self.server.send("protocol", interrogationClientResponse, null, clientSocket);
7680
+ }
7681
+
7682
+ });
7683
+
7684
+
7685
+ // ------------------------------------- CLIENTS INPUTS HANDLING -------------------------------------
7686
+ // Adding propagation listener :
7687
+
7688
+
7689
+
7690
+ // DBG
7691
+ lognow(">>>>>>>>FOR CLIENTS this.listeners",this.listeners);
7692
+
7693
+ }
7694
+
7695
+ // /*private*/getServersNodesIdsForModelObjectsForClient(clientObjectsIds){
7696
+ // const serversNodesIdsForModelObjectsForClient={};
7697
+ // const self=this;
7698
+ // // TODO : FIXME : INEFFICIENT !!!
7699
+ // foreach(clientObjectsIds,(objectId)=>{
7700
+ // foreach(self.modelObjectsDirectory,(objectsInNode, nodeId)=>{
7701
+ // if(contains(objectsInNode,objectId))
7702
+ // serversNodesIdsForModelObjectsForClient[objectId]=nodeId;
7703
+ // });
7704
+ // });
7705
+ //
7706
+ // return serversNodesIdsForModelObjectsForClient;
7707
+ // }
7708
+
7709
+ /*private*/connectToOutcomingServers(self){
7710
+
7711
+ const server=self.server;
7712
+
7713
+ const theoreticNumberOfOutcomingServers=getArraySize(self.outcomingNodesOrigins);
7714
+
7715
+ return new Promise((resolve,error)=>{
7716
+
7717
+ // In case we timeout :
7718
+ setTimeout(()=>{
7719
+ if(self.isConnectedToCluster) return;
7720
+ self.isConnectedToCluster=true;
7721
+ resolve(server);
7722
+ },AORTAC_OUTCOMING_SERVERS_CONNECTION_GLOBAL_TIMEOUT);
7723
+
7350
7724
  // We try to connect to all the other outcoming servers :
7725
+
7726
+ // Special case : if node has no outcoming serves, (it's the seed node), then
7727
+ // it should be considered as connected to cluster nonetheless :
7728
+ if(empty(self.outcomingNodesOrigins)){
7729
+ self.finalizeClusterConnection(self.nodeId);
7730
+ resolve(server);
7731
+ return;
7732
+ }
7733
+
7734
+ // We try to connect to all outcoming nodes :
7351
7735
  foreach(self.outcomingNodesOrigins, outcomingNodeOrigin=>{
7352
- let outcomingNodeHost;
7353
- let outcomingNodePort;
7354
- if(contains(outcomingNodeOrigin,":")){
7355
- const splits=outcomingNodeOrigin.split(":");
7356
- outcomingNodeHost=splits[0];
7357
- outcomingNodePort=splits[1];
7358
- }
7359
- const isSecure=(contains(outcomingNodeOrigin,"wss"));
7360
- const clientInstance=connectToServer(outcomingNodeHost,outcomingNodePort, isSecure, 5000);
7361
7736
 
7362
- //self.outcomingServers[]={clientInstanceToServer:clientInstance};
7737
+ // TRACE
7738
+ lognow(` (${self.nodeId}) Sending registering response to node ${outcomingNodeOrigin}...`);
7739
+
7740
+ const splits=splitURL(outcomingNodeOrigin);
7741
+ const outcomingNodeProtocol=nonull(splits.protocol,"ws");
7742
+ const outcomingNodeHost=nonull(splits.host,"localhost");
7743
+ const outcomingNodePort=nonull(splits.port,"40000");
7744
+ //const isSecure=splits.isSecure; // UNUSED
7745
+
7746
+ const clientInstance=initClient(true, false, (socketToServer)=>{
7747
+
7748
+ // TODO : FIXME : IF CONNECTION TO SERVER FAILS, IT MUST BE ABLE TO TRY AGAIN LATER !
7749
+
7750
+ // TRACE
7751
+ lognow(` (${self.nodeId}) Sending registering request to outcoming node...`);
7752
+
7753
+ const helloRequest={
7754
+ nodeId:self.nodeId,
7755
+ type:"request.register.server.incoming"
7756
+ };
7757
+ socketToServer.send("protocol", helloRequest);
7758
+
7759
+
7760
+ // We place a listener from outcoming node on this client instance to this outcoming node :
7761
+ socketToServer.receive("protocol",(message)=>{
7762
+ if(message.type==="response.register.server.incoming"){
7763
+
7764
+ // TRACE
7765
+ lognow(` (${self.nodeId}) Receving registering response from requested outcoming node...`);
7766
+
7767
+ const welcomeNodeId=message.nodeId;
7768
+ const duplicatesFreeAuthorizedNodesIds=[...new Set([...self.authorizedNodesIds, ...message.authorizedNodesIds, welcomeNodeId ])];
7769
+ self.authorizedNodesIds=duplicatesFreeAuthorizedNodesIds;
7770
+
7771
+ self.outcomingServers[welcomeNodeId]={clientInstance:clientInstance};
7772
+
7773
+ // Once we have registered to all the outcoming nodes :
7774
+ const currentNumberOfOutcomingServers=getArraySize(self.outcomingServers);
7775
+ if(!self.isConnectedToCluster && theoreticNumberOfOutcomingServers<=currentNumberOfOutcomingServers){
7776
+ //self.finalizeClusterConnection(welcomeNodeId);
7777
+ self.finalizeClusterConnection(self.nodeId);
7778
+ resolve(server);
7779
+ }
7780
+ }
7781
+ });
7782
+
7783
+ // Listeners handling ;
7784
+ foreach(Object.keys(self.listeners), channelName=>{
7785
+ socketToServer.receive(channelName, (message, clientSocket)=>{
7786
+
7787
+ // OUTCOMING NODES LISTENERS HOOK :
7788
+ // TRACE
7789
+ console.log("INFO : SERVER : Outcoming node has sent a "+channelName+" message: ", message);
7790
+ foreach(self.listeners[channelName], listener => {
7791
+ listener.execute(self, message, self.server, clientSocket);
7792
+ },(listener)=>(message.type===listener.messageType));
7793
+
7794
+ });
7795
+ });
7796
+
7797
+
7798
+ }, outcomingNodeProtocol+"://"+outcomingNodeHost, outcomingNodePort, false);
7799
+ clientInstance.client.start();
7363
7800
 
7364
7801
  });
7365
7802
 
7803
+ });
7804
+
7805
+ }
7806
+
7807
+ /*private*/finalizeClusterConnection(welcomeNodeId){
7808
+ // TRACE
7809
+ lognow(` (${this.nodeId}) Propagating this new node to the whole cluster...`);
7810
+
7811
+ // We propagate the new node id to the whole cluster :
7812
+ const newNodeRequest={
7813
+ nodeId:welcomeNodeId,
7814
+ type:"request.node.new"
7815
+ };
7816
+ this.propagateToCluster(newNodeRequest);
7817
+
7818
+ this.isConnectedToCluster=true;
7819
+
7820
+ this.addPropagationListeners();
7821
+ this.addPropagationForClientsListeners();
7822
+ }
7823
+
7824
+
7825
+
7826
+
7827
+ /*public*/doOnConnectedToCluster(){
7366
7828
 
7829
+ // TRACE
7830
+ lognow(`(${this.nodeId}) Node is connected to cluster...`);
7831
+
7832
+ // The node asks the cluster for its model partition :
7833
+
7834
+ // At this point, the new node does not know the state of the model in the cluster, only cluster nodes know.
7835
+
7836
+ // TRACE
7837
+ lognow(`(${this.nodeId}) Node is asking to its model partition...`);
7838
+
7839
+ // New node asks for its model partition :
7840
+
7841
+ // - First we need to know which server has the biggest model :
7842
+ const modelSizeRequest={
7843
+ type:"request.model.getSize",
7844
+ };
7845
+ this.propagateToCluster(modelSizeRequest);
7367
7846
 
7847
+ }
7848
+
7849
+
7850
+ /*private*/addPropagationListeners(){
7851
+
7852
+ // Adding propagation listener for acknowledging new node added to cluster :
7853
+ this.setPropagation("cluster","request.node.new", (self, message, server, clientSocket)=>{
7854
+ const newNodeId=message.nodeId;
7855
+ // TRACE
7856
+ lognow(` (${self.nodeId}) Acknowledging new node ${newNodeId} added to cluster...`);
7857
+ if(!contains(self.authorizedNodesIds,newNodeId)) self.authorizedNodesIds.push(newNodeId);
7858
+ });
7859
+
7860
+ // Adding propagation listener if a node receives a model size request :
7861
+ this.setPropagation("cluster","request.model.getSize",(self, message, server, clientSocket)=>{
7862
+ const modelSize=self.model.getModelSize();
7863
+ // TRACE
7864
+ lognow(`(${self.nodeId}) Node gives its model size to node ${message.nodeId}, modelSize=${modelSize}...`);
7865
+ return modelSize;
7866
+ },"response.model.getSize");
7867
+
7868
+ // Adding propagation listener :
7869
+ this.modelSizes={};
7870
+ this.setPropagation("cluster","response.model.getSize",(self, message, server, clientSocket)=>{
7368
7871
 
7369
- ////
7370
- // CURRENT AORTAC
7872
+ const modelSizes=self.modelSizes;
7873
+ const currentClusterSize=getArraySize(self.authorizedNodesIds);
7371
7874
 
7875
+ const concernedNodeId=message.nodeId;
7876
+ const modelSize=message.result;
7372
7877
 
7373
- },this.sslConfig.port, this.sslConfig.certPath, this.sslConfig.keyPath);
7878
+ let currentNumberOfAnswers=getArraySize(modelSizes);
7879
+
7880
+ if(currentNumberOfAnswers<=currentClusterSize-1){// -1 because we want to exclude this node also !
7881
+ modelSizes[concernedNodeId]=modelSize;
7882
+ }
7883
+
7884
+ currentNumberOfAnswers=getArraySize(modelSizes);
7885
+ // TRACE
7886
+ lognow(`(${self.nodeId}) Node receiving a model size from node ${message.nodeId}...currentClusterSize=${currentClusterSize} ; modelSizes:`,modelSizes);
7887
+ if(currentClusterSize-1<=currentNumberOfAnswers){
7888
+ const nodeIdWithBiggestModel=Math.maxInArray(modelSizes, true);
7889
+ // TRACE
7890
+ lognow(`(${self.nodeId}) Node with biggest model is nodeIdWithBiggestModel=${nodeIdWithBiggestModel}...`);
7891
+
7892
+ const modelPartitionRequest={
7893
+ type:"request.model.partition",
7894
+ };
7895
+ this.propagateToCluster(modelPartitionRequest, nodeIdWithBiggestModel);
7896
+ }
7897
+ });
7374
7898
 
7899
+ // Adding propagation listener :
7900
+ // If a node receives a model partition request :
7901
+ this.setPropagation("cluster","request.model.partition",(self, message, server, clientSocket)=>{
7902
+ // Each node will give a little portion of its model, according to its size :
7903
+ // TRACE
7904
+ lognow(`(${self.nodeId}) Node splits its model for node ${message.nodeId}...`);
7905
+
7906
+ const ownModel=self.model;
7907
+ const splittedModel=ownModel.split();
7908
+
7909
+ // Node shares also its model objects directory with the newcomer node :
7910
+ const ownModelObjectsIds=ownModel.getAllObjectsIds();
7911
+ self.modelObjectsDirectory[self.nodeId]=ownModelObjectsIds;
7912
+
7913
+ const modelString=JSON.stringifyDecycle(splittedModel);
7914
+ const modelPartitionResponse={
7915
+ type:"response.model.partition",
7916
+ modelString:modelString,
7917
+ modelObjectsDirectory:self.modelObjectsDirectory
7918
+ };
7919
+ this.propagateToCluster(modelPartitionResponse, message.nodeId);
7920
+
7921
+ // This node also advertises that its model has changed to all other nodes (not the newcomer, because it's unnecessary) :
7922
+ const updatemodelObjectsDirectoryRequest={
7923
+ type:"request.update.modelObjectsDirectory",
7924
+ ownModelObjectsIds:ownModelObjectsIds
7925
+ };
7926
+ this.propagateToCluster(updatemodelObjectsDirectoryRequest, null, [message.nodeId]);
7927
+
7928
+ });
7929
+
7930
+ // Adding propagation listener :
7931
+ this.setPropagation("cluster","response.model.partition",(self, message, server, clientSocket)=>{
7932
+ // TRACE
7933
+ lognow(`(${self.nodeId}) Node receives a model partition from node ${message.nodeId}...`,message);
7934
+
7935
+ const newModel=JSON.parseRecycle(message.modelString);
7936
+ self.model.replaceBy(newModel);
7375
7937
 
7938
+ // We initialize this node's model objects directory from the one froom the node tha provided it its model partition :
7939
+ self.modelObjectsDirectory=message.modelObjectsDirectory;
7940
+
7941
+ // Once this node has received its model partition, it updates its model objects directory
7942
+ const ownModelObjectsIds=newModel.getAllObjectsIds();
7943
+ self.modelObjectsDirectory[self.nodeId]=ownModelObjectsIds;
7944
+
7945
+ // TRACE
7946
+ lognow(`(${self.nodeId}) Node sends a model objects directory update request...ownModelObjectsIds=${ownModelObjectsIds}`);
7947
+
7948
+ // And notifies the other nodes of the change :
7949
+ const updatemodelObjectsDirectoryRequest={
7950
+ type:"request.update.modelObjectsDirectory",
7951
+ ownModelObjectsIds:ownModelObjectsIds
7952
+ };
7953
+ this.propagateToCluster(updatemodelObjectsDirectoryRequest);
7954
+
7955
+ });
7956
+
7957
+ this.addModelObjectsDirectoryListeners();
7958
+
7959
+
7960
+ // DBG
7961
+ lognow(">>>>>>>>FOR OTHER NODES this.listeners",this.listeners);
7962
+
7376
7963
  }
7377
7964
 
7965
+
7966
+
7967
+ /*private*/addModelObjectsDirectoryListeners(){
7968
+
7969
+ const self=this;
7970
+
7971
+ // Adding propagation listener :
7972
+ // If a node receives a model objects directory update request :
7973
+ this.setPropagation("cluster","request.update.modelObjectsDirectory",(self, message, server, clientSocket)=>{
7974
+
7975
+ // TRACE
7976
+ lognow(`(${self.nodeId}) !!!!!! Node receives a model objects directory update request from node ${message.nodeId}...`,message);
7977
+
7978
+ const modelObjectsIds=message.ownModelObjectsIds;
7979
+ self.modelObjectsDirectory[message.nodeId]=modelObjectsIds;
7980
+
7981
+ });
7982
+
7983
+
7984
+ // Adding propagation listener :
7985
+ // If a node receives a model objects directory update request :
7986
+ this.setPropagation("cluster","request.add.modelObjectsDirectory",(self, message, server, clientSocket)=>{
7987
+
7988
+ // TRACE
7989
+ lognow(`(${self.nodeId}) !!!!!! Node receives a model objects directory add request from node ${message.nodeId}...`,message);
7990
+
7991
+ const newObjectsIds=message.newObjectsIds;
7992
+ self.modelObjectsDirectory[message.nodeId].push(...newObjectsIds);
7993
+
7994
+ });
7995
+
7996
+ }
7997
+
7998
+
7999
+ /*public*/sendUpdatedObjects(modifiedObjects, modifiedObjectsIds){
8000
+
8001
+ if(empty(modifiedObjects)) return modifiedObjects;
8002
+
8003
+ // Then we send back the modified objects to all the concerned clients : (which are all clients connected to this node)
8004
+ return this.sendObjectsUpdatesToClients(modifiedObjects);
8005
+ }
8006
+
8007
+ /*public*/sendNewObjects(newObjects, newObjectsIds){
8008
+
8009
+ if(empty(newObjects)) return newObjects;
8010
+
8011
+ // At this point, node controller has already added the objects to its model.
8012
+ // We just want to update the objects directories of all the nodes in the cluster:
8013
+
8014
+ // !!! CAUTION : NOTE THAT THE EMITTER NODE IS ALWAYS INCLUDED IN THE THE VISITED NODES !!!
8015
+ // So this is why ir needs to update its mode objects directory itself, before sending the add request to the cluster :
8016
+ this.modelObjectsDirectory[this.nodeId].push(...newObjectsIds);
8017
+
8018
+ // This node also advertises that its model has changed to all other nodes (not the newcomer, because it's unnecessary) :
8019
+ const updatemodelObjectsDirectoryRequest={
8020
+ type:"request.add.modelObjectsDirectory",
8021
+ newObjectsIds:newObjectsIds
8022
+ };
8023
+ this.propagateToCluster(updatemodelObjectsDirectoryRequest);
8024
+
8025
+
8026
+ // But we also want all the concerned clients to update their local models : (which are all clients connected to this node)
8027
+ return this.sendObjectsUpdatesToClients(newObjects, true);
8028
+ }
8029
+
8030
+ /*private*/sendObjectsUpdatesToClients(modifiedOrAddedObjects, isAddingObjects=false){
8031
+
8032
+ const self=this;
8033
+ foreach(this.clients, client=>{
8034
+ const clientSocket=client.clientSocket;
8035
+ const objectsModifiedOrAddedClientResponse={
8036
+ nodeId:self.nodeId,
8037
+ type:"response.objectsModifiedOrAdded.client",
8038
+ objectsString:JSON.stringifyDecycle(modifiedOrAddedObjects),
8039
+ isAddingObjects:isAddingObjects,
8040
+ };
8041
+ self.server.send("inputs", objectsModifiedOrAddedClientResponse, null, clientSocket);
8042
+ });
8043
+
8044
+ return modifiedOrAddedObjects;
8045
+ }
8046
+
8047
+
8048
+
8049
+
8050
+ /*private*/getNeighborsNodesIds(){
8051
+ const neighborsNodesIds=[];
8052
+ foreach(this.incomingServers,(incomingServer,nodeId)=>{
8053
+ neighborsNodesIds.push(nodeId);
8054
+ });
8055
+ foreach(this.outcomingServers,(outcomingServer,nodeId)=>{
8056
+ neighborsNodesIds.push(nodeId);
8057
+ });
8058
+ return neighborsNodesIds;
8059
+ }
8060
+
8061
+ /*private*/getLeastOccupiedNeighborNodeId(){
8062
+ const self=this;
8063
+
8064
+ // We send the new objects to the neighbor node with the least amount of objects :
8065
+ let leastOccupiedNodeId=null;
8066
+ foreach(this.getNeighborsNodesIds(), (nodeId)=>{
8067
+ const entry=self.modelObjectsDirectory[nodeId];
8068
+ const numberOfObjects=getArraySize(entry);
8069
+ if(!leastOccupiedNodeId || numberOfObjects<getArraySize(self.modelObjectsDirectory[leastOccupiedNodeId]))
8070
+ leastOccupiedNodeId=nodeId;
8071
+ });
8072
+
8073
+ if(!leastOccupiedNodeId){
8074
+ // TRACE
8075
+ lognow("WARN : Cannot find the least occupied node. Aborting.");
8076
+ }
8077
+ return leastOccupiedNodeId;
8078
+ }
8079
+
8080
+
8081
+ // ****************************************************************************************************
8082
+ /*private*/setPropagation(channelName, requestType, doOnReception=null, responseRequestType=null, propagateToAllCluster=true){
8083
+ const self=this;
8084
+ this.setUniqueReceptionPoint(channelName, requestType, (self, message, server, clientSocket)=>{
8085
+
8086
+ const requestId=message.requestId;
8087
+ if(contains(self.executedRequestIdsHistory, requestId)){
8088
+ // TRACE
8089
+ lognow(`WARN : Request of type ${message.type} already answered. Aborting.`);
8090
+ return;
8091
+ }
8092
+ if(message.visitedNodeIds){
8093
+ if(contains(message.visitedNodeIds, self.nodeId)){
8094
+ return;
8095
+ }
8096
+ message.visitedNodeIds.push(self.nodeId);
8097
+ }
8098
+
8099
+ const hasNoExclusionsOrExclusionDoesNotApplyOnThisNode=(!message.excludedNodesIds || !contains(message.excludedNodesIds,self.nodeId));
8100
+ let response=null;
8101
+ if(!message.destinationNodeId && hasNoExclusionsOrExclusionDoesNotApplyOnThisNode){
8102
+ // Case broadcast message :
8103
+ if(doOnReception)
8104
+ response=doOnReception(self, message, server, clientSocket);
8105
+ pushInArrayAsQueue(self.executedRequestIdsHistory, REQUESTS_IDS_HISTORY_SIZE, requestId);
8106
+ if(propagateToAllCluster) self.sendToOtherNodes(channelName, message);
8107
+ }else{
8108
+ if(message.destinationNodeId===self.nodeId && hasNoExclusionsOrExclusionDoesNotApplyOnThisNode){
8109
+ if(doOnReception)
8110
+ response=doOnReception(self, message, server, clientSocket);
8111
+ pushInArrayAsQueue(self.executedRequestIdsHistory, REQUESTS_IDS_HISTORY_SIZE, requestId);
8112
+ }else{
8113
+ if(propagateToAllCluster) self.sendToOtherNodes(channelName, message);
8114
+ }
8115
+ }
8116
+
8117
+ // We send back the answer to the originating node :
8118
+ if(response!=null && responseRequestType){
8119
+ // TRACE
8120
+ lognow(`(${self.nodeId}) Node sending back a response of type ${responseRequestType}...`);
8121
+
8122
+ const clusterResponse={
8123
+ type:responseRequestType,
8124
+ result:response
8125
+ };
8126
+ const originNodeId=message.nodeId;
8127
+ if(propagateToAllCluster) self.propagateToCluster(clusterResponse, originNodeId);
8128
+ else self.propagateToSingleNeighbor(clusterResponse, originNodeId);
8129
+ }
8130
+
8131
+ });
8132
+ }
8133
+ // ****************************************************************************************************
8134
+
8135
+ /*private*/propagateToCluster(request, destinationNodeId=null, excludedNodesIds=null){
8136
+ request.requestId=getUUID();
8137
+ const originNodeId=this.nodeId;
8138
+ request.nodeId=originNodeId;
8139
+ if(destinationNodeId) request.destinationNodeId=destinationNodeId;
8140
+ if(excludedNodesIds) request.excludedNodesIds=excludedNodesIds;
8141
+ request.visitedNodeIds=[originNodeId];
8142
+ this.sendToOtherNodes("cluster",request);
8143
+ }
8144
+
8145
+ /*private*/propagateToNeighbors(request){
8146
+ request.requestId=getUUID();
8147
+ const originNodeId=this.nodeId;
8148
+ request.nodeId=originNodeId;
8149
+ this.sendToOtherNodes("neightbors", request);
8150
+ }
8151
+ /*private*/propagateToSingleNeighbor(request, destinationNodeId){
8152
+ request.requestId=getUUID();
8153
+ const originNodeId=this.nodeId;
8154
+ request.nodeId=originNodeId;
8155
+ request.destinationNodeId=destinationNodeId;
8156
+ this.sendToOtherSingleNode("neightbors", request, destinationNodeId);
8157
+ }
8158
+
8159
+ // ****************************************************************************************************
8160
+
8161
+ /*private*/sendToOtherNodes(channelName, message){
8162
+ let hasBeenSentIncoming=this.sendToIncomingServers(channelName, message);
8163
+ let hasBeenSentOutcoming=this.sendToOutcomingServers(channelName, message);
8164
+ return (hasBeenSentIncoming || hasBeenSentOutcoming);
8165
+ }
8166
+ /*private*/sendToIncomingServers(channelName, message){
8167
+ const self=this;
8168
+ let hasBeenSent=false;
8169
+ foreach(this.incomingServers, (client)=>{
8170
+ const clientSocket=client.clientSocket;
8171
+ self.server.send(channelName, message, null, clientSocket);
8172
+ hasBeenSent=true;
8173
+ });
8174
+ return hasBeenSent;
8175
+ }
8176
+ /*private*/sendToOutcomingServers(channelName, message){
8177
+ let hasBeenSent=false;
8178
+ foreach(this.outcomingServers, (client)=>{
8179
+ const clientInstanceToOtherServer=client.clientInstance;
8180
+ clientInstanceToOtherServer.client.socketToServer.send(channelName, message);
8181
+ hasBeenSent=true;
8182
+ });
8183
+ return hasBeenSent;
8184
+ }
8185
+ /*private*/sendToOtherSingleNode(channelName, request, destinationNodeId){
8186
+ const self=this;
8187
+ let hasBeenSent=false;
8188
+ foreach(this.incomingServers, (client)=>{
8189
+ const clientSocket=client.clientSocket;
8190
+ self.server.send(channelName, message, null, clientSocket);
8191
+ hasBeenSent=true;
8192
+ },(c, nodeId)=>(nodeId===destinationNodeId));
8193
+ if(!hasBeenSent){
8194
+ foreach(this.outcomingServers, (client)=>{
8195
+ const clientInstanceToOtherServer=client.clientInstance;
8196
+ clientInstanceToOtherServer.client.socketToServer.send(channelName, message);
8197
+ hasBeenSent=true;
8198
+ },(c, nodeId)=>(nodeId===destinationNodeId));
8199
+ }
8200
+ return hasBeenSent;
8201
+ }
8202
+
8203
+ /*private*/setUniqueReceptionPoint(channelName, messageType, doOnReception){
8204
+ const self=this;
8205
+ const listeners=this.listeners[channelName];
8206
+ if(foreach(listeners,listener=>{
8207
+ if(listener.messageType===messageType) return true;
8208
+ })) return;
8209
+ listeners.push({
8210
+ messageType:messageType,
8211
+ execute:(self, message, server, clientSocket)=>{
8212
+ doOnReception(self, message, server, clientSocket);
8213
+ }
8214
+ });
8215
+ }
8216
+
8217
+ // ****************************************************************************************************
8218
+
8219
+ /*private*/isInAuthorizedNodes(incomingServerNodeId){
8220
+ return contains(this.authorizedNodesIds,incomingServerNodeId);
8221
+ }
8222
+
8223
+ /*public*/traceNode(){
8224
+ // TRACE
8225
+ lognow("-------------------------------------------------------------");
8226
+ lognow(`(${this.nodeId}) :`,this);
8227
+ lognow("-------------------------------------------------------------");
8228
+ }
8229
+
8230
+
8231
+
7378
8232
  }
7379
8233
 
7380
- getAORTACNode=function(selfOrigin="127.0.0.1:40000",outcomingNodesOrigins=[]){
7381
- return new AORTACNode(selfOrigin,outcomingNodesOrigins);
8234
+
8235
+ getAORTACNode=function(nodeId=getUUID(), selfOrigin="ws://127.0.0.1:40000", outcomingNodesOrigins=[], model, controller){
8236
+ return new AORTACNode(nodeId, selfOrigin, outcomingNodesOrigins, model, controller);
7382
8237
  }
7383
8238
 
7384
8239
 
@@ -7388,6 +8243,7 @@ getAORTACNode=function(selfOrigin="127.0.0.1:40000",outcomingNodesOrigins=[]){
7388
8243
 
7389
8244
 
7390
8245
 
8246
+
7391
8247
  /* INCLUDED EXTERNAL LIBRAIRIES
7392
8248
  */
7393
8249
  "use strict";var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a},notReady:function(a){this.toString=function(){return"NOT READY: "+this.message};this.message=a}}};
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aotrautils-srv",
3
- "version": "0.0.1057",
3
+ "version": "0.0.1059",
4
4
  "main": "aotrautils-srv.build.js",
5
5
  "description": "A library for vanilla javascript utils (server-side) used in aotra javascript CMS",
6
6
  "author": "Jeremie Ratomposon <info@alqemia.com> (https://alqemia.com)",