1/* 2 * Copyright (c) 2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16class DistributedStorage implements IMultiPropertiesChangeSubscriber { 17 private storage_: IStorage; 18 19 private id_: number; 20 private links_: Map<string, SubscribedAbstractProperty<any>>; 21 private aviliable_: boolean; 22 private notifier_: (status: string) => void; 23 24 public constructor(sessionId: string, notifier: (status: string) => void) { 25 this.links_ = new Map<string, ObservedPropertyAbstract<any>>(); 26 this.id_ = SubscriberManager.MakeId(); 27 SubscriberManager.Add(this); 28 this.aviliable_ = false; 29 this.notifier_ = notifier; 30 } 31 32 public keys(): Array<string> { 33 let result = []; 34 const it = this.links_.keys(); 35 let val = it.next(); 36 37 while (!val.done) { 38 result.push(val.value); 39 val = it.next(); 40 } 41 42 return result; 43 } 44 45 46 public distributeProp<T>(propName: string, defaultValue: T): void { 47 if (this.link(propName, defaultValue)) { 48 stateMgmtConsole.debug(`DistributedStorage: writing '${propName}' - '${this.links_.get(propName)}' to storage`); 49 } 50 } 51 52 public distributeProps(properties: { 53 key: string, 54 defaultValue: any 55 }[]): void { 56 properties.forEach(property => this.link(property.key, property.defaultValue)); 57 } 58 59 public link<T>(propName: string, defaultValue: T): boolean { 60 if (defaultValue == null || defaultValue == undefined) { 61 stateMgmtConsole.error(`DistributedStorage: linkProp for ${propName} called with 'null' or 'undefined' default value!`); 62 return false; 63 } 64 if (this.links_.get(propName)) { 65 stateMgmtConsole.warn(`DistributedStorage: linkProp: ${propName} is already exist`); 66 return false; 67 } 68 let link = AppStorage.Link(propName, this); 69 if (link) { 70 stateMgmtConsole.debug(`DistributedStorage: linkProp ${propName} in AppStorage, using that`); 71 this.links_.set(propName, link); 72 this.setDistributedProp(propName, defaultValue); 73 } 74 else { 75 let returnValue = defaultValue; 76 if (this.aviliable_) { 77 let newValue = this.getDistributedProp(propName); 78 if (newValue == null) { 79 stateMgmtConsole.debug(`DistributedStorage: no entry for ${propName}, will initialize with default value`); 80 this.setDistributedProp(propName, defaultValue); 81 } 82 else { 83 returnValue = newValue; 84 } 85 } 86 link = AppStorage.SetAndLink(propName, returnValue, this); 87 this.links_.set(propName, link); 88 stateMgmtConsole.debug(`DistributedStorage: created new linkProp prop for ${propName}`); 89 } 90 return true; 91 } 92 93 public deleteProp(propName: string): void { 94 let link = this.links_.get(propName); 95 if (link) { 96 link.aboutToBeDeleted(); 97 this.links_.delete(propName); 98 if (this.aviliable_) { 99 this.storage_.delete(propName); 100 } 101 } else { 102 stateMgmtConsole.warn(`DistributedStorage: '${propName}' is not a distributed property warning.`); 103 } 104 } 105 106 private write(key: string): void { 107 let link = this.links_.get(key); 108 if (link) { 109 this.setDistributedProp(key, link.get()); 110 } 111 } 112 113 114 // public required by the interface, use the static method instead! 115 public aboutToBeDeleted(): void { 116 stateMgmtConsole.debug("DistributedStorage: about to be deleted"); 117 this.links_.forEach((val, key, map) => { 118 stateMgmtConsole.debug(`DistributedStorage: removing ${key}`); 119 val.aboutToBeDeleted(); 120 }); 121 122 this.links_.clear(); 123 SubscriberManager.Delete(this.id__()); 124 } 125 126 public id__(): number { 127 return this.id_; 128 } 129 130 public propertyHasChanged(info?: PropertyInfo): void { 131 stateMgmtConsole.debug("DistributedStorage: property changed"); 132 this.write(info); 133 } 134 135 public onDataOnChange(propName: string): void { 136 let link = this.links_.get(propName); 137 let newValue = this.getDistributedProp(propName); 138 if (link && newValue != null) { 139 stateMgmtConsole.info(`DistributedStorage: dataOnChange[${propName}-${newValue}]`); 140 link.set(newValue) 141 } 142 } 143 144 public onConnected(status): void { 145 stateMgmtConsole.info(`DistributedStorage onConnected: status = ${status}`); 146 if (!this.aviliable_) { 147 this.syncProp(); 148 this.aviliable_ = true; 149 } 150 if (this.notifier_ != null) { 151 this.notifier_(status); 152 } 153 } 154 155 private syncProp(): void { 156 this.links_.forEach((val, key) => { 157 let newValue = this.getDistributedProp(key); 158 if (newValue == null) { 159 this.setDistributedProp(key, val.get()); 160 } else { 161 val.set(newValue); 162 } 163 }); 164 } 165 166 private setDistributedProp(key, value): void { 167 if (!this.aviliable_) { 168 stateMgmtConsole.warn(`DistributedStorage is not aviliable`); 169 return; 170 } 171 stateMgmtConsole.error(`DistributedStorage value is object ${key}-${JSON.stringify(value)}`); 172 if (typeof value == 'object') { 173 this.storage_.set(key, JSON.stringify(value)); 174 return; 175 } 176 this.storage_.set(key, value); 177 } 178 179 private getDistributedProp(key): any { 180 let value = this.storage_.get(key); 181 if (typeof value == 'string') { 182 try { 183 let returnValue = JSON.parse(value); 184 return returnValue; 185 } 186 finally { 187 return value; 188 } 189 } 190 return value; 191 } 192}; 193