Rename ResourceAttributes to RCSResourceAttributes
[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             try
178             {
179                 typedef OCStackResult (*RegisterResource)(OCResourceHandle&, std::string&,
180                         const std::string&, const std::string&, OC::EntityHandler, uint8_t);
181
182                 invokeOCFunc(static_cast<RegisterResource>(OC::OCPlatform::registerResource),
183                         handle, m_uri, m_type, m_interface, entityHandler, m_properties);
184             }
185             catch (OC::OCException& e)
186             {
187                 throw PlatformException(e.code());
188             }
189
190             server->m_resourceHandle = handle;
191
192             return server;
193         }
194
195
196         RCSResourceObject::RCSResourceObject(uint8_t properties, RCSResourceAttributes&& attrs) :
197                 m_properties { properties },
198                 m_resourceHandle{ },
199                 m_resourceAttributes{ std::move(attrs) },
200                 m_getRequestHandler{ },
201                 m_setRequestHandler{ },
202                 m_autoNotifyPolicy { AutoNotifyPolicy::UPDATED },
203                 m_setRequestHandlerPolicy { SetRequestHandlerPolicy::NEVER },
204                 m_keyAttributesUpdatedListeners{ },
205                 m_lockOwner{ },
206                 m_mutex{ },
207                 m_mutexKeyAttributeUpdate{ }
208         {
209         }
210
211         RCSResourceObject::~RCSResourceObject()
212         {
213             if (m_resourceHandle)
214             {
215                 try
216                 {
217                     OC::OCPlatform::unregisterResource(m_resourceHandle);
218                 }
219                 catch (...)
220                 {
221                     OC_LOG(WARNING, LOG_TAG, "Failed to unregister resource.");
222                 }
223             }
224         }
225
226         void RCSResourceObject::setAttribute(const std::string& key,
227                 const RCSResourceAttributes::Value& value)
228         {
229             WeakGuard lock(*this);
230
231             if(lock.hasLocked())
232             {
233                 autoNotifyIfNeeded(key, value);
234             }
235
236             m_resourceAttributes[key] = value;
237         }
238
239         void RCSResourceObject::setAttribute(const std::string& key,
240                 RCSResourceAttributes::Value&& value)
241         {
242             WeakGuard lock(*this);
243
244             if(lock.hasLocked())
245             {
246                 autoNotifyIfNeeded(key, value);
247             }
248
249             m_resourceAttributes[key] = std::move(value);
250         }
251
252         void RCSResourceObject::setAttribute(std::string&& key,
253                 const RCSResourceAttributes::Value& value)
254         {
255             WeakGuard lock(*this);
256
257             if(lock.hasLocked())
258             {
259                 autoNotifyIfNeeded(key, value);
260             }
261
262             m_resourceAttributes[std::move(key)] = value;
263         }
264
265         void RCSResourceObject::setAttribute(std::string&& key,
266                 RCSResourceAttributes::Value&& value)
267         {
268             WeakGuard lock(*this);
269
270             if(lock.hasLocked())
271             {
272                 autoNotifyIfNeeded(key, value);
273             }
274
275             m_resourceAttributes[std::move(key)] = std::move(value);
276         }
277
278         RCSResourceAttributes::Value RCSResourceObject::getAttributeValue(
279                 const std::string& key) const
280         {
281             WeakGuard lock(*this);
282             return m_resourceAttributes.at(key);
283         }
284
285         bool RCSResourceObject::removeAttribute(const std::string& key)
286         {
287             WeakGuard lock(*this);
288             if (m_resourceAttributes.erase(key))
289             {
290                 autoNotify(true, getAutoNotifyPolicy());
291                 return true;
292             }
293             return false;
294         }
295
296         bool RCSResourceObject::containsAttribute(const std::string& key) const
297         {
298             WeakGuard lock(*this);
299             return m_resourceAttributes.contains(key);
300         }
301
302         RCSResourceAttributes& RCSResourceObject::getAttributes()
303         {
304             expectOwnLock();
305             return m_resourceAttributes;
306         }
307
308         const RCSResourceAttributes& RCSResourceObject::getAttributes() const
309         {
310             expectOwnLock();
311             return m_resourceAttributes;
312         }
313
314         void RCSResourceObject::expectOwnLock() const
315         {
316             if (m_lockOwner != std::this_thread::get_id())
317             {
318                 throw NoLockException{ "Must acquire the lock first using LockGuard." };
319             }
320         }
321
322         bool RCSResourceObject::isObservable() const
323         {
324             return ::hasProperty(m_properties, OC_OBSERVABLE);
325         }
326
327         bool RCSResourceObject::isDiscoverable() const
328         {
329             return ::hasProperty(m_properties, OC_DISCOVERABLE);
330         }
331
332         void RCSResourceObject::setGetRequestHandler(GetRequestHandler h)
333         {
334             m_getRequestHandler = std::move(h);
335         }
336
337         void RCSResourceObject::setSetRequestHandler(SetRequestHandler h)
338         {
339             m_setRequestHandler = std::move(h);
340         }
341
342         void RCSResourceObject::notify() const
343         {
344             typedef OCStackResult (*NotifyAllObservers)(OCResourceHandle);
345
346             invokeOCFuncWithResultExpect(
347                     { OC_STACK_OK, OC_STACK_NO_OBSERVERS },
348                     static_cast< NotifyAllObservers >(OC::OCPlatform::notifyAllObservers),
349                     m_resourceHandle);
350         }
351
352         void RCSResourceObject::addAttributeUpdatedListener(const std::string& key,
353                 AttributeUpdatedListener h)
354         {
355             std::lock_guard<std::mutex> lock(m_mutexKeyAttributeUpdate);
356             m_keyAttributesUpdatedListeners[key] = std::move(h);
357         }
358
359         void RCSResourceObject::addAttributeUpdatedListener(std::string&& key,
360                 AttributeUpdatedListener h)
361         {
362            std::lock_guard<std::mutex> lock(m_mutexKeyAttributeUpdate);
363            m_keyAttributesUpdatedListeners[std::move(key)] = std::move(h);
364         }
365
366         bool RCSResourceObject::removeAttributeUpdatedListener(const std::string& key)
367         {
368            std::lock_guard<std::mutex> lock(m_mutexKeyAttributeUpdate);
369            return (bool) m_keyAttributesUpdatedListeners.erase(key);
370         }
371
372         void RCSResourceObject::autoNotifyIfNeeded(const std::string& key,
373                                                 const RCSResourceAttributes::Value& value)
374         {
375             autoNotify( m_resourceAttributes.contains(key) == false
376                         || m_resourceAttributes.at(key) != value
377                         , m_autoNotifyPolicy);
378         }
379
380         void RCSResourceObject::setAutoNotifyPolicy(AutoNotifyPolicy policy)
381         {
382             m_autoNotifyPolicy = policy;
383         }
384
385         RCSResourceObject::AutoNotifyPolicy RCSResourceObject::getAutoNotifyPolicy() const
386         {
387             return m_autoNotifyPolicy;
388         }
389
390         void RCSResourceObject::setSetRequestHandlerPolicy(SetRequestHandlerPolicy policy)
391         {
392             m_setRequestHandlerPolicy = policy;
393         }
394
395         auto RCSResourceObject::getSetRequestHandlerPolicy() const -> SetRequestHandlerPolicy
396         {
397             return m_setRequestHandlerPolicy;
398         }
399
400         void RCSResourceObject::autoNotify(
401                         bool isAttributesChanged, AutoNotifyPolicy autoNotifyPolicy) const
402         {
403             if(autoNotifyPolicy == AutoNotifyPolicy::NEVER) return;
404             if(autoNotifyPolicy == AutoNotifyPolicy::UPDATED && isAttributesChanged == false) return;
405             notify();
406         }
407
408         OCEntityHandlerResult RCSResourceObject::entityHandler(
409                 std::shared_ptr< OC::OCResourceRequest > request)
410         {
411             if (!request)
412             {
413                 return OC_EH_ERROR;
414             }
415
416             try
417             {
418                 if (request->getRequestHandlerFlag() & OC::RequestHandlerFlag::RequestFlag)
419                 {
420                     return handleRequest(request);
421                 }
422
423                 if (request->getRequestHandlerFlag() & OC::RequestHandlerFlag::ObserverFlag)
424                 {
425                     return handleObserve(request);
426                 }
427             }
428             catch (const std::exception& e)
429             {
430                 OC_LOG_V(WARNING, LOG_TAG, "Failed to handle request : %s", e.what());
431                 throw;
432             }
433             catch (...)
434             {
435                 OC_LOG(WARNING, LOG_TAG, "Failed to handle request.");
436                 throw;
437             }
438
439             return OC_EH_ERROR;
440         }
441
442         OCEntityHandlerResult RCSResourceObject::handleRequest(
443                 std::shared_ptr< OC::OCResourceRequest > request)
444         {
445             assert(request != nullptr);
446
447             if (request->getRequestType() == "GET")
448             {
449                 return handleRequestGet(request);
450             }
451
452             if (request->getRequestType() == "PUT")
453             {
454                 return handleRequestSet(request);
455             }
456
457             return OC_EH_ERROR;
458         }
459
460         OCEntityHandlerResult RCSResourceObject::handleRequestGet(
461                 std::shared_ptr< OC::OCResourceRequest > request)
462         {
463             assert(request != nullptr);
464
465             auto attrs = getAttributesFromOCRequest(request);
466
467             return sendResponse(*this, request, invokeHandler(attrs, request, m_getRequestHandler));
468         }
469
470         OCEntityHandlerResult RCSResourceObject::handleRequestSet(
471                 std::shared_ptr< OC::OCResourceRequest > request)
472         {
473             assert(request != nullptr);
474
475             auto attrs = getAttributesFromOCRequest(request);
476             auto response = invokeHandler(attrs, request, m_setRequestHandler);
477             auto requestHandler = response.getHandler();
478
479             assert(requestHandler != nullptr);
480
481             AttrKeyValuePairs replaced = requestHandler->applyAcceptanceMethod(
482                     response.getAcceptanceMethod(), *this, attrs);
483
484             for (const auto& it : replaced)
485             {
486                 std::lock_guard<std::mutex> lock(m_mutexKeyAttributeUpdate);
487
488                 auto keyAttribute = m_keyAttributesUpdatedListeners.find(it.first);
489                 if(keyAttribute != m_keyAttributesUpdatedListeners.end())
490                 {
491                     keyAttribute-> second(it.second, attrs[it.first]);
492                 }
493             }
494
495             autoNotify(!replaced.empty(), m_autoNotifyPolicy);
496             return sendResponse(*this, request, response);
497         }
498
499         OCEntityHandlerResult RCSResourceObject::handleObserve(
500                 std::shared_ptr< OC::OCResourceRequest > request)
501         {
502             assert(request != nullptr);
503
504             if (!isObservable())
505             {
506                 return OC_EH_ERROR;
507             }
508
509             return OC_EH_OK;
510         }
511
512         RCSResourceObject::LockGuard::LockGuard(const RCSResourceObject::Ptr ptr) :
513                 m_resourceObject(*ptr),
514                 m_autoNotifyPolicy{ ptr->getAutoNotifyPolicy() },
515                 m_isOwningLock{ false }
516         {
517             init();
518         }
519
520         RCSResourceObject::LockGuard::LockGuard(
521                 const RCSResourceObject& serverResource) :
522                 m_resourceObject(serverResource),
523                 m_autoNotifyPolicy{ serverResource.getAutoNotifyPolicy() },
524                 m_isOwningLock{ false }
525         {
526             init();
527         }
528
529         RCSResourceObject::LockGuard::LockGuard(
530                 const RCSResourceObject::Ptr ptr, AutoNotifyPolicy autoNotifyPolicy) :
531                 m_resourceObject(*ptr),
532                 m_autoNotifyPolicy { autoNotifyPolicy },
533                 m_isOwningLock{ false }
534         {
535             init();
536         }
537
538         RCSResourceObject::LockGuard::LockGuard(
539                 const RCSResourceObject& resourceObject, AutoNotifyPolicy autoNotifyPolicy) :
540                         m_resourceObject(resourceObject),
541                         m_autoNotifyPolicy { autoNotifyPolicy },
542                         m_isOwningLock{ false }
543         {
544             init();
545         }
546
547         RCSResourceObject::LockGuard::~LockGuard()
548         {
549             if (m_autoNotifyFunc) m_autoNotifyFunc();
550
551             if (m_isOwningLock)
552             {
553                 m_resourceObject.m_lockOwner = std::thread::id{ };
554                 m_resourceObject.m_mutex.unlock();
555             }
556         }
557
558         void RCSResourceObject::LockGuard::init()
559         {
560             if (m_resourceObject.m_lockOwner != std::this_thread::get_id())
561             {
562                 m_resourceObject.m_mutex.lock();
563                 m_resourceObject.m_lockOwner = std::this_thread::get_id();
564                 m_isOwningLock = true;
565             }
566             m_autoNotifyFunc = ::createAutoNotifyInvoker(&RCSResourceObject::autoNotify,
567                     m_resourceObject, m_resourceObject.m_resourceAttributes, m_autoNotifyPolicy);
568         }
569
570         RCSResourceObject::WeakGuard::WeakGuard(
571                 const RCSResourceObject& resourceObject) :
572                 m_isOwningLock{ false },
573                 m_resourceObject(resourceObject)
574         {
575             if (resourceObject.m_lockOwner != std::this_thread::get_id())
576             {
577                 m_resourceObject.m_mutex.lock();
578                 m_isOwningLock = true;
579             }
580         }
581
582         RCSResourceObject::WeakGuard::~WeakGuard()
583         {
584             if (m_isOwningLock)
585             {
586                 m_resourceObject.m_mutex.unlock();
587             }
588         }
589
590         bool RCSResourceObject::WeakGuard::hasLocked() const
591         {
592             return m_isOwningLock;
593         }
594     }
595 }