1 //******************************************************************
3 // Copyright 2015 Samsung Electronics All Rights Reserved.
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
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
11 // http://www.apache.org/licenses/LICENSE-2.0
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.
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
21 #include <RCSResourceObject.h>
27 #include <RequestHandler.h>
28 #include <AssertUtils.h>
29 #include <AtomicHelper.h>
30 #include <ResourceAttributesConverter.h>
31 #include <ResourceAttributesUtils.h>
34 #include <OCPlatform.h>
36 #define LOG_TAG "RCSResourceObject"
40 using namespace OIC::Service;
42 inline bool hasProperty(uint8_t base, uint8_t target)
44 return (base & target) == target;
47 inline uint8_t makePropertyFlags(uint8_t base, uint8_t target, bool add)
54 return base & ~target;
57 template <typename RESPONSE>
58 OCEntityHandlerResult sendResponse(RCSResourceObject& resource,
59 std::shared_ptr< OC::OCResourceRequest > ocRequest, RESPONSE&& response)
61 auto ocResponse = response.getHandler()->buildResponse(resource);
62 ocResponse->setRequestHandle(ocRequest->getRequestHandle());
63 ocResponse->setResourceHandle(ocRequest->getResourceHandle());
67 if (OC::OCPlatform::sendResponse(ocResponse) == OC_STACK_OK)
72 catch (const OC::OCException& e)
74 OC_LOG_V(WARNING, LOG_TAG, "Error (%s)", e.what());
80 RCSResourceAttributes getAttributesFromOCRequest(
81 std::shared_ptr< OC::OCResourceRequest > request)
83 return ResourceAttributesConverter::fromOCRepresentation(
84 request->getResourceRepresentation());
87 template< typename HANDLER, typename RESPONSE =
88 typename std::decay<HANDLER>::type::result_type >
89 RESPONSE invokeHandler(RCSResourceAttributes& attrs,
90 std::shared_ptr< OC::OCResourceRequest > ocRequest, HANDLER&& handler)
94 return handler(RCSRequest{ ocRequest->getResourceUri() }, attrs);
97 return RESPONSE::defaultAction();
100 typedef void (RCSResourceObject::* AutoNotifyFunc)
101 (bool, RCSResourceObject::AutoNotifyPolicy) const;
103 std::function <void ()> createAutoNotifyInvoker(AutoNotifyFunc autoNotifyFunc,
104 const RCSResourceObject& resourceObject, const RCSResourceAttributes& resourceAttributes,
105 RCSResourceObject::AutoNotifyPolicy autoNotifyPolicy)
107 if(autoNotifyPolicy == RCSResourceObject::AutoNotifyPolicy::UPDATED)
109 auto&& compareAttributesFunc =
110 std::bind(std::not_equal_to<RCSResourceAttributes>(),
112 std::cref(resourceAttributes));
113 return std::bind(autoNotifyFunc,
114 &resourceObject, std::move(compareAttributesFunc), autoNotifyPolicy);
116 else if(autoNotifyPolicy == RCSResourceObject::AutoNotifyPolicy::ALWAYS)
118 return std::bind(autoNotifyFunc,
119 &resourceObject, true, autoNotifyPolicy);
123 } // unnamed namespace
131 RCSResourceObject::Builder::Builder(const std::string& uri, const std::string& type,
132 const std::string& interface) :
135 m_interface{ interface },
136 m_properties{ OC_DISCOVERABLE | OC_OBSERVABLE },
137 m_resourceAttributes{ }
141 RCSResourceObject::Builder& RCSResourceObject::Builder::setDiscoverable(
144 m_properties = ::makePropertyFlags(m_properties, OC_DISCOVERABLE, discoverable);
148 RCSResourceObject::Builder& RCSResourceObject::Builder::setObservable(
151 m_properties = ::makePropertyFlags(m_properties, OC_OBSERVABLE, observable);
155 RCSResourceObject::Builder& RCSResourceObject::Builder::setAttributes(
156 const RCSResourceAttributes& attrs)
158 m_resourceAttributes = attrs;
162 RCSResourceObject::Builder& RCSResourceObject::Builder::setAttributes(
163 RCSResourceAttributes&& attrs)
165 m_resourceAttributes = std::move(attrs);
169 RCSResourceObject::Ptr RCSResourceObject::Builder::build()
171 OCResourceHandle handle{ nullptr };
173 RCSResourceObject::Ptr server {
174 new RCSResourceObject{ m_properties, std::move(m_resourceAttributes) } };
176 OC::EntityHandler entityHandler{ std::bind(&RCSResourceObject::entityHandler,
177 server.get(), std::placeholders::_1) };
179 typedef OCStackResult (*RegisterResource)(OCResourceHandle&, std::string&,
180 const std::string&, const std::string&, OC::EntityHandler, uint8_t);
182 invokeOCFunc(static_cast<RegisterResource>(OC::OCPlatform::registerResource),
183 handle, m_uri, m_type, m_interface, entityHandler, m_properties);
185 server->m_resourceHandle = handle;
191 RCSResourceObject::RCSResourceObject(uint8_t properties, RCSResourceAttributes&& attrs) :
192 m_properties { properties },
194 m_resourceAttributes{ std::move(attrs) },
195 m_getRequestHandler{ },
196 m_setRequestHandler{ },
197 m_autoNotifyPolicy { AutoNotifyPolicy::UPDATED },
198 m_setRequestHandlerPolicy { SetRequestHandlerPolicy::NEVER },
199 m_attributeUpdatedListeners{ },
202 m_mutexAttributeUpdatedListeners{ }
204 m_lockOwner.reset(new AtomicThreadId);
207 RCSResourceObject::~RCSResourceObject()
209 if (m_resourceHandle)
213 OC::OCPlatform::unregisterResource(m_resourceHandle);
217 OC_LOG(WARNING, LOG_TAG, "Failed to unregister resource.");
222 template< typename K, typename V >
223 void RCSResourceObject::setAttributeInternal(K&& key, V&& value)
225 bool needToNotify = false;
226 bool valueUpdated = false;
229 WeakGuard lock(*this);
231 if (lock.hasLocked())
234 valueUpdated = testValueUpdated(key, value);
237 m_resourceAttributes[std::forward< K >(key)] = std::forward< V >(value);
240 if (needToNotify) autoNotify(valueUpdated);
242 void RCSResourceObject::setAttribute(const std::string& key,
243 const RCSResourceAttributes::Value& value)
245 setAttributeInternal(key, value);
248 void RCSResourceObject::setAttribute(const std::string& key,
249 RCSResourceAttributes::Value&& value)
251 setAttributeInternal(key, std::move(value));
254 void RCSResourceObject::setAttribute(std::string&& key,
255 const RCSResourceAttributes::Value& value)
257 setAttributeInternal(std::move(key), value);
260 void RCSResourceObject::setAttribute(std::string&& key,
261 RCSResourceAttributes::Value&& value)
263 setAttributeInternal(std::move(key), std::move(value));
266 RCSResourceAttributes::Value RCSResourceObject::getAttributeValue(
267 const std::string& key) const
269 WeakGuard lock(*this);
270 return m_resourceAttributes.at(key);
273 bool RCSResourceObject::removeAttribute(const std::string& key)
275 bool needToNotify = false;
278 WeakGuard lock(*this);
280 if (m_resourceAttributes.erase(key))
283 needToNotify = lock.hasLocked();
287 if (needToNotify) autoNotify(true);
292 bool RCSResourceObject::containsAttribute(const std::string& key) const
294 WeakGuard lock(*this);
295 return m_resourceAttributes.contains(key);
298 RCSResourceAttributes& RCSResourceObject::getAttributes()
301 return m_resourceAttributes;
304 const RCSResourceAttributes& RCSResourceObject::getAttributes() const
307 return m_resourceAttributes;
310 void RCSResourceObject::expectOwnLock() const
312 if (getLockOwner() != std::this_thread::get_id())
314 throw NoLockException{ "Must acquire the lock first using LockGuard." };
318 std::thread::id RCSResourceObject::getLockOwner() const noexcept
323 void RCSResourceObject::setLockOwner(std::thread::id&& id) const noexcept
325 m_lockOwner->store(std::move(id));
328 bool RCSResourceObject::isObservable() const
330 return ::hasProperty(m_properties, OC_OBSERVABLE);
333 bool RCSResourceObject::isDiscoverable() const
335 return ::hasProperty(m_properties, OC_DISCOVERABLE);
338 void RCSResourceObject::setGetRequestHandler(GetRequestHandler h)
340 m_getRequestHandler = std::move(h);
343 void RCSResourceObject::setSetRequestHandler(SetRequestHandler h)
345 m_setRequestHandler = std::move(h);
348 void RCSResourceObject::notify() const
350 typedef OCStackResult (*NotifyAllObservers)(OCResourceHandle);
352 invokeOCFuncWithResultExpect({ OC_STACK_OK, OC_STACK_NO_OBSERVERS },
353 static_cast< NotifyAllObservers >(OC::OCPlatform::notifyAllObservers),
357 void RCSResourceObject::addAttributeUpdatedListener(const std::string& key,
358 AttributeUpdatedListener h)
360 std::lock_guard< std::mutex > lock(m_mutexAttributeUpdatedListeners);
362 m_attributeUpdatedListeners[key] =
363 std::make_shared< AttributeUpdatedListener >(std::move(h));
366 void RCSResourceObject::addAttributeUpdatedListener(std::string&& key,
367 AttributeUpdatedListener h)
369 std::lock_guard< std::mutex > lock(m_mutexAttributeUpdatedListeners);
371 m_attributeUpdatedListeners[std::move(key)] =
372 std::make_shared< AttributeUpdatedListener >(std::move(h));
375 bool RCSResourceObject::removeAttributeUpdatedListener(const std::string& key)
377 std::lock_guard< std::mutex > lock(m_mutexAttributeUpdatedListeners);
379 return m_attributeUpdatedListeners.erase(key) != 0;
382 bool RCSResourceObject::testValueUpdated(const std::string& key,
383 const RCSResourceAttributes::Value& value) const
385 return m_resourceAttributes.contains(key) == false
386 || m_resourceAttributes.at(key) != value;
389 void RCSResourceObject::setAutoNotifyPolicy(AutoNotifyPolicy policy)
391 m_autoNotifyPolicy = policy;
394 RCSResourceObject::AutoNotifyPolicy RCSResourceObject::getAutoNotifyPolicy() const
396 return m_autoNotifyPolicy;
399 void RCSResourceObject::setSetRequestHandlerPolicy(SetRequestHandlerPolicy policy)
401 m_setRequestHandlerPolicy = policy;
404 auto RCSResourceObject::getSetRequestHandlerPolicy() const -> SetRequestHandlerPolicy
406 return m_setRequestHandlerPolicy;
409 void RCSResourceObject::autoNotify(bool isAttributesChanged) const
411 autoNotify(isAttributesChanged, m_autoNotifyPolicy);
414 void RCSResourceObject::autoNotify(
415 bool isAttributesChanged, AutoNotifyPolicy autoNotifyPolicy) const
417 if(autoNotifyPolicy == AutoNotifyPolicy::NEVER) return;
418 if(autoNotifyPolicy == AutoNotifyPolicy::UPDATED &&
419 isAttributesChanged == false) return;
424 OCEntityHandlerResult RCSResourceObject::entityHandler(
425 std::shared_ptr< OC::OCResourceRequest > request)
427 OC_LOG(WARNING, LOG_TAG, "entityHandler");
435 if (request->getRequestHandlerFlag() & OC::RequestHandlerFlag::RequestFlag)
437 return handleRequest(request);
440 if (request->getRequestHandlerFlag() & OC::RequestHandlerFlag::ObserverFlag)
442 return handleObserve(request);
445 catch (const std::exception& e)
447 OC_LOG_V(WARNING, LOG_TAG, "Failed to handle request : %s", e.what());
452 OC_LOG(WARNING, LOG_TAG, "Failed to handle request.");
459 OCEntityHandlerResult RCSResourceObject::handleRequest(
460 std::shared_ptr< OC::OCResourceRequest > request)
462 assert(request != nullptr);
464 if (request->getRequestType() == "GET")
466 return handleRequestGet(request);
469 if (request->getRequestType() == "PUT")
471 return handleRequestSet(request);
477 OCEntityHandlerResult RCSResourceObject::handleRequestGet(
478 std::shared_ptr< OC::OCResourceRequest > request)
480 assert(request != nullptr);
482 auto attrs = getAttributesFromOCRequest(request);
483 OC_LOG(INFO, "RESOURCE_OBJECT", ("handleRequestGet " + request->getResourceUri()).c_str());
484 return sendResponse(*this, request, invokeHandler(attrs, request, m_getRequestHandler));
487 bool RCSResourceObject::applyAcceptanceMethod(const RCSSetResponse& response,
488 const RCSResourceAttributes& requstAttrs)
490 auto requestHandler = response.getHandler();
492 assert(requestHandler != nullptr);
494 auto replaced = requestHandler->applyAcceptanceMethod(response.getAcceptanceMethod(),
497 OC_LOG_V(WARNING, LOG_TAG, "replaced num %d", replaced.size());
498 for (const auto& attrKeyValPair : replaced)
500 std::shared_ptr< AttributeUpdatedListener > foundListener;
502 std::lock_guard< std::mutex > lock(m_mutexAttributeUpdatedListeners);
504 auto it = m_attributeUpdatedListeners.find(attrKeyValPair.first);
505 if (it != m_attributeUpdatedListeners.end())
507 foundListener = it->second;
513 (*foundListener)(attrKeyValPair.second, requstAttrs.at(attrKeyValPair.first));
517 return !replaced.empty();
520 OCEntityHandlerResult RCSResourceObject::handleRequestSet(
521 std::shared_ptr< OC::OCResourceRequest > request)
523 assert(request != nullptr);
525 auto attrs = getAttributesFromOCRequest(request);
526 auto response = invokeHandler(attrs, request, m_setRequestHandler);
528 auto attrsChanged = applyAcceptanceMethod(response, attrs);
532 autoNotify(attrsChanged, m_autoNotifyPolicy);
533 return sendResponse(*this, request, response);
534 } catch (const RCSPlatformException& e) {
535 OC_LOG_V(ERROR, LOG_TAG, "Error : %s ", e.what());
540 OCEntityHandlerResult RCSResourceObject::handleObserve(
541 std::shared_ptr< OC::OCResourceRequest >)
551 RCSResourceObject::LockGuard::LockGuard(const RCSResourceObject::Ptr ptr) :
552 m_resourceObject(*ptr),
553 m_autoNotifyPolicy{ ptr->getAutoNotifyPolicy() },
554 m_isOwningLock{ false }
559 RCSResourceObject::LockGuard::LockGuard(
560 const RCSResourceObject& serverResource) :
561 m_resourceObject(serverResource),
562 m_autoNotifyPolicy{ serverResource.getAutoNotifyPolicy() },
563 m_isOwningLock{ false }
568 RCSResourceObject::LockGuard::LockGuard(
569 const RCSResourceObject::Ptr ptr, AutoNotifyPolicy autoNotifyPolicy) :
570 m_resourceObject(*ptr),
571 m_autoNotifyPolicy { autoNotifyPolicy },
572 m_isOwningLock{ false }
577 RCSResourceObject::LockGuard::LockGuard(
578 const RCSResourceObject& resourceObject, AutoNotifyPolicy autoNotifyPolicy) :
579 m_resourceObject(resourceObject),
580 m_autoNotifyPolicy { autoNotifyPolicy },
581 m_isOwningLock{ false }
586 RCSResourceObject::LockGuard::~LockGuard()
588 if (m_autoNotifyFunc) m_autoNotifyFunc();
592 m_resourceObject.setLockOwner(std::thread::id{ });
593 m_resourceObject.m_mutex.unlock();
597 void RCSResourceObject::LockGuard::init()
599 if (m_resourceObject.getLockOwner() != std::this_thread::get_id())
601 m_resourceObject.m_mutex.lock();
602 m_resourceObject.setLockOwner(std::this_thread::get_id());
603 m_isOwningLock = true;
605 m_autoNotifyFunc = ::createAutoNotifyInvoker(&RCSResourceObject::autoNotify,
606 m_resourceObject, m_resourceObject.m_resourceAttributes, m_autoNotifyPolicy);
609 RCSResourceObject::WeakGuard::WeakGuard(
610 const RCSResourceObject& resourceObject) :
611 m_isOwningLock{ false },
612 m_resourceObject(resourceObject)
614 if (m_resourceObject.getLockOwner() != std::this_thread::get_id())
616 m_resourceObject.m_mutex.lock();
617 m_resourceObject.setLockOwner(std::this_thread::get_id());
618 m_isOwningLock = true;
622 RCSResourceObject::WeakGuard::~WeakGuard()
626 m_resourceObject.setLockOwner(std::thread::id{ });
627 m_resourceObject.m_mutex.unlock();
631 bool RCSResourceObject::WeakGuard::hasLocked() const
633 return m_isOwningLock;