Rename ResourceObject to RCSResourceObject
[platform/upstream/iotivity.git] / service / resource-encapsulation / src / serverBuilder / src / RCSResourceObject.cpp
1 //******************************************************************
2 //
3 // Copyright 2015 Samsung Electronics All Rights Reserved.
4 //
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
6 //
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
10 //
11 //      http://www.apache.org/licenses/LICENSE-2.0
12 //
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
18 //
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
20
21 #include <RCSResourceObject.h>
22
23 #include <string>
24 #include <functional>
25 #include <vector>
26
27 #include <RequestHandler.h>
28 #include <AssertUtils.h>
29 #include <ResourceAttributesConverter.h>
30
31 #include <logger.h>
32 #include <OCPlatform.h>
33
34 namespace
35 {
36     using namespace OIC::Service;
37
38     constexpr char LOG_TAG[]{ "RCSResourceObject" };
39
40     inline bool hasProperty(uint8_t base, uint8_t target)
41     {
42         return (base & target) == target;
43     }
44
45     inline uint8_t makePropertyFlags(uint8_t base, uint8_t target, bool add)
46     {
47         if (add)
48         {
49             return base | target;
50         }
51
52         return base & ~target;
53     }
54
55     template <typename RESPONSE>
56     OCEntityHandlerResult sendResponse(RCSResourceObject& resource,
57             std::shared_ptr< OC::OCResourceRequest > ocRequest, RESPONSE&& response)
58     {
59         auto ocResponse = response.getHandler()->buildResponse(resource);
60         ocResponse->setRequestHandle(ocRequest->getRequestHandle());
61         ocResponse->setResourceHandle(ocRequest->getResourceHandle());
62
63         try
64         {
65             if (OC::OCPlatform::sendResponse(ocResponse) == OC_STACK_OK)
66             {
67                 return OC_EH_OK;
68             }
69         }
70         catch (const OC::OCException& e)
71         {
72             OC_LOG(WARNING, LOG_TAG, e.what());
73         }
74
75         return OC_EH_ERROR;
76     }
77
78     ResourceAttributes getAttributesFromOCRequest(std::shared_ptr< OC::OCResourceRequest > request)
79     {
80         return ResourceAttributesConverter::fromOCRepresentation(
81                 request->getResourceRepresentation());
82     }
83
84     template< typename HANDLER, typename RESPONSE = typename std::decay<HANDLER>::type::result_type >
85     RESPONSE invokeHandler(ResourceAttributes& attrs,
86             std::shared_ptr< OC::OCResourceRequest > ocRequest, HANDLER&& handler)
87     {
88         if (handler)
89         {
90             return handler(RCSRequest{ ocRequest->getResourceUri() }, attrs);
91         }
92
93         return RESPONSE::defaultAction();
94     }
95
96     typedef void (RCSResourceObject::* AutoNotifyFunc)(bool, RCSResourceObject::AutoNotifyPolicy) const;
97
98     std::function <void ()> createAutoNotifyInvoker(AutoNotifyFunc autoNotifyFunc,
99             const RCSResourceObject& resourceObject, const ResourceAttributes& resourceAttributes,
100             RCSResourceObject::AutoNotifyPolicy autoNotifyPolicy)
101     {
102         if(autoNotifyPolicy == RCSResourceObject::AutoNotifyPolicy::UPDATED)
103         {
104             auto&& compareAttributesFunc =
105                     std::bind(std::not_equal_to<ResourceAttributes>(),
106                                 resourceAttributes,
107                                 std::cref(resourceAttributes));
108             return std::bind(autoNotifyFunc,
109                     &resourceObject, std::move(compareAttributesFunc), autoNotifyPolicy);
110         }
111         else if(autoNotifyPolicy == RCSResourceObject::AutoNotifyPolicy::ALWAYS)
112         {
113             return std::bind(autoNotifyFunc,
114                     &resourceObject, true, autoNotifyPolicy);
115         }
116         return {};
117     }
118 } // unnamed namespace
119
120
121 namespace OIC
122 {
123     namespace Service
124     {
125
126         RCSResourceObject::Builder::Builder(const std::string& uri, const std::string& type,
127                 const std::string& interface) :
128                 m_uri{ uri },
129                 m_type{ type },
130                 m_interface{ interface },
131                 m_properties{ OC_DISCOVERABLE | OC_OBSERVABLE },
132                 m_resourceAttributes{ }
133         {
134         }
135
136         RCSResourceObject::Builder& RCSResourceObject::Builder::setDiscoverable(
137                 bool discoverable)
138         {
139             m_properties = ::makePropertyFlags(m_properties, OC_DISCOVERABLE, discoverable);
140             return *this;
141         }
142
143         RCSResourceObject::Builder& RCSResourceObject::Builder::setObservable(
144                 bool observable)
145         {
146             m_properties = ::makePropertyFlags(m_properties, OC_OBSERVABLE, observable);
147             return *this;
148         }
149
150         RCSResourceObject::Builder& RCSResourceObject::Builder::setAttributes(
151                 const ResourceAttributes& attrs)
152         {
153             m_resourceAttributes = attrs;
154             return *this;
155         }
156
157         RCSResourceObject::Builder& RCSResourceObject::Builder::setAttributes(
158                 ResourceAttributes&& attrs)
159         {
160             m_resourceAttributes = std::move(attrs);
161             return *this;
162         }
163
164         RCSResourceObject::Ptr RCSResourceObject::Builder::build()
165         {
166             OCResourceHandle handle{ nullptr };
167
168             RCSResourceObject::Ptr server {
169                 new RCSResourceObject{ m_properties, std::move(m_resourceAttributes) } };
170
171             OC::EntityHandler entityHandler{ std::bind(&RCSResourceObject::entityHandler,
172                     server.get(), std::placeholders::_1) };
173
174             try
175             {
176                 typedef OCStackResult (*RegisterResource)(OCResourceHandle&, std::string&,
177                         const std::string&, const std::string&, OC::EntityHandler, uint8_t);
178
179                 invokeOCFunc(static_cast<RegisterResource>(OC::OCPlatform::registerResource),
180                         handle, m_uri, m_type, m_interface, entityHandler, m_properties);
181             }
182             catch (OC::OCException& e)
183             {
184                 throw PlatformException(e.code());
185             }
186
187             server->m_resourceHandle = handle;
188
189             return server;
190         }
191
192
193         RCSResourceObject::RCSResourceObject(uint8_t properties, ResourceAttributes&& attrs) :
194                 m_properties { properties },
195                 m_resourceHandle{ },
196                 m_resourceAttributes{ std::move(attrs) },
197                 m_getRequestHandler{ },
198                 m_setRequestHandler{ },
199                 m_autoNotifyPolicy { AutoNotifyPolicy::UPDATED },
200                 m_setRequestHandlerPolicy { SetRequestHandlerPolicy::NEVER },
201                 m_keyAttributesUpdatedListeners{ },
202                 m_lockOwner{ },
203                 m_mutex{ },
204                 m_mutexKeyAttributeUpdate{ }
205         {
206         }
207
208         RCSResourceObject::~RCSResourceObject()
209         {
210             if (m_resourceHandle)
211             {
212                 try
213                 {
214                     OC::OCPlatform::unregisterResource(m_resourceHandle);
215                 }
216                 catch (...)
217                 {
218                     OC_LOG(WARNING, LOG_TAG, "Failed to unregister resource.");
219                 }
220             }
221         }
222
223         void RCSResourceObject::setAttribute(const std::string& key,
224                 const ResourceAttributes::Value& value)
225         {
226             WeakGuard lock(*this);
227
228             if(lock.hasLocked())
229             {
230                 autoNotifyIfNeeded(key, value);
231             }
232
233             m_resourceAttributes[key] = value;
234         }
235
236         void RCSResourceObject::setAttribute(const std::string& key, ResourceAttributes::Value&& value)
237         {
238             WeakGuard lock(*this);
239
240             if(lock.hasLocked())
241             {
242                 autoNotifyIfNeeded(key, value);
243             }
244
245             m_resourceAttributes[key] = std::move(value);
246         }
247
248         void RCSResourceObject::setAttribute(std::string&& key, const ResourceAttributes::Value& value)
249         {
250             WeakGuard lock(*this);
251
252             if(lock.hasLocked())
253             {
254                 autoNotifyIfNeeded(key, value);
255             }
256
257             m_resourceAttributes[std::move(key)] = value;
258         }
259
260         void RCSResourceObject::setAttribute(std::string&& key, ResourceAttributes::Value&& value)
261         {
262             WeakGuard lock(*this);
263
264             if(lock.hasLocked())
265             {
266                 autoNotifyIfNeeded(key, value);
267             }
268
269             m_resourceAttributes[std::move(key)] = std::move(value);
270         }
271
272         ResourceAttributes::Value RCSResourceObject::getAttributeValue(const std::string& key) const
273         {
274             WeakGuard lock(*this);
275             return m_resourceAttributes.at(key);
276         }
277
278         bool RCSResourceObject::removeAttribute(const std::string& key)
279         {
280             WeakGuard lock(*this);
281             if (m_resourceAttributes.erase(key))
282             {
283                 autoNotify(true, getAutoNotifyPolicy());
284                 return true;
285             }
286             return false;
287         }
288
289         bool RCSResourceObject::containsAttribute(const std::string& key) const
290         {
291             WeakGuard lock(*this);
292             return m_resourceAttributes.contains(key);
293         }
294
295         ResourceAttributes& RCSResourceObject::getAttributes()
296         {
297             expectOwnLock();
298             return m_resourceAttributes;
299         }
300
301         const ResourceAttributes& RCSResourceObject::getAttributes() const
302         {
303             expectOwnLock();
304             return m_resourceAttributes;
305         }
306
307         void RCSResourceObject::expectOwnLock() const
308         {
309             if (m_lockOwner != std::this_thread::get_id())
310             {
311                 throw NoLockException{ "Must acquire the lock first using LockGuard." };
312             }
313         }
314
315         bool RCSResourceObject::isObservable() const
316         {
317             return ::hasProperty(m_properties, OC_OBSERVABLE);
318         }
319
320         bool RCSResourceObject::isDiscoverable() const
321         {
322             return ::hasProperty(m_properties, OC_DISCOVERABLE);
323         }
324
325         void RCSResourceObject::setGetRequestHandler(GetRequestHandler h)
326         {
327             m_getRequestHandler = std::move(h);
328         }
329
330         void RCSResourceObject::setSetRequestHandler(SetRequestHandler h)
331         {
332             m_setRequestHandler = std::move(h);
333         }
334
335         void RCSResourceObject::notify() const
336         {
337             typedef OCStackResult (*NotifyAllObservers)(OCResourceHandle);
338
339             invokeOCFuncWithResultExpect(
340                     { OC_STACK_OK, OC_STACK_NO_OBSERVERS },
341                     static_cast< NotifyAllObservers >(OC::OCPlatform::notifyAllObservers),
342                     m_resourceHandle);
343         }
344
345         void RCSResourceObject::addAttributeUpdatedListener(const std::string& key,
346                 AttributeUpdatedListener h)
347         {
348             std::lock_guard<std::mutex> lock(m_mutexKeyAttributeUpdate);
349             m_keyAttributesUpdatedListeners[key] = std::move(h);
350         }
351
352         void RCSResourceObject::addAttributeUpdatedListener(std::string&& key,
353                 AttributeUpdatedListener h)
354         {
355            std::lock_guard<std::mutex> lock(m_mutexKeyAttributeUpdate);
356            m_keyAttributesUpdatedListeners[std::move(key)] = std::move(h);
357         }
358
359         bool RCSResourceObject::removeAttributeUpdatedListener(const std::string& key)
360         {
361            std::lock_guard<std::mutex> lock(m_mutexKeyAttributeUpdate);
362            return (bool) m_keyAttributesUpdatedListeners.erase(key);
363         }
364
365         void RCSResourceObject::autoNotifyIfNeeded(const std::string& key,
366                                                 const ResourceAttributes::Value& value)
367         {
368             autoNotify( m_resourceAttributes.contains(key) == false
369                         || m_resourceAttributes.at(key) != value
370                         , m_autoNotifyPolicy);
371         }
372
373         void RCSResourceObject::setAutoNotifyPolicy(AutoNotifyPolicy policy)
374         {
375             m_autoNotifyPolicy = policy;
376         }
377
378         RCSResourceObject::AutoNotifyPolicy RCSResourceObject::getAutoNotifyPolicy() const
379         {
380             return m_autoNotifyPolicy;
381         }
382
383         void RCSResourceObject::setSetRequestHandlerPolicy(SetRequestHandlerPolicy policy)
384         {
385             m_setRequestHandlerPolicy = policy;
386         }
387
388         RCSResourceObject::SetRequestHandlerPolicy RCSResourceObject::getSetRequestHandlerPolicy() const
389         {
390             return m_setRequestHandlerPolicy;
391         }
392
393         void RCSResourceObject::autoNotify(
394                         bool isAttributesChanged, AutoNotifyPolicy autoNotifyPolicy) const
395         {
396             if(autoNotifyPolicy == AutoNotifyPolicy::NEVER) return;
397             if(autoNotifyPolicy == AutoNotifyPolicy::UPDATED && isAttributesChanged == false) return;
398             notify();
399         }
400
401         OCEntityHandlerResult RCSResourceObject::entityHandler(
402                 std::shared_ptr< OC::OCResourceRequest > request)
403         {
404             if (!request)
405             {
406                 return OC_EH_ERROR;
407             }
408
409             try
410             {
411                 if (request->getRequestHandlerFlag() & OC::RequestHandlerFlag::RequestFlag)
412                 {
413                     return handleRequest(request);
414                 }
415
416                 if (request->getRequestHandlerFlag() & OC::RequestHandlerFlag::ObserverFlag)
417                 {
418                     return handleObserve(request);
419                 }
420             }
421             catch (const std::exception& e)
422             {
423                 OC_LOG_V(WARNING, LOG_TAG, "Failed to handle request : %s", e.what());
424                 throw;
425             }
426             catch (...)
427             {
428                 OC_LOG(WARNING, LOG_TAG, "Failed to handle request.");
429                 throw;
430             }
431
432             return OC_EH_ERROR;
433         }
434
435         OCEntityHandlerResult RCSResourceObject::handleRequest(
436                 std::shared_ptr< OC::OCResourceRequest > request)
437         {
438             assert(request != nullptr);
439
440             if (request->getRequestType() == "GET")
441             {
442                 return handleRequestGet(request);
443             }
444
445             if (request->getRequestType() == "PUT")
446             {
447                 return handleRequestSet(request);
448             }
449
450             return OC_EH_ERROR;
451         }
452
453         OCEntityHandlerResult RCSResourceObject::handleRequestGet(
454                 std::shared_ptr< OC::OCResourceRequest > request)
455         {
456             assert(request != nullptr);
457
458             auto attrs = getAttributesFromOCRequest(request);
459
460             return sendResponse(*this, request, invokeHandler(attrs, request, m_getRequestHandler));
461         }
462
463         OCEntityHandlerResult RCSResourceObject::handleRequestSet(
464                 std::shared_ptr< OC::OCResourceRequest > request)
465         {
466             assert(request != nullptr);
467
468             auto attrs = getAttributesFromOCRequest(request);
469             auto response = invokeHandler(attrs, request, m_setRequestHandler);
470             auto requestHandler = response.getHandler();
471
472             assert(requestHandler != nullptr);
473
474             AttrKeyValuePairs replaced = requestHandler->applyAcceptanceMethod(
475                     response.getAcceptanceMethod(), *this, attrs);
476
477             for (const auto& it : replaced)
478             {
479                 std::lock_guard<std::mutex> lock(m_mutexKeyAttributeUpdate);
480
481                 auto keyAttribute = m_keyAttributesUpdatedListeners.find(it.first);
482                 if(keyAttribute != m_keyAttributesUpdatedListeners.end())
483                 {
484                     keyAttribute-> second(it.second, attrs[it.first]);
485                 }
486             }
487
488             autoNotify(!replaced.empty(), m_autoNotifyPolicy);
489             return sendResponse(*this, request, response);
490         }
491
492         OCEntityHandlerResult RCSResourceObject::handleObserve(
493                 std::shared_ptr< OC::OCResourceRequest > request)
494         {
495             assert(request != nullptr);
496
497             if (!isObservable())
498             {
499                 return OC_EH_ERROR;
500             }
501
502             return OC_EH_OK;
503         }
504
505         RCSResourceObject::LockGuard::LockGuard(const RCSResourceObject::Ptr ptr) :
506                 m_resourceObject(*ptr),
507                 m_autoNotifyPolicy{ ptr->getAutoNotifyPolicy() },
508                 m_isOwningLock{ false }
509         {
510             init();
511         }
512
513         RCSResourceObject::LockGuard::LockGuard(
514                 const RCSResourceObject& serverResource) :
515                 m_resourceObject(serverResource),
516                 m_autoNotifyPolicy{ serverResource.getAutoNotifyPolicy() },
517                 m_isOwningLock{ false }
518         {
519             init();
520         }
521
522         RCSResourceObject::LockGuard::LockGuard(
523                 const RCSResourceObject::Ptr ptr, AutoNotifyPolicy autoNotifyPolicy) :
524                 m_resourceObject(*ptr),
525                 m_autoNotifyPolicy { autoNotifyPolicy },
526                 m_isOwningLock{ false }
527         {
528             init();
529         }
530
531         RCSResourceObject::LockGuard::LockGuard(
532                 const RCSResourceObject& resourceObject, AutoNotifyPolicy autoNotifyPolicy) :
533                         m_resourceObject(resourceObject),
534                         m_autoNotifyPolicy { autoNotifyPolicy },
535                         m_isOwningLock{ false }
536         {
537             init();
538         }
539
540         RCSResourceObject::LockGuard::~LockGuard()
541         {
542             if (m_autoNotifyFunc) m_autoNotifyFunc();
543
544             if (m_isOwningLock)
545             {
546                 m_resourceObject.m_lockOwner = std::thread::id{ };
547                 m_resourceObject.m_mutex.unlock();
548             }
549         }
550
551         void RCSResourceObject::LockGuard::init()
552         {
553             if (m_resourceObject.m_lockOwner != std::this_thread::get_id())
554             {
555                 m_resourceObject.m_mutex.lock();
556                 m_resourceObject.m_lockOwner = std::this_thread::get_id();
557                 m_isOwningLock = true;
558             }
559             m_autoNotifyFunc = ::createAutoNotifyInvoker(&RCSResourceObject::autoNotify,
560                     m_resourceObject, m_resourceObject.m_resourceAttributes, m_autoNotifyPolicy);
561         }
562
563         RCSResourceObject::WeakGuard::WeakGuard(
564                 const RCSResourceObject& resourceObject) :
565                 m_isOwningLock{ false },
566                 m_resourceObject(resourceObject)
567         {
568             if (resourceObject.m_lockOwner != std::this_thread::get_id())
569             {
570                 m_resourceObject.m_mutex.lock();
571                 m_isOwningLock = true;
572             }
573         }
574
575         RCSResourceObject::WeakGuard::~WeakGuard()
576         {
577             if (m_isOwningLock)
578             {
579                 m_resourceObject.m_mutex.unlock();
580             }
581         }
582
583         bool RCSResourceObject::WeakGuard::hasLocked() const
584         {
585             return m_isOwningLock;
586         }
587     }
588 }