ws_storeService.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2019 HPCC Systems.
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "ws_storeService.hpp"
  14. #include "exception_util.hpp"
  15. #define SDS_LOCK_TIMEOUT_ESPSTORE (30*1000)
  16. #define DEFAULT_ESP_STORE_FACTORY_METHOD "newEspStore"
  17. #define DEFAULT_ESP_STORE_MAX_VAL_SIZE 1024
  18. #define ESP_STORE_NAME_ATT "@name"
  19. #define ESP_STORE_TYPE_ATT "@type"
  20. #define ESP_STORE_DESCRIPTION_ATT "@description"
  21. #define ESP_STORE_MAXVALSIZE_ATT "@maxvalsize"
  22. #define ESP_STORE_DEFAULT_ATT "@default"
  23. typedef IEspStore* (*newEspStore_t_)();
  24. void CwsstoreEx::init(IPropertyTree *_cfg, const char *_process, const char *_service)
  25. {
  26. if(_cfg == nullptr)
  27. throw MakeStringException(-1, "CwsstoreEx::init: Empty configuration provided.");
  28. #ifdef _DEBUG
  29. StringBuffer thexml;
  30. toXML(_cfg, thexml,0,0);
  31. fprintf(stderr, "%s", thexml.str());
  32. #endif
  33. StringBuffer xpath;
  34. xpath.appendf("Software/EspProcess[@name=\"%s\"]/EspService[@name=\"%s\"]", _process, _service);
  35. m_serviceConfig.setown(_cfg->getPropTree(xpath.str()));
  36. if(!m_serviceConfig)
  37. throw MakeStringException(-1, "CwsstoreEx::init: Config not found for service %s/%s",_process, _service);
  38. IPropertyTree * storeProviderTree = m_serviceConfig->queryPropTree("StoreProvider[1]");
  39. if (storeProviderTree == nullptr)
  40. throw MakeStringException(-1, "CwsstoreEx::init: Store provider configuration not found for service %s/%s",_process, _service);
  41. const char * providerLibraryName = storeProviderTree->queryProp("@lib");
  42. if (!providerLibraryName || !*providerLibraryName)
  43. throw MakeStringException(-1, "CwsstoreEx::init: Must provide store provider library name for service %s/%s",_process, _service);
  44. const char * providerInstanceName = storeProviderTree->queryProp("@name");
  45. const char * providerFactoryMethod = storeProviderTree->queryProp("@factoryMethod");
  46. m_storeProvider.setown(loadStoreProvider(providerInstanceName, providerLibraryName, (providerFactoryMethod && *providerFactoryMethod) ? providerFactoryMethod : DEFAULT_ESP_STORE_FACTORY_METHOD));
  47. if (!m_storeProvider)
  48. throw MakeStringException(-1, "CwsstoreEx::init: Couldn't instantiate storeprovider lib: '%s' method: '%s'",providerLibraryName, (providerFactoryMethod && *providerFactoryMethod) ? providerFactoryMethod : DEFAULT_ESP_STORE_FACTORY_METHOD);
  49. m_storeProvider->init(providerInstanceName, "type", storeProviderTree);
  50. if (!m_isDetachedFromDali)
  51. {
  52. ESPLOG(LogMin, "CwsstoreEx: Ensuring configured stores are created:");
  53. Owned<IPropertyTreeIterator> iter = m_serviceConfig->getElements("Stores/Store");
  54. StringBuffer owner;
  55. owner.setf("%s/%s", _process, _service);
  56. Owned<ISecUser> secuser = new CSecureUser(owner.str(), nullptr);
  57. m_defaultStore.clear();
  58. Owned<IPropertyTree> stores = m_storeProvider->getStores(nullptr, nullptr, nullptr, secuser.get());
  59. ForEach(*iter)
  60. {
  61. StringBuffer id;
  62. StringBuffer type;
  63. StringBuffer description;
  64. bool isDefault = false;
  65. iter->query().getProp(ESP_STORE_NAME_ATT, id);
  66. iter->query().getProp(ESP_STORE_TYPE_ATT, type);
  67. iter->query().getProp(ESP_STORE_DESCRIPTION_ATT, description);
  68. unsigned int maxvalsize = iter->query().getPropInt(ESP_STORE_MAXVALSIZE_ATT, DEFAULT_ESP_STORE_MAX_VAL_SIZE);
  69. isDefault = iter->query().getPropBool(ESP_STORE_DEFAULT_ATT, false);
  70. VStringBuffer xpath("Stores/Store[%s='%s']", ESP_STORE_NAME_ATT, id.str());
  71. if (stores && stores->hasProp(xpath.str()))
  72. {
  73. ESPLOG(LogMin, "CwsstoreEx: Detected previously created store '%s'.", id.str());
  74. }
  75. else
  76. {
  77. ESPLOG(LogMin, "CwsstoreEx: Creating Store: '%s'%s", id.str(), isDefault ? " - as Default" : "");
  78. m_storeProvider->createStore(type.str(), id.str(), description.str(), secuser.get(), maxvalsize);
  79. }
  80. if (isDefault)
  81. {
  82. if (!m_defaultStore.isEmpty())
  83. throw MakeStringException(-1, "ws_store init(): Multiple stores erroneously configured as default store!");
  84. ESPLOG(LogMin, "CwsstoreEx: setting '%s' as default store", id.str());
  85. m_defaultStore.set(id.str());
  86. }
  87. }
  88. }
  89. }
  90. IEspStore* CwsstoreEx::loadStoreProvider(const char* instanceName, const char* libName, const char * factoryMethodName)
  91. {
  92. if (!libName || !*libName)
  93. throw MakeStringException(-1, "CwsstoreEx::loadStoreProvider: Library name not provided!");
  94. HINSTANCE espstorelib = LoadSharedObject(libName, true, false);
  95. if(!espstorelib)
  96. {
  97. StringBuffer realName;
  98. // add suffix and prefix if needed
  99. realName.append(SharedObjectPrefix).append(libName).append(SharedObjectExtension);
  100. espstorelib = LoadSharedObject(realName.str(), true, false);
  101. if(!espstorelib)
  102. throw MakeStringException(-1, "CwsstoreEx::loadStoreProvider: Cannot load library '%s'", realName.str());
  103. }
  104. newEspStore_t_ xproc = (newEspStore_t_)GetSharedProcedure(espstorelib, factoryMethodName);
  105. if (!xproc)
  106. throw MakeStringException(-1, "CwsstoreEx::loadStoreProvider: Cannot load procedure '%s' from library '%s'", factoryMethodName, libName);
  107. return (IEspStore*) xproc();
  108. }
  109. bool CwsstoreEx::onListStores(IEspContext &context, IEspListStoresRequest &req, IEspListStoresResponse &resp)
  110. {
  111. const char *user = context.queryUserId();
  112. Owned<ISecUser> secuser = new CSecureUser(user, nullptr);
  113. double version = context.getClientVersion();
  114. const char * namefilter = req.getNameFilter();
  115. const char * ownerfilter = req.getOwnerFilter();
  116. const char * typefilter = req.getTypeFilter();
  117. IArrayOf<IEspStoreInfo> storeinfos;
  118. Owned<IPropertyTree> stores = m_storeProvider->getStores(namefilter, ownerfilter, typefilter, secuser.get());
  119. if (stores)
  120. {
  121. Owned<IPropertyTreeIterator> iter = stores->getElements("Store");
  122. ForEach(*iter)
  123. {
  124. IPropertyTree * tree = &iter->query();
  125. Owned<IEspStoreInfo> store = createStoreInfo();
  126. store->setOwner(tree->queryProp("@createUser"));
  127. store->setName(tree->queryProp("@name"));
  128. store->setCreateTime(tree->queryProp("@createTime"));
  129. store->setType(tree->queryProp("@type"));
  130. store->setDescription(tree->queryProp("@description"));
  131. store->setMaxValSize(tree->queryProp("@maxValSize"));
  132. store->setIsDefault (!m_defaultStore.isEmpty() && stricmp(m_defaultStore.str(), tree->queryProp("@name"))==0);
  133. storeinfos.append(*store.getClear());
  134. }
  135. resp.setStores(storeinfos);
  136. }
  137. return true;
  138. }
  139. bool CwsstoreEx::onCreateStore(IEspContext &context, IEspCreateStoreRequest &req, IEspCreateStoreResponse &resp)
  140. {
  141. const char *user = context.queryUserId();
  142. Owned<ISecUser> secuser = new CSecureUser(user, nullptr);
  143. double version = context.getClientVersion();
  144. unsigned int maxvalsize = DEFAULT_ESP_STORE_MAX_VAL_SIZE;
  145. if (version >= 1.02)
  146. {
  147. maxvalsize = req.getMaxValueSize();
  148. }
  149. bool success = m_storeProvider->createStore(req.getType(), req.getName(), req.getDescription(), secuser.get(), maxvalsize);
  150. if (version > 1)
  151. resp.setSuccess(success);
  152. resp.setName(req.getName());
  153. resp.setType(req.getType());
  154. resp.setDescription(req.getDescription());
  155. resp.setOwner(user);
  156. return true;
  157. }
  158. bool CwsstoreEx::onDelete(IEspContext &context, IEspDeleteRequest &req, IEspDeleteResponse &resp)
  159. {
  160. const char *user = context.queryUserId();
  161. Owned<ISecUser> secuser = new CSecureUser(user, nullptr);
  162. const char *storename = req.getStoreName();
  163. if (!storename || !*storename)
  164. {
  165. if (!m_defaultStore.isEmpty())
  166. storename = m_defaultStore.get();
  167. }
  168. resp.setSuccess( m_storeProvider->deletekey(storename, req.getNamespace(), req.getKey(), secuser.get(), !req.getUserSpecific()));
  169. return true;
  170. }
  171. bool CwsstoreEx::onDeleteNamespace(IEspContext &context, IEspDeleteNamespaceRequest &req, IEspDeleteNamespaceResponse &resp)
  172. {
  173. const char *user = context.queryUserId();
  174. Owned<ISecUser> secuser = new CSecureUser(user, nullptr);
  175. const char *storename = req.getStoreName();
  176. bool global = !req.getUserSpecific();
  177. const char *targetUser = req.getTargetUser();
  178. if (!global && !isEmptyString(targetUser))
  179. {
  180. ESPLOG(LogMin, "CwsstoreEx::onDeleteNamespace: '%s' requesting to delete namespace on behalf of '%s'", user, targetUser);
  181. user = targetUser;
  182. }
  183. if (!storename || !*storename)
  184. {
  185. if (!m_defaultStore.isEmpty())
  186. storename = m_defaultStore.get();
  187. }
  188. resp.setSuccess(m_storeProvider->deleteNamespace(storename, req.getNamespace(), secuser.get(), !req.getUserSpecific()));
  189. return true;
  190. }
  191. bool CwsstoreEx::onListNamespaces(IEspContext &context, IEspListNamespacesRequest &req, IEspListNamespacesResponse &resp)
  192. {
  193. const char *user = context.queryUserId();
  194. Owned<ISecUser> secuser = new CSecureUser(user, nullptr);
  195. const char *storename = req.getStoreName();
  196. if (!storename || !*storename)
  197. {
  198. if (!m_defaultStore.isEmpty())
  199. storename = m_defaultStore.get();
  200. }
  201. StringArray namespaces;
  202. m_storeProvider->fetchAllNamespaces(namespaces, storename, secuser.get(), !req.getUserSpecific());
  203. resp.setNamespaces(namespaces);
  204. resp.setStoreName(storename);
  205. return true;
  206. }
  207. bool CwsstoreEx::onListKeys(IEspContext &context, IEspListKeysRequest &req, IEspListKeysResponse &resp)
  208. {
  209. const char * ns = req.getNamespace();
  210. const char *user = context.queryUserId();
  211. Owned<ISecUser> secuser = new CSecureUser(user, nullptr);
  212. const char *storename = req.getStoreName();
  213. if (!storename || !*storename)
  214. {
  215. if (!m_defaultStore.isEmpty())
  216. storename = m_defaultStore.get();
  217. }
  218. StringArray keys;
  219. m_storeProvider->fetchKeySet(keys, storename, ns, secuser.get(), !req.getUserSpecific());
  220. resp.setKeySet(keys);
  221. resp.setNamespace(ns);
  222. resp.setStoreName(storename);
  223. return true;
  224. }
  225. bool CwsstoreEx::onSet(IEspContext &context, IEspSetRequest &req, IEspSetResponse &resp)
  226. {
  227. const char * ns = req.getNamespace();
  228. const char * key = req.getKey();
  229. const char * value = req.getValue();
  230. const char * storename = req.getStoreName();
  231. if (!storename || !*storename)
  232. {
  233. if (!m_defaultStore.isEmpty())
  234. storename = m_defaultStore.get();
  235. }
  236. const char *user = context.queryUserId();
  237. Owned<ISecUser> secuser = new CSecureUser(user, nullptr);
  238. resp.setSuccess(m_storeProvider->set(storename, ns, key, value, secuser.get(), !req.getUserSpecific()));
  239. return true;
  240. }
  241. bool CwsstoreEx::onFetch(IEspContext &context, IEspFetchRequest &req, IEspFetchResponse &resp)
  242. {
  243. StringBuffer value;
  244. const char *user = context.queryUserId();
  245. Owned<ISecUser> secuser = new CSecureUser(user, nullptr);
  246. const char * storename = req.getStoreName();
  247. if (!storename || !*storename)
  248. {
  249. if (!m_defaultStore.isEmpty())
  250. storename = m_defaultStore.get();
  251. }
  252. try
  253. {
  254. m_storeProvider->fetch(storename, req.getNamespace(), req.getKey(), value, secuser.get(), !req.getUserSpecific());
  255. resp.setValue(value.str());
  256. }
  257. catch(IException * e)
  258. {
  259. if (e->errorCode() == ECLWATCH_INVALID_QUERY_KEY)
  260. {
  261. StringBuffer msg;
  262. LOG(MCuserInfo, "WsStore: %s", e->errorMessage(msg).str());
  263. e->Release();
  264. return false;
  265. }
  266. else
  267. throw e;
  268. }
  269. return true;
  270. }
  271. bool CwsstoreEx::onFetchKeyMetadata(IEspContext &context, IEspFetchKeyMDRequest &req, IEspFetchKeyMDResponse &resp)
  272. {
  273. const char * ns = req.getNamespace();
  274. const char * user = context.queryUserId();
  275. Owned<ISecUser> secuser = new CSecureUser(user, nullptr);
  276. const char * storename = req.getStoreName();
  277. const char * key = req.getKey();
  278. if (!storename || !*storename)
  279. {
  280. if (!m_defaultStore.isEmpty())
  281. storename = m_defaultStore.get();
  282. }
  283. Owned<IPropertyTree> nstree = m_storeProvider->getAllKeyProperties(storename, ns, key, secuser.get(), !req.getUserSpecific());
  284. if (nstree)
  285. {
  286. IArrayOf<IEspKVPair> pairs;
  287. Owned<IAttributeIterator> attributes = nstree->getAttributes();
  288. ForEach(*attributes)
  289. {
  290. Owned<IEspKVPair> kvpair = createKVPair("","");
  291. kvpair->setKey(attributes->queryName());
  292. kvpair->setValue(attributes->queryValue());
  293. pairs.append(*kvpair.getClear());
  294. }
  295. resp.setPairs(pairs);
  296. }
  297. resp.setStoreName(storename);
  298. resp.setNamespace(req.getNamespace());
  299. resp.setKey(req.getKey());
  300. return true;
  301. }
  302. bool CwsstoreEx::onFetchAll(IEspContext &context, IEspFetchAllRequest &req, IEspFetchAllResponse &resp)
  303. {
  304. const char * ns = req.getNamespace();
  305. const char * user = context.queryUserId();
  306. Owned<ISecUser> secuser = new CSecureUser(user, nullptr);
  307. const char * storename = req.getStoreName();
  308. if (!storename || !*storename)
  309. {
  310. if (!m_defaultStore.isEmpty())
  311. storename = m_defaultStore.get();
  312. }
  313. Owned<IPropertyTree> nstree = m_storeProvider->getAllPairs(storename, ns, secuser.get(), !req.getUserSpecific());
  314. IArrayOf<IEspKVPair> pairs;
  315. Owned<IPropertyTreeIterator> iter = nstree->getElements("*");
  316. ForEach(*iter)
  317. {
  318. StringBuffer name;
  319. StringBuffer value;
  320. iter->query().getName(name);
  321. nstree->getProp(name.str(), value);
  322. Owned<IEspKVPair> kvpair = createKVPair("","");
  323. kvpair->setKey(name.str());
  324. kvpair->setValue(value.str());
  325. pairs.append(*kvpair.getClear());
  326. }
  327. resp.setPairs(pairs);
  328. resp.setNamespace(req.getNamespace());
  329. return true;
  330. }