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
16 #include "js_watcher.h"
17
18 #include <cstring>
19
20 #include "js_common.h"
21 #include "js_util.h"
22 #include "logger.h"
23 #include "notifier_impl.h"
24 #include "objectstore_errors.h"
25
26 namespace OHOS::ObjectStore {
JSWatcher(const napi_env env,DistributedObjectStore * objectStore,DistributedObject * object)27 JSWatcher::JSWatcher(const napi_env env, DistributedObjectStore *objectStore, DistributedObject *object)
28 : UvQueue(env), env_(env)
29 {
30 changeEventListener_ = new ChangeEventListener(this, objectStore, object);
31 statusEventListener_ = new StatusEventListener(this, object->GetSessionId());
32 }
33
~JSWatcher()34 JSWatcher::~JSWatcher()
35 {
36 delete changeEventListener_;
37 delete statusEventListener_;
38 changeEventListener_ = nullptr;
39 statusEventListener_ = nullptr;
40 }
41
On(const char * type,napi_value handler)42 bool JSWatcher::On(const char *type, napi_value handler)
43 {
44 EventListener *listener = Find(type);
45 if (listener == nullptr) {
46 LOG_ERROR("error type %{public}s", type);
47 return false;
48 }
49 return listener->Add(env_, handler);
50 }
51
Off(const char * type,napi_value handler)52 void JSWatcher::Off(const char *type, napi_value handler)
53 {
54 EventListener *listener = Find(type);
55 if (listener == nullptr) {
56 LOG_ERROR("error type %{public}s", type);
57 return;
58 }
59 if (handler == nullptr) {
60 listener->Clear(env_);
61 } else {
62 listener->Del(env_, handler);
63 }
64 }
ProcessChange(napi_env env,std::list<void * > & args)65 void JSWatcher::ProcessChange(napi_env env, std::list<void *> &args)
66 {
67 constexpr static int8_t ARGV_SIZE = 2;
68 napi_value callback = nullptr;
69 napi_value global = nullptr;
70 napi_value param[ARGV_SIZE];
71 napi_value result;
72 napi_status status = napi_get_global(env, &global);
73 ASSERT_MATCH_ELSE_GOTO_ERROR(status == napi_ok);
74 for (auto item : args) {
75 ChangeArgs *changeArgs = static_cast<ChangeArgs *>(item);
76 status = napi_get_reference_value(env, changeArgs->callback_, &callback);
77 ASSERT_MATCH_ELSE_GOTO_ERROR(status == napi_ok);
78 status = JSUtil::SetValue(env, changeArgs->sessionId_, param[0]);
79 ASSERT_MATCH_ELSE_GOTO_ERROR(status == napi_ok);
80 JSUtil::SetValue(env, changeArgs->changeData_, param[1]);
81 ASSERT_MATCH_ELSE_GOTO_ERROR(status == napi_ok);
82 LOG_INFO("start %{public}s, %{public}zu", changeArgs->sessionId_.c_str(), changeArgs->changeData_.size());
83 status = napi_call_function(env, global, callback, ARGV_SIZE, param, &result);
84 LOG_INFO("end %{public}s, %{public}zu", changeArgs->sessionId_.c_str(), changeArgs->changeData_.size());
85 ASSERT_MATCH_ELSE_GOTO_ERROR(status == napi_ok);
86 }
87 ERROR:
88 for (auto item : args) {
89 ChangeArgs *changeArgs = static_cast<ChangeArgs *>(item);
90 delete changeArgs;
91 }
92 args.clear();
93 }
Emit(const char * type,const std::string & sessionId,const std::vector<std::string> & changeData)94 void JSWatcher::Emit(const char *type, const std::string &sessionId, const std::vector<std::string> &changeData)
95 {
96 if (changeData.empty()) {
97 LOG_ERROR("empty change");
98 return;
99 }
100 LOG_INFO("start %{public}s, %{public}s", sessionId.c_str(), changeData.at(0).c_str());
101 EventListener *listener = Find(type);
102 if (listener == nullptr) {
103 LOG_ERROR("error type %{public}s", type);
104 return;
105 }
106
107 for (EventHandler *handler = listener->handlers_; handler != nullptr; handler = handler->next) {
108 ChangeArgs *changeArgs = new (std::nothrow) ChangeArgs(handler->callbackRef, sessionId, changeData);
109 if (changeArgs == nullptr) {
110 LOG_ERROR("JSWatcher::Emit no memory for changeArgs malloc!");
111 return;
112 }
113 CallFunction(ProcessChange, changeArgs);
114 }
115 }
116
Find(const char * type)117 EventListener *JSWatcher::Find(const char *type)
118 {
119 if (!strcmp(CHANGE, type)) {
120 return changeEventListener_;
121 }
122 if (!strcmp(STATUS, type)) {
123 return statusEventListener_;
124 }
125 return nullptr;
126 }
127
ProcessStatus(napi_env env,std::list<void * > & args)128 void JSWatcher::ProcessStatus(napi_env env, std::list<void *> &args)
129 {
130 constexpr static int8_t ARGV_SIZE = 3;
131 napi_value callback = nullptr;
132 napi_value global = nullptr;
133 napi_value param[ARGV_SIZE];
134 napi_value result;
135 napi_status status = napi_get_global(env, &global);
136 ASSERT_MATCH_ELSE_GOTO_ERROR(status == napi_ok);
137 for (auto item : args) {
138 StatusArgs *statusArgs = static_cast<StatusArgs *>(item);
139 status = napi_get_reference_value(env, statusArgs->callback_, &callback);
140 ASSERT_MATCH_ELSE_GOTO_ERROR(status == napi_ok);
141 status = JSUtil::SetValue(env, statusArgs->sessionId_, param[0]);
142 ASSERT_MATCH_ELSE_GOTO_ERROR(status == napi_ok);
143 status = JSUtil::SetValue(env, statusArgs->networkId_, param[1]);
144 ASSERT_MATCH_ELSE_GOTO_ERROR(status == napi_ok);
145 status = JSUtil::SetValue(env, statusArgs->status_, param[2]);
146 ASSERT_MATCH_ELSE_GOTO_ERROR(status == napi_ok);
147 LOG_INFO("start %{public}s, %{public}s, %{public}s", statusArgs->sessionId_.c_str(),
148 statusArgs->networkId_.c_str(), statusArgs->status_.c_str());
149 status = napi_call_function(env, global, callback, ARGV_SIZE, param, &result);
150 LOG_INFO("end %{public}s, %{public}s, %{public}s", statusArgs->sessionId_.c_str(),
151 statusArgs->networkId_.c_str(), statusArgs->status_.c_str());
152 ASSERT_MATCH_ELSE_GOTO_ERROR(status == napi_ok);
153 }
154 ERROR:
155 LOG_DEBUG("do clear");
156 for (auto item : args) {
157 StatusArgs *statusArgs = static_cast<StatusArgs *>(item);
158 delete statusArgs;
159 }
160 args.clear();
161 }
162
Emit(const char * type,const std::string & sessionId,const std::string & networkId,const std::string & status)163 void JSWatcher::Emit(
164 const char *type, const std::string &sessionId, const std::string &networkId, const std::string &status)
165 {
166 if (sessionId.empty() || networkId.empty()) {
167 LOG_ERROR("empty %{public}s %{public}s", sessionId.c_str(), networkId.c_str());
168 return;
169 }
170 LOG_ERROR("status change %{public}s %{public}s", sessionId.c_str(), networkId.c_str());
171 EventListener *listener = Find(type);
172 if (listener == nullptr) {
173 LOG_ERROR("error type %{public}s", type);
174 return;
175 }
176
177 for (EventHandler *handler = listener->handlers_; handler != nullptr; handler = handler->next) {
178 StatusArgs *changeArgs = new (std::nothrow) StatusArgs(handler->callbackRef, sessionId, networkId, status);
179 if (changeArgs == nullptr) {
180 LOG_ERROR("JSWatcher::Emit no memory for StatusArgs malloc!");
181 return;
182 }
183 CallFunction(ProcessStatus, changeArgs);
184 }
185 return;
186 }
187
IsEmpty()188 bool JSWatcher::IsEmpty()
189 {
190 if (changeEventListener_->IsEmpty() && statusEventListener_->IsEmpty()) {
191 return true;
192 }
193 return false;
194 }
195
Find(napi_env env,napi_value handler)196 EventHandler *EventListener::Find(napi_env env, napi_value handler)
197 {
198 EventHandler *result = nullptr;
199 for (EventHandler *i = handlers_; i != nullptr; i = i->next) {
200 napi_value callback = nullptr;
201 napi_get_reference_value(env, i->callbackRef, &callback);
202 bool isEquals = false;
203 napi_strict_equals(env, handler, callback, &isEquals);
204 if (isEquals) {
205 result = i;
206 }
207 }
208 return result;
209 }
210
Clear(napi_env env)211 void EventListener::Clear(napi_env env)
212 {
213 for (EventHandler *i = handlers_; i != nullptr; i = handlers_) {
214 handlers_ = i->next;
215 napi_delete_reference(env, i->callbackRef);
216 delete i;
217 }
218 }
219
Del(napi_env env,napi_value handler)220 bool EventListener::Del(napi_env env, napi_value handler)
221 {
222 EventHandler *temp = nullptr;
223 napi_status status;
224 for (EventHandler *i = handlers_; i != nullptr;) {
225 napi_value callback = nullptr;
226 status = napi_get_reference_value(env, i->callbackRef, &callback);
227 if (status != napi_ok) {
228 LOG_ERROR("error! %{public}d", status);
229 return false;
230 }
231 bool isEquals = false;
232 napi_strict_equals(env, handler, callback, &isEquals);
233 if (isEquals) {
234 EventHandler *delData = i;
235 i = i->next;
236 if (temp == nullptr) {
237 handlers_ = delData->next;
238 } else {
239 temp->next = delData->next;
240 }
241 napi_delete_reference(env, delData->callbackRef);
242 delete delData;
243 } else {
244 temp = i;
245 i = i->next;
246 }
247 }
248 return handlers_ == nullptr;
249 }
250
Add(napi_env env,napi_value handler)251 bool EventListener::Add(napi_env env, napi_value handler)
252 {
253 if (Find(env, handler) != nullptr) {
254 LOG_ERROR("has added,return");
255 return false;
256 }
257
258 if (handlers_ == nullptr) {
259 handlers_ = new (std::nothrow) EventHandler();
260 if (handlers_ == nullptr) {
261 LOG_ERROR("EventListener::Add no memory for EventHandler malloc!");
262 return false;
263 }
264 handlers_->next = nullptr;
265 } else {
266 auto temp = new (std::nothrow) EventHandler();
267 if (temp == nullptr) {
268 LOG_ERROR("EventListener::Add no memory for EventHandler malloc!");
269 return false;
270 }
271 temp->next = handlers_;
272 handlers_ = temp;
273 }
274 napi_create_reference(env, handler, 1, &handlers_->callbackRef);
275 return true;
276 }
277
OnChanged(const std::string & sessionid,const std::vector<std::string> & changedData)278 void WatcherImpl::OnChanged(const std::string &sessionid, const std::vector<std::string> &changedData)
279 {
280 if (watcher_ == nullptr) {
281 LOG_ERROR("watcher_ is null");
282 return;
283 }
284 watcher_->Emit(CHANGE, sessionid, changedData);
285 }
286
~WatcherImpl()287 WatcherImpl::~WatcherImpl()
288 {
289 LOG_ERROR("destroy");
290 watcher_ = nullptr;
291 }
292
Add(napi_env env,napi_value handler)293 bool ChangeEventListener::Add(napi_env env, napi_value handler)
294 {
295 if (!isWatched_ && object_ != nullptr) {
296 std::shared_ptr<WatcherImpl> watcher = std::make_shared<WatcherImpl>(watcher_);
297 if (watcher == nullptr) {
298 LOG_ERROR("new %{public}s error", object_->GetSessionId().c_str());
299 return false;
300 }
301 uint32_t ret = objectStore_->Watch(object_, watcher);
302 if (ret != SUCCESS) {
303 LOG_ERROR("Watch %{public}s error", object_->GetSessionId().c_str());
304 } else {
305 LOG_INFO("Watch %{public}s success", object_->GetSessionId().c_str());
306 isWatched_ = true;
307 }
308 }
309 return EventListener::Add(env, handler);
310 }
311
Del(napi_env env,napi_value handler)312 bool ChangeEventListener::Del(napi_env env, napi_value handler)
313 {
314 bool isEmpty = EventListener::Del(env, handler);
315 if (isEmpty && isWatched_ && object_ != nullptr) {
316 uint32_t ret = objectStore_->UnWatch(object_);
317 if (ret != SUCCESS) {
318 LOG_ERROR("UnWatch %{public}s error", object_->GetSessionId().c_str());
319 } else {
320 LOG_INFO("UnWatch %{public}s success", object_->GetSessionId().c_str());
321 isWatched_ = false;
322 }
323 }
324 return isEmpty;
325 }
326
Clear(napi_env env)327 void ChangeEventListener::Clear(napi_env env)
328 {
329 EventListener::Clear(env);
330 if (isWatched_ && object_ != nullptr) {
331 uint32_t ret = objectStore_->UnWatch(object_);
332 if (ret != SUCCESS) {
333 LOG_ERROR("UnWatch %{public}s error", object_->GetSessionId().c_str());
334 } else {
335 LOG_INFO("UnWatch %{public}s success", object_->GetSessionId().c_str());
336 isWatched_ = false;
337 }
338 }
339 }
340
ChangeEventListener(JSWatcher * watcher,DistributedObjectStore * objectStore,DistributedObject * object)341 ChangeEventListener::ChangeEventListener(
342 JSWatcher *watcher, DistributedObjectStore *objectStore, DistributedObject *object)
343 : objectStore_(objectStore), object_(object), watcher_(watcher)
344 {
345 }
346
Add(napi_env env,napi_value handler)347 bool StatusEventListener::Add(napi_env env, napi_value handler)
348 {
349 LOG_INFO("Add status watch %{public}s", sessionId_.c_str());
350 NotifierImpl::GetInstance()->AddWatcher(sessionId_, watcher_);
351 return EventListener::Add(env, handler);
352 }
353
Del(napi_env env,napi_value handler)354 bool StatusEventListener::Del(napi_env env, napi_value handler)
355 {
356 if (EventListener::Del(env, handler)) {
357 LOG_INFO("Del status watch %{public}s", sessionId_.c_str());
358 NotifierImpl::GetInstance()->DelWatcher(sessionId_);
359 return true;
360 }
361 return false;
362 }
363
Clear(napi_env env)364 void StatusEventListener::Clear(napi_env env)
365 {
366 NotifierImpl::GetInstance()->DelWatcher(sessionId_);
367 EventListener::Clear(env);
368 }
369
StatusEventListener(JSWatcher * watcher,const std::string & sessionId)370 StatusEventListener::StatusEventListener(JSWatcher *watcher, const std::string &sessionId)
371 : watcher_(watcher), sessionId_(sessionId)
372 {
373 }
374
ChangeArgs(const napi_ref callback,const std::string & sessionId,const std::vector<std::string> & changeData)375 JSWatcher::ChangeArgs::ChangeArgs(
376 const napi_ref callback, const std::string &sessionId, const std::vector<std::string> &changeData)
377 : callback_(callback), sessionId_(sessionId), changeData_(changeData)
378 {
379 }
380
StatusArgs(const napi_ref callback,const std::string & sessionId,const std::string & networkId,const std::string & status)381 JSWatcher::StatusArgs::StatusArgs(
382 const napi_ref callback, const std::string &sessionId, const std::string &networkId, const std::string &status)
383 : callback_(callback), sessionId_(sessionId), networkId_(networkId), status_(status)
384 {
385 }
386 } // namespace OHOS::ObjectStore
387