• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #define LOG_TAG "JsKVManager"
16 #include "js_kv_manager.h"
17 #include "distributed_kv_data_manager.h"
18 #include "js_device_kv_store.h"
19 #include "js_single_kv_store.h"
20 #include "js_util.h"
21 #include "log_print.h"
22 #include "napi_queue.h"
23 #include "js_error_utils.h"
24 
25 using namespace OHOS::DistributedKv;
26 
27 namespace OHOS::DistributedKVStore {
IsStoreTypeSupported(Options options)28 static bool IsStoreTypeSupported(Options options)
29 {
30     return (options.kvStoreType == KvStoreType::DEVICE_COLLABORATION)
31         || (options.kvStoreType == KvStoreType::SINGLE_VERSION);
32 }
33 
JsKVManager(const std::string & bundleName,napi_env env,ContextParam param)34 JsKVManager::JsKVManager(const std::string &bundleName, napi_env env, ContextParam param)
35     : bundleName_(bundleName), uvQueue_(std::make_shared<UvQueue>(env)),
36     param_(std::make_shared<ContextParam>(std::move(param)))
37 {
38 }
39 
~JsKVManager()40 JsKVManager::~JsKVManager()
41 {
42     ZLOGD("no memory leak for JsKVManager");
43     std::lock_guard<std::mutex> lck(deathMutex_);
44     for (auto& it : deathRecipient_) {
45         kvDataManager_.UnRegisterKvStoreServiceDeathRecipient(it);
46         it->Clear();
47     }
48     deathRecipient_.clear();
49 }
50 
CreateKVManager(napi_env env,napi_callback_info info)51 napi_value JsKVManager::CreateKVManager(napi_env env, napi_callback_info info)
52 {
53     size_t argc = 2;
54     napi_value argv[2] = { nullptr };
55     napi_value result = nullptr;
56     napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
57 
58     if (argc < 1) {
59         ThrowNapiError(env, Status::INVALID_ARGUMENT, "Parameter error:Mandatory parameters are left unspecified");
60         return result;
61     }
62 
63     std::string bundleName;
64     napi_status status = JSUtil::GetNamedProperty(env, argv[0], "bundleName", bundleName);
65     if (status == napi_generic_failure) {
66         ThrowNapiError(env, Status::INVALID_ARGUMENT, "Parameter error:Missing bundleName parameter.");
67         return result;
68     }
69     if (bundleName.empty()) {
70         ThrowNapiError(env, Status::INVALID_ARGUMENT, "Parameter error:The type of bundleName must be string.");
71         return result;
72     }
73 
74     napi_value jsContext = nullptr;
75     status = JSUtil::GetNamedProperty(env, argv[0], "context", jsContext);
76     if (status == napi_generic_failure) {
77         ThrowNapiError(env, Status::INVALID_ARGUMENT, "Parameter error:Missing context parameter.");
78         return result;
79     }
80 
81     status = napi_new_instance(env, Constructor(env), argc, argv, &result);
82     if (result == nullptr || status != napi_ok) {
83         ThrowNapiError(env, status, "KVManager::New failed!", false);
84         return result;
85     }
86 
87     return result;
88 }
89 
90 struct GetKVStoreContext : public ContextBase {
91     std::string storeId;
92     Options options;
93     JsSingleKVStore* kvStore = nullptr;
94     napi_ref ref = nullptr;
95 
GetCbInfoOHOS::DistributedKVStore::GetKVStoreContext96     void GetCbInfo(napi_env env, napi_callback_info info)
97     {
98         auto input = [env, this](size_t argc, napi_value* argv) {
99             // required 2 arguments :: <storeId> <options>
100             ASSERT_BUSINESS_ERR(this, argc >= 2, Status::INVALID_ARGUMENT,
101                 "Parameter error:Mandatory parameters are left unspecified");
102             status = JSUtil::GetValue(env, argv[0], storeId);
103             ASSERT_BUSINESS_ERR(this, ((status == napi_ok) && JSUtil::IsValid(storeId)), Status::INVALID_ARGUMENT,
104                 "Parameter error:storeId must be string,consist of letters,digits,underscores(_),limit 128 chars");
105             status = JSUtil::GetValue(env, argv[1], options);
106             ASSERT_BUSINESS_ERR(this, status == napi_ok, Status::INVALID_ARGUMENT,
107                 "Parameter error:The params type not matching option");
108             ASSERT_BUSINESS_ERR(this, options.securityLevel != INVALID_LABEL, Status::INVALID_ARGUMENT,
109                 "Parameter error:unusable securityLevel");
110             ASSERT_BUSINESS_ERR(this, IsStoreTypeSupported(options), Status::INVALID_ARGUMENT,
111                 "Parameter error:only support DEVICE_COLLABORATION or SINGLE_VERSION");
112             ZLOGD("GetKVStore kvStoreType=%{public}d", options.kvStoreType);
113             if (options.kvStoreType == KvStoreType::DEVICE_COLLABORATION) {
114                 ref = JSUtil::NewWithRef(env, argc, argv, reinterpret_cast<void**>(&kvStore),
115                                          JsDeviceKVStore::Constructor(env));
116             } else if (options.kvStoreType == KvStoreType::SINGLE_VERSION) {
117                 ref = JSUtil::NewWithRef(env, argc, argv, reinterpret_cast<void**>(&kvStore),
118                                          JsSingleKVStore::Constructor(env));
119             }
120         };
121         ContextBase::GetCbInfo(env, info, input);
122     }
123 };
124 
125 /*
126  * [JS API Prototype]
127  * [AsyncCallback]
128  *      getKVStore<T extends KVStore>(storeId: string, options: Options, callback: AsyncCallback<T>): void;
129  * [Promise]
130  *      getKVStore<T extends KVStore>(storeId: string, options: Options): Promise<T>;
131  */
GetKVStore(napi_env env,napi_callback_info info)132 napi_value JsKVManager::GetKVStore(napi_env env, napi_callback_info info)
133 {
134     auto ctxt = std::make_shared<GetKVStoreContext>();
135     ctxt->GetCbInfo(env, info);
136     ASSERT_NULL(!ctxt->isThrowError, "GetKVStore exit");
137 
138     auto execute = [ctxt]() {
139         auto kvm = reinterpret_cast<JsKVManager*>(ctxt->native);
140         ASSERT_ARGS(ctxt, kvm != nullptr, "KVManager is null, failed!");
141         AppId appId = { kvm->bundleName_ };
142         StoreId storeId = { ctxt->storeId };
143         ctxt->options.baseDir = kvm->param_->baseDir;
144         ctxt->options.area = kvm->param_->area + 1;
145         ctxt->options.hapName = kvm->param_->hapName;
146         ctxt->options.apiVersion = kvm->param_->apiVersion;
147         ZLOGD("Options area:%{public}d dir:%{public}s", ctxt->options.area,
148             JSUtil::Anonymous(ctxt->options.baseDir).c_str());
149         std::shared_ptr<DistributedKv::SingleKvStore> kvStore;
150         Status status = kvm->kvDataManager_.GetSingleKvStore(ctxt->options, appId, storeId, kvStore);
151         if (status == DATA_CORRUPTED) {
152             ctxt->options.rebuild = true;
153             status = kvm->kvDataManager_.GetSingleKvStore(ctxt->options, appId, storeId, kvStore);
154             ZLOGE("Data has corrupted, rebuild db");
155         }
156         ctxt->status = (GenerateNapiError(status, ctxt->jsCode, ctxt->error) == Status::SUCCESS) ?
157             napi_ok : napi_generic_failure;
158         ctxt->kvStore->SetKvStorePtr(kvStore);
159         ctxt->kvStore->SetSchemaInfo(!ctxt->options.schema.empty());
160         ctxt->kvStore->SetContextParam(kvm->param_);
161         ctxt->kvStore->SetUvQueue(kvm->uvQueue_);
162     };
163     auto output = [env, ctxt](napi_value& result) {
164         ctxt->status = napi_get_reference_value(env, ctxt->ref, &result);
165         napi_delete_reference(env, ctxt->ref);
166         ASSERT_STATUS(ctxt, "output KVManager failed");
167         ZLOGI("output delete reference success");
168     };
169     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
170 }
171 
172 /*
173  * [JS API Prototype]
174  * [AsyncCB]  closeKVStore(appId: string, storeId: string, kvStore: KVStore, callback: AsyncCallback<void>):void
175  * [Promise]  closeKVStore(appId: string, storeId: string, kvStore: KVStore):Promise<void>
176  */
CloseKVStore(napi_env env,napi_callback_info info)177 napi_value JsKVManager::CloseKVStore(napi_env env, napi_callback_info info)
178 {
179     struct ContextInfo : public ContextBase {
180         std::string appId;
181         std::string storeId;
182         napi_value kvStore;
183     };
184     auto ctxt = std::make_shared<ContextInfo>();
185     auto input = [env, ctxt](size_t argc, napi_value* argv) {
186         // required 3 arguments :: <appId> <storeId> <kvStore>
187         ASSERT_BUSINESS_ERR(ctxt, argc >= 2, Status::INVALID_ARGUMENT,
188             "Parameter error:Mandatory parameters are left unspecified");
189         ctxt->status = JSUtil::GetValue(env, argv[0], ctxt->appId);
190         ASSERT_BUSINESS_ERR(ctxt, (ctxt->status == napi_ok) && !ctxt->appId.empty(), Status::INVALID_ARGUMENT,
191             "Parameter error:appId empty");
192         ctxt->status = JSUtil::GetValue(env, argv[1], ctxt->storeId);
193         ASSERT_BUSINESS_ERR(ctxt, (ctxt->status == napi_ok) && JSUtil::IsValid(ctxt->storeId), Status::INVALID_ARGUMENT,
194             "Parameter error:storeId must be string,consist of letters, digits, underscores(_), limit 128 characters");
195     };
196     ctxt->GetCbInfo(env, info, input);
197     ASSERT_NULL(!ctxt->isThrowError, "CloseKVStore exits");
198 
199     auto execute = [ctxt]() {
200         AppId appId { ctxt->appId };
201         StoreId storeId { ctxt->storeId };
202         Status status = reinterpret_cast<JsKVManager*>(ctxt->native)->kvDataManager_.CloseKvStore(appId, storeId);
203         status = GenerateNapiError(status, ctxt->jsCode, ctxt->error);
204         ZLOGD("CloseKVStore return status:%{public}d", status);
205         ctxt->status
206             = (status == Status::SUCCESS) || (status == Status::STORE_NOT_FOUND) || (status == Status::STORE_NOT_OPEN)
207             ? napi_ok
208             : napi_generic_failure;
209     };
210     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute);
211 }
212 
213 /*
214  * [JS API Prototype]
215  * [AsyncCB]  deleteKVStore(appId: string, storeId: string, callback: AsyncCallback<void>): void
216  * [Promise]  deleteKVStore(appId: string, storeId: string):Promise<void>
217  */
DeleteKVStore(napi_env env,napi_callback_info info)218 napi_value JsKVManager::DeleteKVStore(napi_env env, napi_callback_info info)
219 {
220     struct ContextInfo : public ContextBase {
221         std::string appId;
222         std::string storeId;
223     };
224     auto ctxt = std::make_shared<ContextInfo>();
225     auto input = [env, ctxt](size_t argc, napi_value* argv) {
226         // required 2 arguments :: <appId> <storeId>
227         ASSERT_BUSINESS_ERR(ctxt, argc >= 2, Status::INVALID_ARGUMENT,
228             "Parameter error:Mandatory parameters are left unspecified");
229         size_t index = 0;
230         ctxt->status = JSUtil::GetValue(env, argv[index++], ctxt->appId);
231         ASSERT_BUSINESS_ERR(ctxt, !ctxt->appId.empty(), Status::INVALID_ARGUMENT,
232             "Parameter error:appId empty");
233         ctxt->status = JSUtil::GetValue(env, argv[index++], ctxt->storeId);
234         ASSERT_BUSINESS_ERR(ctxt, JSUtil::IsValid(ctxt->storeId), Status::INVALID_ARGUMENT,
235             "error:storeId must be string; consist of only letters, digits underscores (_),limit 128 characters");
236     };
237     ctxt->GetCbInfo(env, info, input);
238     ASSERT_NULL(!ctxt->isThrowError, "DeleteKVStore exits");
239 
240     auto execute = [ctxt]() {
241         AppId appId { ctxt->appId };
242         StoreId storeId { ctxt->storeId };
243         auto kvm = reinterpret_cast<JsKVManager*>(ctxt->native);
244         ASSERT_ARGS(ctxt, kvm != nullptr, "KVManager is null, failed!");
245         std::string databaseDir = kvm->param_->baseDir;
246         ZLOGD("DeleteKVStore databaseDir is: %{public}s", JSUtil::Anonymous(databaseDir).c_str());
247         Status status = kvm->kvDataManager_.DeleteKvStore(appId, storeId, databaseDir);
248         ZLOGD("DeleteKvStore status:%{public}d", status);
249         ctxt->status = (GenerateNapiError(status, ctxt->jsCode, ctxt->error) == Status::SUCCESS) ?
250             napi_ok : napi_generic_failure;
251     };
252     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute);
253 }
254 
255 /*
256  * [JS API Prototype]
257  * [AsyncCB]  getAllKVStoreId(appId: string, callback: AsyncCallback<string[]>):void
258  * [Promise]  getAllKVStoreId(appId: string):Promise<string[]>
259  */
GetAllKVStoreId(napi_env env,napi_callback_info info)260 napi_value JsKVManager::GetAllKVStoreId(napi_env env, napi_callback_info info)
261 {
262     struct ContextInfo : public ContextBase {
263         std::string appId;
264         std::vector<StoreId> storeIdList;
265     };
266 
267     auto ctxt = std::make_shared<ContextInfo>();
268     auto input = [env, ctxt](size_t argc, napi_value* argv) {
269         // required 1 arguments :: <appId>
270         ASSERT_BUSINESS_ERR(ctxt, argc >= 1, Status::INVALID_ARGUMENT,
271             "Parameter error:Mandatory parameters are left unspecified");
272         ctxt->status = JSUtil::GetValue(env, argv[0], ctxt->appId);
273         ASSERT_BUSINESS_ERR(ctxt, !ctxt->appId.empty(), Status::INVALID_ARGUMENT,
274             "Parameter error:appId empty");
275         ASSERT_BUSINESS_ERR(ctxt, (ctxt->appId.size() < MAX_APP_ID_LEN), Status::INVALID_ARGUMENT,
276             "Parameter error:appId exceed 256 characters");
277     };
278     ctxt->GetCbInfo(env, info, input);
279     ASSERT_NULL(!ctxt->isThrowError, "GetAllKVStoreId exits");
280 
281     auto execute = [ctxt]() {
282         auto kvm = reinterpret_cast<JsKVManager*>(ctxt->native);
283         ASSERT_ARGS(ctxt, kvm != nullptr, "KVManager is null, failed!");
284         AppId appId { ctxt->appId };
285         Status status = kvm->kvDataManager_.GetAllKvStoreId(appId, ctxt->storeIdList);
286         ZLOGD("execute status:%{public}d", status);
287         ctxt->status = (GenerateNapiError(status, ctxt->jsCode, ctxt->error) == Status::SUCCESS) ?
288             napi_ok : napi_generic_failure;
289     };
290     auto output = [env, ctxt](napi_value& result) {
291         ctxt->status = JSUtil::SetValue(env, ctxt->storeIdList, result);
292         ZLOGD("output status:%{public}d", ctxt->status);
293     };
294     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
295 }
296 
On(napi_env env,napi_callback_info info)297 napi_value JsKVManager::On(napi_env env, napi_callback_info info)
298 {
299     auto ctxt = std::make_shared<ContextBase>();
300     auto input = [env, ctxt](size_t argc, napi_value* argv) {
301         // required 2 arguments :: <event> <callback>
302         ASSERT_BUSINESS_ERR(ctxt, argc >= 2, Status::INVALID_ARGUMENT,
303             "Parameter error:Mandatory parameters are left unspecified");
304         std::string event;
305         ctxt->status = JSUtil::GetValue(env, argv[0], event);
306         ZLOGI("subscribe to event:%{public}s", event.c_str());
307         ASSERT_BUSINESS_ERR(ctxt, event == "distributedDataServiceDie", Status::INVALID_ARGUMENT,
308             "Parameter error:parameter event type not equal distributedDataServiceDie");
309 
310         napi_valuetype valueType = napi_undefined;
311         ctxt->status = napi_typeof(env, argv[1], &valueType);
312         ASSERT_BUSINESS_ERR(ctxt, (ctxt->status == napi_ok) && (valueType == napi_function), Status::INVALID_ARGUMENT,
313             "Parameter error:parameter callback must be function type");
314 
315         JsKVManager* proxy = reinterpret_cast<JsKVManager*>(ctxt->native);
316         ASSERT_BUSINESS_ERR(ctxt, proxy != nullptr, Status::INVALID_ARGUMENT,
317             "Parameter error:JsKVManager nullptr");
318 
319         std::lock_guard<std::mutex> lck(proxy->deathMutex_);
320         for (auto& it : proxy->deathRecipient_) {
321             if (JSUtil::Equals(env, argv[1], it->GetCallback())) {
322                 ZLOGD("KVManager::On callback already register!");
323                 return;
324             }
325         }
326         auto deathRecipient = std::make_shared<DeathRecipient>(proxy->uvQueue_, argv[1]);
327         proxy->kvDataManager_.RegisterKvStoreServiceDeathRecipient(deathRecipient);
328         proxy->deathRecipient_.push_back(deathRecipient);
329         ZLOGD("on mapsize: %{public}d", static_cast<int>(proxy->deathRecipient_.size()));
330     };
331     ctxt->GetCbInfoSync(env, info, input);
332     if (ctxt->status != napi_ok) {
333         ThrowNapiError(env, Status::INVALID_ARGUMENT, "");
334     }
335     return nullptr;
336 }
337 
Off(napi_env env,napi_callback_info info)338 napi_value JsKVManager::Off(napi_env env, napi_callback_info info)
339 {
340     auto ctxt = std::make_shared<ContextBase>();
341     auto input = [env, ctxt](size_t argc, napi_value* argv) {
342         // required 1 or 2 arguments :: <event> [callback]
343         ASSERT_BUSINESS_ERR(ctxt, argc > 0, Status::INVALID_ARGUMENT,
344             "Parameter error:Mandatory parameters are left unspecified");
345         std::string event;
346         ctxt->status = JSUtil::GetValue(env, argv[0], event);
347         // required 1 arguments :: <event>
348         ZLOGI("unsubscribe to event:%{public}s %{public}s specified", event.c_str(), (argc == 1) ? "without" : "with");
349         ASSERT_BUSINESS_ERR(ctxt, event == "distributedDataServiceDie", Status::INVALID_ARGUMENT,
350             "Parameter error:parameter event not equal distributedDataServiceDie");
351         // have 2 arguments :: have the [callback]
352         if (argc == 2) {
353             napi_valuetype valueType = napi_undefined;
354             ctxt->status = napi_typeof(env, argv[1], &valueType);
355             ASSERT_BUSINESS_ERR(ctxt, (ctxt->status == napi_ok) && (valueType == napi_function),
356                 Status::INVALID_ARGUMENT, "Parameter error:parameter callback type must be function");
357         }
358         JsKVManager* proxy = reinterpret_cast<JsKVManager*>(ctxt->native);
359         std::lock_guard<std::mutex> lck(proxy->deathMutex_);
360         auto it = proxy->deathRecipient_.begin();
361         while (it != proxy->deathRecipient_.end()) {
362             // have 2 arguments :: have the [callback]
363             if ((argc == 1) || JSUtil::Equals(env, argv[1], (*it)->GetCallback())) {
364                 proxy->kvDataManager_.UnRegisterKvStoreServiceDeathRecipient(*it);
365                 (*it)->Clear();
366                 it = proxy->deathRecipient_.erase(it);
367             } else {
368                 ++it;
369             }
370         }
371         ZLOGD("off mapsize: %{public}d", static_cast<int>(proxy->deathRecipient_.size()));
372     };
373     ctxt->GetCbInfoSync(env, info, input);
374     if (ctxt->status != napi_ok) {
375         ThrowNapiError(env, Status::INVALID_ARGUMENT, "Parameter error:params must be sting or function");
376     }
377     ZLOGD("KVManager::Off callback is not register or already unregister!");
378     return nullptr;
379 }
380 
Constructor(napi_env env)381 napi_value JsKVManager::Constructor(napi_env env)
382 {
383     auto lambda = []() -> std::vector<napi_property_descriptor> {
384         std::vector<napi_property_descriptor> properties = {
385             DECLARE_NAPI_FUNCTION("getKVStore", JsKVManager::GetKVStore),
386             DECLARE_NAPI_FUNCTION("closeKVStore", JsKVManager::CloseKVStore),
387             DECLARE_NAPI_FUNCTION("deleteKVStore", JsKVManager::DeleteKVStore),
388             DECLARE_NAPI_FUNCTION("getAllKVStoreId", JsKVManager::GetAllKVStoreId),
389             DECLARE_NAPI_FUNCTION("on", JsKVManager::On),
390             DECLARE_NAPI_FUNCTION("off", JsKVManager::Off),
391         };
392         return properties;
393     };
394     return JSUtil::DefineClass(env, "ohos.data.distributedKVStore", "JsKVManager", lambda, JsKVManager::New);
395 }
396 
New(napi_env env,napi_callback_info info)397 napi_value JsKVManager::New(napi_env env, napi_callback_info info)
398 {
399     std::string bundleName;
400     ContextParam param;
401     auto ctxt = std::make_shared<ContextBase>();
402     auto input = [env, ctxt, &bundleName, &param](size_t argc, napi_value* argv) {
403         // required 1 arguments :: <bundleName>
404         ASSERT_BUSINESS_ERR(ctxt, argc >= 1, Status::INVALID_ARGUMENT,
405             "Parameter error:Mandatory parameters are left unspecified");
406         ctxt->status = JSUtil::GetNamedProperty(env, argv[0], "bundleName", bundleName);
407         ASSERT_BUSINESS_ERR(ctxt, ctxt->status != napi_generic_failure, Status::INVALID_ARGUMENT,
408             "Parameter error:The bundleName parameter is missing.");
409         ASSERT_BUSINESS_ERR(ctxt, !bundleName.empty(), Status::INVALID_ARGUMENT,
410             "Parameter error:The bundleName field cannot be empty.");
411 
412         napi_value jsContext = nullptr;
413         JSUtil::GetNamedProperty(env, argv[0], "context", jsContext);
414         ctxt->status = JSUtil::GetValue(env, jsContext, param);
415         ASSERT_BUSINESS_ERR(ctxt, ctxt->status == napi_ok, Status::INVALID_ARGUMENT,
416             "Parameter error:get context failed");
417     };
418     ctxt->GetCbInfoSync(env, info, input);
419     ASSERT_NULL(!ctxt->isThrowError, "JsKVManager New exit");
420 
421     JsKVManager* kvManager = new (std::nothrow) JsKVManager(bundleName, env, param);
422     ASSERT_ERR(env, kvManager != nullptr, Status::INVALID_ARGUMENT, "Parameter error:kvManager is nullptr");
423 
424     auto finalize = [](napi_env env, void* data, void* hint) {
425         ZLOGD("kvManager finalize.");
426         auto* kvManager = reinterpret_cast<JsKVManager*>(data);
427         ASSERT_VOID(kvManager != nullptr, "kvManager is null!");
428         delete kvManager;
429     };
430     ASSERT_CALL(env, napi_wrap(env, ctxt->self, kvManager, finalize, nullptr, nullptr), kvManager);
431     return ctxt->self;
432 }
433 
OnRemoteDied()434 void JsKVManager::DeathRecipient::OnRemoteDied()
435 {
436     AsyncCall();
437 }
438 } // namespace OHOS::DistributedKVStore
439