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"
32 #include "RCSRequest.h"
33 #include "RCSRepresentation.h"
36 #include "OCPlatform.h"
38 #define LOG_TAG "RCSResourceObject"
42 using namespace OIC::Service;
44 inline bool hasProperty(uint8_t base, uint8_t target)
46 return (base & target) == target;
49 inline uint8_t makePropertyFlags(uint8_t base, uint8_t target, bool add)
56 return base & ~target;
59 inline bool requestContainsInterface(const std::shared_ptr< OC::OCResourceRequest >& request,
60 const std::string& interface)
62 return request->getQueryParameters().find(interface) != request->getQueryParameters().end();
65 OCEntityHandlerResult sendResponse(const std::shared_ptr< OC::OCResourceRequest >& ocRequest,
66 const std::shared_ptr< OC::OCResourceResponse >& ocResponse)
68 ocResponse->setRequestHandle(ocRequest->getRequestHandle());
69 ocResponse->setResourceHandle(ocRequest->getResourceHandle());
73 if (OC::OCPlatform::sendResponse(ocResponse) == OC_STACK_OK)
78 catch (const OC::OCException& e)
80 OIC_LOG_V(WARNING, LOG_TAG, "Error (%s)", e.what());
87 template <typename RESPONSE>
88 OCEntityHandlerResult sendResponse(RCSResourceObject& resource,
89 const std::shared_ptr< OC::OCResourceRequest >& ocRequest, RESPONSE&& response)
91 return sendResponse(ocRequest, response.getHandler()->buildResponse(resource));
94 RCSResourceAttributes getAttributesFromOCRequest(
95 const std::shared_ptr< OC::OCResourceRequest >& request)
97 return ResourceAttributesConverter::fromOCRepresentation(
98 request->getResourceRepresentation());
101 template< typename HANDLER, typename RESPONSE =
102 typename std::decay<HANDLER>::type::result_type >
103 RESPONSE invokeHandler(RCSResourceAttributes& attrs,
104 const std::shared_ptr< OC::OCResourceRequest >& ocRequest,
105 std::shared_ptr< HANDLER > handler)
109 return (*handler)(RCSRequest{ ocRequest->getResourceUri() }, attrs);
112 return RESPONSE::defaultAction();
115 typedef void (RCSResourceObject::* AutoNotifyFunc)
116 (bool, RCSResourceObject::AutoNotifyPolicy) const;
118 std::function<void()> createAutoNotifyInvoker(AutoNotifyFunc autoNotifyFunc,
119 const RCSResourceObject& resourceObject, const RCSResourceAttributes& resourceAttributes,
120 RCSResourceObject::AutoNotifyPolicy autoNotifyPolicy)
122 if(autoNotifyPolicy == RCSResourceObject::AutoNotifyPolicy::UPDATED)
124 auto&& compareAttributesFunc =
125 std::bind(std::not_equal_to<RCSResourceAttributes>(),
127 std::cref(resourceAttributes));
128 return std::bind(autoNotifyFunc,
129 &resourceObject, std::move(compareAttributesFunc), autoNotifyPolicy);
131 else if(autoNotifyPolicy == RCSResourceObject::AutoNotifyPolicy::ALWAYS)
133 return std::bind(autoNotifyFunc,
134 &resourceObject, true, autoNotifyPolicy);
139 OCEntityHandlerResult handleBatchInterfaceGetRequest(
140 const std::shared_ptr< OC::OCResourceRequest >& request,
141 const RCSResourceObject* resourceObject)
143 auto rcsRep = resourceObject->toRepresentation();
145 for (const auto& bound : resourceObject->getBoundResources())
147 rcsRep.addChild(bound->toRepresentation());
150 auto response = std::make_shared< OC::OCResourceResponse >();
152 response->setResponseResult(OC_EH_OK);
153 response->setErrorCode(200);
154 response->setResourceRepresentation(
155 RCSRepresentation::toOCRepresentation(std::move(rcsRep)));
157 return sendResponse(request, response);
160 } // unnamed namespace
167 RCSResourceObject::Builder::Builder(const std::string& uri, const std::string& type,
168 const std::string& interface) :
171 m_interfaces{ interface },
172 m_properties{ OC_DISCOVERABLE | OC_OBSERVABLE },
173 m_resourceAttributes{ }
177 RCSResourceObject::Builder& RCSResourceObject::Builder::addInterface(
178 const std::string& interface)
180 return addInterface(std::string{ interface });
183 RCSResourceObject::Builder& RCSResourceObject::Builder::addInterface(
184 std::string&& interface)
186 m_interfaces.push_back(std::move(interface));
190 RCSResourceObject::Builder& RCSResourceObject::Builder::addType(const std::string& type)
192 return addType(std::string{ type });
195 RCSResourceObject::Builder& RCSResourceObject::Builder::addType(std::string&& type)
197 m_types.push_back(std::move(type));
201 RCSResourceObject::Builder& RCSResourceObject::Builder::setDiscoverable(
204 m_properties = ::makePropertyFlags(m_properties, OC_DISCOVERABLE, discoverable);
208 RCSResourceObject::Builder& RCSResourceObject::Builder::setObservable(
211 m_properties = ::makePropertyFlags(m_properties, OC_OBSERVABLE, observable);
215 RCSResourceObject::Builder& RCSResourceObject::Builder::setSecureFlag(
218 m_properties = ::makePropertyFlags(m_properties, OC_SECURE, secureFlag);
221 RCSResourceObject::Builder& RCSResourceObject::Builder::setAttributes(
222 const RCSResourceAttributes& attrs)
224 m_resourceAttributes = attrs;
228 RCSResourceObject::Builder& RCSResourceObject::Builder::setAttributes(
229 RCSResourceAttributes&& attrs)
231 m_resourceAttributes = std::move(attrs);
235 RCSResourceObject::Ptr RCSResourceObject::Builder::build()
237 OCResourceHandle handle{ nullptr };
239 RCSResourceObject::Ptr server {
240 new RCSResourceObject{ m_uri, m_properties, std::move(m_resourceAttributes) } };
242 OC::EntityHandler entityHandler{ std::bind(&RCSResourceObject::entityHandler,
243 server.get(), std::placeholders::_1) };
245 typedef OCStackResult (*RegisterResource)(OCResourceHandle&, std::string&,
246 const std::string&, const std::string&, OC::EntityHandler, uint8_t);
248 invokeOCFunc(static_cast<RegisterResource>(OC::OCPlatform::registerResource),
249 handle, m_uri, m_types[0], m_interfaces[0], entityHandler, m_properties);
251 std::for_each(m_interfaces.begin() + 1, m_interfaces.end(),
252 [&handle](const std::string& interfaceName){
253 invokeOCFunc(OC::OCPlatform::bindInterfaceToResource, handle, interfaceName);
256 std::for_each(m_types.begin() + 1, m_types.end(),
257 [&handle](const std::string& typeName){
258 invokeOCFunc(OC::OCPlatform::bindTypeToResource, handle, typeName);
262 server->m_resourceHandle = handle;
263 server->m_interfaces = m_interfaces;
264 server->m_types = m_types;
270 RCSResourceObject::RCSResourceObject(const std::string& uri,
271 uint8_t properties, RCSResourceAttributes&& attrs) :
272 m_properties{ properties },
277 m_resourceAttributes{ std::move(attrs) },
278 m_getRequestHandler{ },
279 m_setRequestHandler{ },
280 m_autoNotifyPolicy{ AutoNotifyPolicy::UPDATED },
281 m_setRequestHandlerPolicy{ SetRequestHandlerPolicy::NEVER },
282 m_attributeUpdatedListeners{ },
285 m_mutexAttributeUpdatedListeners{ }
287 m_lockOwner.reset(new AtomicThreadId);
290 RCSResourceObject::~RCSResourceObject()
292 if (m_resourceHandle)
296 OC::OCPlatform::unregisterResource(m_resourceHandle);
300 OIC_LOG(WARNING, LOG_TAG, "Failed to unregister resource.");
305 template< typename K, typename V >
306 void RCSResourceObject::setAttributeInternal(K&& key, V&& value)
308 bool needToNotify = false;
309 bool valueUpdated = false;
312 WeakGuard lock(*this);
314 if (lock.hasLocked())
317 valueUpdated = testValueUpdated(key, value);
320 m_resourceAttributes[std::forward< K >(key)] = std::forward< V >(value);
323 if (needToNotify) autoNotify(valueUpdated);
325 void RCSResourceObject::setAttribute(const std::string& key,
326 const RCSResourceAttributes::Value& value)
328 setAttributeInternal(key, value);
331 void RCSResourceObject::setAttribute(const std::string& key,
332 RCSResourceAttributes::Value&& value)
334 setAttributeInternal(key, std::move(value));
337 void RCSResourceObject::setAttribute(std::string&& key,
338 const RCSResourceAttributes::Value& value)
340 setAttributeInternal(std::move(key), value);
343 void RCSResourceObject::setAttribute(std::string&& key,
344 RCSResourceAttributes::Value&& value)
346 setAttributeInternal(std::move(key), std::move(value));
349 RCSResourceAttributes::Value RCSResourceObject::getAttributeValue(
350 const std::string& key) const
352 WeakGuard lock(*this);
353 return m_resourceAttributes.at(key);
356 bool RCSResourceObject::removeAttribute(const std::string& key)
358 bool needToNotify = false;
361 WeakGuard lock(*this);
363 if (m_resourceAttributes.erase(key))
366 needToNotify = lock.hasLocked();
370 if (needToNotify) autoNotify(true);
375 bool RCSResourceObject::containsAttribute(const std::string& key) const
377 WeakGuard lock(*this);
378 return m_resourceAttributes.contains(key);
381 RCSResourceAttributes& RCSResourceObject::getAttributes()
384 return m_resourceAttributes;
387 const RCSResourceAttributes& RCSResourceObject::getAttributes() const
390 return m_resourceAttributes;
393 void RCSResourceObject::expectOwnLock() const
395 if (getLockOwner() != std::this_thread::get_id())
397 throw NoLockException{ "Must acquire the lock first using LockGuard." };
401 std::thread::id RCSResourceObject::getLockOwner() const noexcept
406 void RCSResourceObject::setLockOwner(std::thread::id&& id) const noexcept
408 m_lockOwner->store(std::move(id));
411 bool RCSResourceObject::isObservable() const
413 return ::hasProperty(m_properties, OC_OBSERVABLE);
416 bool RCSResourceObject::isDiscoverable() const
418 return ::hasProperty(m_properties, OC_DISCOVERABLE);
421 void RCSResourceObject::setGetRequestHandler(GetRequestHandler h)
423 m_getRequestHandler = std::make_shared< GetRequestHandler >(std::move(h));
426 void RCSResourceObject::setSetRequestHandler(SetRequestHandler h)
428 m_setRequestHandler = std::make_shared< SetRequestHandler >(std::move(h));
431 void RCSResourceObject::notify() const
433 typedef OCStackResult (*NotifyAllObservers)(OCResourceHandle);
435 invokeOCFuncWithResultExpect({ OC_STACK_OK, OC_STACK_NO_OBSERVERS },
436 static_cast< NotifyAllObservers >(OC::OCPlatform::notifyAllObservers),
440 void RCSResourceObject::addAttributeUpdatedListener(const std::string& key,
441 AttributeUpdatedListener h)
443 std::lock_guard< std::mutex > lock(m_mutexAttributeUpdatedListeners);
445 m_attributeUpdatedListeners[key] =
446 std::make_shared< AttributeUpdatedListener >(std::move(h));
449 void RCSResourceObject::addAttributeUpdatedListener(std::string&& key,
450 AttributeUpdatedListener h)
452 std::lock_guard< std::mutex > lock(m_mutexAttributeUpdatedListeners);
454 m_attributeUpdatedListeners[std::move(key)] =
455 std::make_shared< AttributeUpdatedListener >(std::move(h));
458 bool RCSResourceObject::removeAttributeUpdatedListener(const std::string& key)
460 std::lock_guard< std::mutex > lock(m_mutexAttributeUpdatedListeners);
462 return m_attributeUpdatedListeners.erase(key) != 0;
465 bool RCSResourceObject::testValueUpdated(const std::string& key,
466 const RCSResourceAttributes::Value& value) const
468 return m_resourceAttributes.contains(key) == false
469 || m_resourceAttributes.at(key) != value;
472 void RCSResourceObject::setAutoNotifyPolicy(AutoNotifyPolicy policy)
474 m_autoNotifyPolicy = policy;
477 RCSResourceObject::AutoNotifyPolicy RCSResourceObject::getAutoNotifyPolicy() const
479 return m_autoNotifyPolicy;
482 void RCSResourceObject::setSetRequestHandlerPolicy(SetRequestHandlerPolicy policy)
484 m_setRequestHandlerPolicy = policy;
487 auto RCSResourceObject::getSetRequestHandlerPolicy() const -> SetRequestHandlerPolicy
489 return m_setRequestHandlerPolicy;
492 void RCSResourceObject::bindResource(const RCSResourceObject::Ptr& resource)
494 if (!resource || resource.get() == this)
496 throw RCSInvalidParameterException("The resource is invalid!");
499 invokeOCFunc(OC::OCPlatform::bindResource,
500 m_resourceHandle, resource->m_resourceHandle);
502 std::lock_guard< std:: mutex > lock{ m_mutexForBoundResources };
503 m_boundResources.push_back(resource);
506 void RCSResourceObject::unbindResource(const RCSResourceObject::Ptr& resource)
508 if (!resource || resource.get() == this)
510 throw RCSInvalidParameterException("The resource is invalid!");
513 invokeOCFunc(OC::OCPlatform::unbindResource,
514 m_resourceHandle, resource->m_resourceHandle);
516 std::lock_guard< std:: mutex > lock{ m_mutexForBoundResources };
517 m_boundResources.erase(std::find(m_boundResources.begin(), m_boundResources.end(),
521 std::vector< RCSResourceObject::Ptr > RCSResourceObject::getBoundResources() const
523 std::lock_guard< std:: mutex > lock{ m_mutexForBoundResources };
524 return m_boundResources;
527 std::vector< std::string > RCSResourceObject::getInterfaces() const
532 std::vector< std::string > RCSResourceObject::getTypes() const
537 RCSRepresentation RCSResourceObject::toRepresentation() const
539 return RCSRepresentation{ m_uri, m_interfaces, m_types, m_resourceAttributes };
542 void RCSResourceObject::autoNotify(bool isAttributesChanged) const
544 autoNotify(isAttributesChanged, m_autoNotifyPolicy);
547 void RCSResourceObject::autoNotify(
548 bool isAttributesChanged, AutoNotifyPolicy autoNotifyPolicy) const
550 if(autoNotifyPolicy == AutoNotifyPolicy::NEVER) return;
551 if(autoNotifyPolicy == AutoNotifyPolicy::UPDATED &&
552 isAttributesChanged == false) return;
557 OCEntityHandlerResult RCSResourceObject::entityHandler(
558 const std::shared_ptr< OC::OCResourceRequest >& request)
560 OIC_LOG(WARNING, LOG_TAG, "entityHandler");
568 if (request->getRequestHandlerFlag() & OC::RequestHandlerFlag::RequestFlag)
570 return handleRequest(request);
573 if (request->getRequestHandlerFlag() & OC::RequestHandlerFlag::ObserverFlag)
575 return handleObserve(request);
578 catch (const std::exception& e)
580 OIC_LOG_V(WARNING, LOG_TAG, "Failed to handle request : %s", e.what());
585 OIC_LOG(WARNING, LOG_TAG, "Failed to handle request.");
592 OCEntityHandlerResult RCSResourceObject::handleRequest(
593 const std::shared_ptr< OC::OCResourceRequest >& request)
595 assert(request != nullptr);
597 if (request->getRequestType() == "GET")
599 return handleRequestGet(request);
602 if (request->getRequestType() == "POST")
604 return handleRequestSet(request);
610 OCEntityHandlerResult RCSResourceObject::handleRequestGet(
611 const std::shared_ptr< OC::OCResourceRequest >& request)
613 assert(request != nullptr);
615 if (requestContainsInterface(request, OC::BATCH_INTERFACE))
617 return handleBatchInterfaceGetRequest(request, this);
620 auto attrs = getAttributesFromOCRequest(request);
622 return sendResponse(*this, request, invokeHandler(attrs, request, m_getRequestHandler));
625 bool RCSResourceObject::applyAcceptanceMethod(const RCSSetResponse& response,
626 const RCSResourceAttributes& requstAttrs)
628 auto requestHandler = response.getHandler();
630 assert(requestHandler != nullptr);
632 auto replaced = requestHandler->applyAcceptanceMethod(response.getAcceptanceMethod(),
635 OIC_LOG_V(WARNING, LOG_TAG, "replaced num %zu", replaced.size());
636 for (const auto& attrKeyValPair : replaced)
638 std::shared_ptr< AttributeUpdatedListener > foundListener;
640 std::lock_guard< std::mutex > lock(m_mutexAttributeUpdatedListeners);
642 auto it = m_attributeUpdatedListeners.find(attrKeyValPair.first);
643 if (it != m_attributeUpdatedListeners.end())
645 foundListener = it->second;
651 (*foundListener)(attrKeyValPair.second, requstAttrs.at(attrKeyValPair.first));
655 return !replaced.empty();
658 OCEntityHandlerResult RCSResourceObject::handleRequestSet(
659 const std::shared_ptr< OC::OCResourceRequest >& request)
661 assert(request != nullptr);
663 auto attrs = getAttributesFromOCRequest(request);
664 auto response = invokeHandler(attrs, request, m_setRequestHandler);
666 auto attrsChanged = applyAcceptanceMethod(response, attrs);
670 autoNotify(attrsChanged, m_autoNotifyPolicy);
671 return sendResponse(*this, request, response);
672 } catch (const RCSPlatformException& e) {
673 OIC_LOG_V(ERROR, LOG_TAG, "Error : %s ", e.what());
678 OCEntityHandlerResult RCSResourceObject::handleObserve(
679 const std::shared_ptr< OC::OCResourceRequest >&)
689 RCSResourceObject::LockGuard::LockGuard(const RCSResourceObject::Ptr ptr) :
690 m_resourceObject(*ptr),
691 m_autoNotifyPolicy{ ptr->getAutoNotifyPolicy() },
692 m_isOwningLock{ false }
697 RCSResourceObject::LockGuard::LockGuard(
698 const RCSResourceObject& serverResource) :
699 m_resourceObject(serverResource),
700 m_autoNotifyPolicy{ serverResource.getAutoNotifyPolicy() },
701 m_isOwningLock{ false }
706 RCSResourceObject::LockGuard::LockGuard(
707 const RCSResourceObject::Ptr ptr, AutoNotifyPolicy autoNotifyPolicy) :
708 m_resourceObject(*ptr),
709 m_autoNotifyPolicy { autoNotifyPolicy },
710 m_isOwningLock{ false }
715 RCSResourceObject::LockGuard::LockGuard(
716 const RCSResourceObject& resourceObject, AutoNotifyPolicy autoNotifyPolicy) :
717 m_resourceObject(resourceObject),
718 m_autoNotifyPolicy { autoNotifyPolicy },
719 m_isOwningLock{ false }
724 RCSResourceObject::LockGuard::~LockGuard() noexcept(false)
726 if (!std::uncaught_exception() && m_autoNotifyFunc) m_autoNotifyFunc();
730 m_resourceObject.setLockOwner(std::thread::id{ });
731 m_resourceObject.m_mutex.unlock();
735 void RCSResourceObject::LockGuard::init()
737 if (m_resourceObject.getLockOwner() != std::this_thread::get_id())
739 m_resourceObject.m_mutex.lock();
740 m_resourceObject.setLockOwner(std::this_thread::get_id());
741 m_isOwningLock = true;
743 m_autoNotifyFunc = ::createAutoNotifyInvoker(&RCSResourceObject::autoNotify,
744 m_resourceObject, m_resourceObject.m_resourceAttributes, m_autoNotifyPolicy);
747 RCSResourceObject::WeakGuard::WeakGuard(
748 const RCSResourceObject& resourceObject) :
749 m_isOwningLock{ false },
750 m_resourceObject(resourceObject)
752 if (m_resourceObject.getLockOwner() != std::this_thread::get_id())
754 m_resourceObject.m_mutex.lock();
755 m_resourceObject.setLockOwner(std::this_thread::get_id());
756 m_isOwningLock = true;
760 RCSResourceObject::WeakGuard::~WeakGuard()
764 m_resourceObject.setLockOwner(std::thread::id{ });
765 m_resourceObject.m_mutex.unlock();
769 bool RCSResourceObject::WeakGuard::hasLocked() const
771 return m_isOwningLock;