Imported Upstream version 0.9.2
[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     RCSResourceAttributes getAttributesFromOCRequest(
79             std::shared_ptr< OC::OCResourceRequest > request)
80     {
81         return ResourceAttributesConverter::fromOCRepresentation(
82                 request->getResourceRepresentation());
83     }
84
85     template< typename HANDLER, typename RESPONSE =
86             typename std::decay<HANDLER>::type::result_type >
87     RESPONSE invokeHandler(RCSResourceAttributes& attrs,
88             std::shared_ptr< OC::OCResourceRequest > ocRequest, HANDLER&& handler)
89     {
90         if (handler)
91         {
92             return handler(RCSRequest{ ocRequest->getResourceUri() }, attrs);
93         }
94
95         return RESPONSE::defaultAction();
96     }
97
98     typedef void (RCSResourceObject::* AutoNotifyFunc)
99             (bool, RCSResourceObject::AutoNotifyPolicy) const;
100
101     std::function <void ()> createAutoNotifyInvoker(AutoNotifyFunc autoNotifyFunc,
102             const RCSResourceObject& resourceObject, const RCSResourceAttributes& resourceAttributes,
103             RCSResourceObject::AutoNotifyPolicy autoNotifyPolicy)
104     {
105         if(autoNotifyPolicy == RCSResourceObject::AutoNotifyPolicy::UPDATED)
106         {
107             auto&& compareAttributesFunc =
108                     std::bind(std::not_equal_to<RCSResourceAttributes>(),
109                                 resourceAttributes,
110                                 std::cref(resourceAttributes));
111             return std::bind(autoNotifyFunc,
112                     &resourceObject, std::move(compareAttributesFunc), autoNotifyPolicy);
113         }
114         else if(autoNotifyPolicy == RCSResourceObject::AutoNotifyPolicy::ALWAYS)
115         {
116             return std::bind(autoNotifyFunc,
117                     &resourceObject, true, autoNotifyPolicy);
118         }
119         return {};
120     }
121 } // unnamed namespace
122
123
124 namespace OIC
125 {
126     namespace Service
127     {
128
129         RCSResourceObject::Builder::Builder(const std::string& uri, const std::string& type,
130                 const std::string& interface) :
131                 m_uri{ uri },
132                 m_type{ type },
133                 m_interface{ interface },
134                 m_properties{ OC_DISCOVERABLE | OC_OBSERVABLE },
135                 m_resourceAttributes{ }
136         {
137         }
138
139         RCSResourceObject::Builder& RCSResourceObject::Builder::setDiscoverable(
140                 bool discoverable)
141         {
142             m_properties = ::makePropertyFlags(m_properties, OC_DISCOVERABLE, discoverable);
143             return *this;
144         }
145
146         RCSResourceObject::Builder& RCSResourceObject::Builder::setObservable(
147                 bool observable)
148         {
149             m_properties = ::makePropertyFlags(m_properties, OC_OBSERVABLE, observable);
150             return *this;
151         }
152
153         RCSResourceObject::Builder& RCSResourceObject::Builder::setAttributes(
154                 const RCSResourceAttributes& attrs)
155         {
156             m_resourceAttributes = attrs;
157             return *this;
158         }
159
160         RCSResourceObject::Builder& RCSResourceObject::Builder::setAttributes(
161                 RCSResourceAttributes&& attrs)
162         {
163             m_resourceAttributes = std::move(attrs);
164             return *this;
165         }
166
167         RCSResourceObject::Ptr RCSResourceObject::Builder::build()
168         {
169             OCResourceHandle handle{ nullptr };
170
171             RCSResourceObject::Ptr server {
172                 new RCSResourceObject{ m_properties, std::move(m_resourceAttributes) } };
173
174             OC::EntityHandler entityHandler{ std::bind(&RCSResourceObject::entityHandler,
175                     server.get(), std::placeholders::_1) };
176
177             typedef OCStackResult (*RegisterResource)(OCResourceHandle&, std::string&,
178                     const std::string&, const std::string&, OC::EntityHandler, uint8_t);
179
180             invokeOCFunc(static_cast<RegisterResource>(OC::OCPlatform::registerResource),
181                     handle, m_uri, m_type, m_interface, entityHandler, m_properties);
182
183             server->m_resourceHandle = handle;
184
185             return server;
186         }
187
188
189         RCSResourceObject::RCSResourceObject(uint8_t properties, RCSResourceAttributes&& attrs) :
190                 m_properties { properties },
191                 m_resourceHandle{ },
192                 m_resourceAttributes{ std::move(attrs) },
193                 m_getRequestHandler{ },
194                 m_setRequestHandler{ },
195                 m_autoNotifyPolicy { AutoNotifyPolicy::UPDATED },
196                 m_setRequestHandlerPolicy { SetRequestHandlerPolicy::NEVER },
197                 m_keyAttributesUpdatedListeners{ },
198                 m_lockOwner{ },
199                 m_mutex{ },
200                 m_mutexKeyAttributeUpdate{ }
201         {
202         }
203
204         RCSResourceObject::~RCSResourceObject()
205         {
206             if (m_resourceHandle)
207             {
208                 try
209                 {
210                     OC::OCPlatform::unregisterResource(m_resourceHandle);
211                 }
212                 catch (...)
213                 {
214                     OC_LOG(WARNING, LOG_TAG, "Failed to unregister resource.");
215                 }
216             }
217         }
218
219         template< typename K, typename V >
220         void RCSResourceObject::setAttributeInternal(K&& key, V&& value)
221         {
222             bool needToNotify = false;
223             bool valueUpdated = false;
224
225             {
226                 WeakGuard lock(*this);
227
228                 if (lock.hasLocked())
229                 {
230                     needToNotify = true;
231                     valueUpdated = testValueUpdated(key, value);
232                 }
233
234                 m_resourceAttributes[std::forward< K >(key)] = std::forward< V >(value);
235             }
236
237             if (needToNotify) autoNotify(valueUpdated);
238         }
239         void RCSResourceObject::setAttribute(const std::string& key,
240                 const RCSResourceAttributes::Value& value)
241         {
242             setAttributeInternal(key, value);
243         }
244
245         void RCSResourceObject::setAttribute(const std::string& key,
246                 RCSResourceAttributes::Value&& value)
247         {
248             setAttributeInternal(key, std::move(value));
249         }
250
251         void RCSResourceObject::setAttribute(std::string&& key,
252                 const RCSResourceAttributes::Value& value)
253         {
254             setAttributeInternal(std::move(key), value);
255         }
256
257         void RCSResourceObject::setAttribute(std::string&& key,
258                 RCSResourceAttributes::Value&& value)
259         {
260             setAttributeInternal(std::move(key), std::move(value));
261         }
262
263         RCSResourceAttributes::Value RCSResourceObject::getAttributeValue(
264                 const std::string& key) const
265         {
266             WeakGuard lock(*this);
267             return m_resourceAttributes.at(key);
268         }
269
270         bool RCSResourceObject::removeAttribute(const std::string& key)
271         {
272             bool needToNotify = false;
273             bool erased = false;
274             {
275                 WeakGuard lock(*this);
276
277                 if (m_resourceAttributes.erase(key))
278                 {
279                     erased = true;
280                     needToNotify = lock.hasLocked();
281                 }
282             }
283
284             if (needToNotify) autoNotify(true);
285
286             return erased;
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         RCSResourceAttributes& RCSResourceObject::getAttributes()
296         {
297             expectOwnLock();
298             return m_resourceAttributes;
299         }
300
301         const RCSResourceAttributes& 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({ OC_STACK_OK, OC_STACK_NO_OBSERVERS },
340                     static_cast< NotifyAllObservers >(OC::OCPlatform::notifyAllObservers),
341                     m_resourceHandle);
342         }
343
344         void RCSResourceObject::addAttributeUpdatedListener(const std::string& key,
345                 AttributeUpdatedListener h)
346         {
347             std::lock_guard<std::mutex> lock(m_mutexKeyAttributeUpdate);
348             m_keyAttributesUpdatedListeners[key] = std::move(h);
349         }
350
351         void RCSResourceObject::addAttributeUpdatedListener(std::string&& key,
352                 AttributeUpdatedListener h)
353         {
354            std::lock_guard<std::mutex> lock(m_mutexKeyAttributeUpdate);
355            m_keyAttributesUpdatedListeners[std::move(key)] = std::move(h);
356         }
357
358         bool RCSResourceObject::removeAttributeUpdatedListener(const std::string& key)
359         {
360            std::lock_guard<std::mutex> lock(m_mutexKeyAttributeUpdate);
361
362            return m_keyAttributesUpdatedListeners.erase(key) != 0;
363         }
364
365         bool RCSResourceObject::testValueUpdated(const std::string& key,
366                 const RCSResourceAttributes::Value& value) const
367         {
368             return m_resourceAttributes.contains(key) == false
369                     || m_resourceAttributes.at(key) != value;
370         }
371
372         void RCSResourceObject::setAutoNotifyPolicy(AutoNotifyPolicy policy)
373         {
374             m_autoNotifyPolicy = policy;
375         }
376
377         RCSResourceObject::AutoNotifyPolicy RCSResourceObject::getAutoNotifyPolicy() const
378         {
379             return m_autoNotifyPolicy;
380         }
381
382         void RCSResourceObject::setSetRequestHandlerPolicy(SetRequestHandlerPolicy policy)
383         {
384             m_setRequestHandlerPolicy = policy;
385         }
386
387         auto RCSResourceObject::getSetRequestHandlerPolicy() const -> SetRequestHandlerPolicy
388         {
389             return m_setRequestHandlerPolicy;
390         }
391
392         void RCSResourceObject::autoNotify(bool isAttributesChanged) const
393         {
394             autoNotify(isAttributesChanged, m_autoNotifyPolicy);
395         }
396
397         void RCSResourceObject::autoNotify(
398                         bool isAttributesChanged, AutoNotifyPolicy autoNotifyPolicy) const
399         {
400             if(autoNotifyPolicy == AutoNotifyPolicy::NEVER) return;
401             if(autoNotifyPolicy == AutoNotifyPolicy::UPDATED &&
402                     isAttributesChanged == false) return;
403
404             notify();
405         }
406
407         OCEntityHandlerResult RCSResourceObject::entityHandler(
408                 std::shared_ptr< OC::OCResourceRequest > request)
409         {
410             if (!request)
411             {
412                 return OC_EH_ERROR;
413             }
414
415             try
416             {
417                 if (request->getRequestHandlerFlag() & OC::RequestHandlerFlag::RequestFlag)
418                 {
419                     return handleRequest(request);
420                 }
421
422                 if (request->getRequestHandlerFlag() & OC::RequestHandlerFlag::ObserverFlag)
423                 {
424                     return handleObserve(request);
425                 }
426             }
427             catch (const std::exception& e)
428             {
429                 OC_LOG_V(WARNING, LOG_TAG, "Failed to handle request : %s", e.what());
430                 throw;
431             }
432             catch (...)
433             {
434                 OC_LOG(WARNING, LOG_TAG, "Failed to handle request.");
435                 throw;
436             }
437
438             return OC_EH_ERROR;
439         }
440
441         OCEntityHandlerResult RCSResourceObject::handleRequest(
442                 std::shared_ptr< OC::OCResourceRequest > request)
443         {
444             assert(request != nullptr);
445
446             if (request->getRequestType() == "GET")
447             {
448                 return handleRequestGet(request);
449             }
450
451             if (request->getRequestType() == "PUT")
452             {
453                 return handleRequestSet(request);
454             }
455
456             return OC_EH_ERROR;
457         }
458
459         OCEntityHandlerResult RCSResourceObject::handleRequestGet(
460                 std::shared_ptr< OC::OCResourceRequest > request)
461         {
462             assert(request != nullptr);
463
464             auto attrs = getAttributesFromOCRequest(request);
465
466             return sendResponse(*this, request, invokeHandler(attrs, request, m_getRequestHandler));
467         }
468
469         OCEntityHandlerResult RCSResourceObject::handleRequestSet(
470                 std::shared_ptr< OC::OCResourceRequest > request)
471         {
472             assert(request != nullptr);
473
474             auto attrs = getAttributesFromOCRequest(request);
475             auto response = invokeHandler(attrs, request, m_setRequestHandler);
476             auto requestHandler = response.getHandler();
477
478             assert(requestHandler != nullptr);
479
480             AttrKeyValuePairs replaced = requestHandler->applyAcceptanceMethod(
481                     response.getAcceptanceMethod(), *this, attrs);
482
483             for (const auto& it : replaced)
484             {
485                 std::lock_guard<std::mutex> lock(m_mutexKeyAttributeUpdate);
486
487                 auto keyAttribute = m_keyAttributesUpdatedListeners.find(it.first);
488                 if(keyAttribute != m_keyAttributesUpdatedListeners.end())
489                 {
490                     keyAttribute-> second(it.second, attrs[it.first]);
491                 }
492             }
493
494             autoNotify(!replaced.empty(), m_autoNotifyPolicy);
495             return sendResponse(*this, request, response);
496         }
497
498         OCEntityHandlerResult RCSResourceObject::handleObserve(
499                 std::shared_ptr< OC::OCResourceRequest > request)
500         {
501             assert(request != nullptr);
502
503             if (!isObservable())
504             {
505                 return OC_EH_ERROR;
506             }
507
508             return OC_EH_OK;
509         }
510
511         RCSResourceObject::LockGuard::LockGuard(const RCSResourceObject::Ptr ptr) :
512                 m_resourceObject(*ptr),
513                 m_autoNotifyPolicy{ ptr->getAutoNotifyPolicy() },
514                 m_isOwningLock{ false }
515         {
516             init();
517         }
518
519         RCSResourceObject::LockGuard::LockGuard(
520                 const RCSResourceObject& serverResource) :
521                 m_resourceObject(serverResource),
522                 m_autoNotifyPolicy{ serverResource.getAutoNotifyPolicy() },
523                 m_isOwningLock{ false }
524         {
525             init();
526         }
527
528         RCSResourceObject::LockGuard::LockGuard(
529                 const RCSResourceObject::Ptr ptr, AutoNotifyPolicy autoNotifyPolicy) :
530                 m_resourceObject(*ptr),
531                 m_autoNotifyPolicy { autoNotifyPolicy },
532                 m_isOwningLock{ false }
533         {
534             init();
535         }
536
537         RCSResourceObject::LockGuard::LockGuard(
538                 const RCSResourceObject& resourceObject, AutoNotifyPolicy autoNotifyPolicy) :
539                 m_resourceObject(resourceObject),
540                 m_autoNotifyPolicy { autoNotifyPolicy },
541                 m_isOwningLock{ false }
542         {
543             init();
544         }
545
546         RCSResourceObject::LockGuard::~LockGuard()
547         {
548             if (m_autoNotifyFunc) m_autoNotifyFunc();
549
550             if (m_isOwningLock)
551             {
552                 m_resourceObject.m_lockOwner = std::thread::id{ };
553                 m_resourceObject.m_mutex.unlock();
554             }
555         }
556
557         void RCSResourceObject::LockGuard::init()
558         {
559             if (m_resourceObject.m_lockOwner != std::this_thread::get_id())
560             {
561                 m_resourceObject.m_mutex.lock();
562                 m_resourceObject.m_lockOwner = std::this_thread::get_id();
563                 m_isOwningLock = true;
564             }
565             m_autoNotifyFunc = ::createAutoNotifyInvoker(&RCSResourceObject::autoNotify,
566                     m_resourceObject, m_resourceObject.m_resourceAttributes, m_autoNotifyPolicy);
567         }
568
569         RCSResourceObject::WeakGuard::WeakGuard(
570                 const RCSResourceObject& resourceObject) :
571                 m_isOwningLock{ false },
572                 m_resourceObject(resourceObject)
573         {
574             if (resourceObject.m_lockOwner != std::this_thread::get_id())
575             {
576                 m_resourceObject.m_mutex.lock();
577                 m_resourceObject.m_lockOwner = std::this_thread::get_id();
578                 m_isOwningLock = true;
579             }
580         }
581
582         RCSResourceObject::WeakGuard::~WeakGuard()
583         {
584             if (m_isOwningLock)
585             {
586                 m_resourceObject.m_lockOwner = std::thread::id{ };
587                 m_resourceObject.m_mutex.unlock();
588             }
589         }
590
591         bool RCSResourceObject::WeakGuard::hasLocked() const
592         {
593             return m_isOwningLock;
594         }
595     }
596 }