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 auto it = request->getQueryParameters().find(OC::Key::INTERFACESKEY);
64 if (it == request->getQueryParameters().end()) return false;
66 return it->second == interface;
69 OCEntityHandlerResult sendResponse(const std::shared_ptr< OC::OCResourceRequest >& ocRequest,
70 const std::shared_ptr< OC::OCResourceResponse >& ocResponse)
72 ocResponse->setRequestHandle(ocRequest->getRequestHandle());
73 ocResponse->setResourceHandle(ocRequest->getResourceHandle());
77 if (OC::OCPlatform::sendResponse(ocResponse) == OC_STACK_OK)
82 catch (const OC::OCException& e)
84 OIC_LOG_V(WARNING, LOG_TAG, "Error (%s)", e.what());
91 template <typename RESPONSE>
92 OCEntityHandlerResult sendResponse(RCSResourceObject& resource,
93 const std::shared_ptr< OC::OCResourceRequest >& ocRequest, RESPONSE&& response)
95 return sendResponse(ocRequest, response.getHandler()->buildResponse(resource));
98 RCSResourceAttributes getAttributesFromOCRequest(
99 const std::shared_ptr< OC::OCResourceRequest >& request)
101 return ResourceAttributesConverter::fromOCRepresentation(
102 request->getResourceRepresentation());
105 template< typename HANDLER, typename RESPONSE =
106 typename std::decay<HANDLER>::type::result_type >
107 RESPONSE invokeHandler(const RCSResourceObject::Ptr& resObj, RCSResourceAttributes& attrs,
108 const std::shared_ptr< OC::OCResourceRequest >& ocRequest,
109 std::shared_ptr< HANDLER > handler)
113 return (*handler)(RCSRequest{ resObj, ocRequest }, attrs);
116 return RESPONSE::defaultAction();
119 typedef void (RCSResourceObject::* AutoNotifyFunc)
120 (bool, RCSResourceObject::AutoNotifyPolicy) const;
122 std::function<void()> createAutoNotifyInvoker(AutoNotifyFunc autoNotifyFunc,
123 const RCSResourceObject& resourceObject, const RCSResourceAttributes& resourceAttributes,
124 RCSResourceObject::AutoNotifyPolicy autoNotifyPolicy)
126 if(autoNotifyPolicy == RCSResourceObject::AutoNotifyPolicy::UPDATED)
128 auto&& compareAttributesFunc =
129 std::bind(std::not_equal_to<RCSResourceAttributes>(),
131 std::cref(resourceAttributes));
132 return std::bind(autoNotifyFunc,
133 &resourceObject, std::move(compareAttributesFunc), autoNotifyPolicy);
135 else if(autoNotifyPolicy == RCSResourceObject::AutoNotifyPolicy::ALWAYS)
137 return std::bind(autoNotifyFunc,
138 &resourceObject, true, autoNotifyPolicy);
143 OCEntityHandlerResult handleBatchInterfaceGetRequest(
144 const std::shared_ptr< OC::OCResourceRequest >& request,
145 const RCSResourceObject* resourceObject)
147 RCSRepresentation rcsRep;
149 for (const auto& bound : resourceObject->getBoundResources())
151 rcsRep.addChild(bound->toRepresentation());
154 auto response = std::make_shared< OC::OCResourceResponse >();
156 response->setResponseResult(OC_EH_OK);
157 response->setErrorCode(200);
158 response->setResourceRepresentation(
159 RCSRepresentation::toOCRepresentation(std::move(rcsRep)));
161 return sendResponse(request, response);
164 } // unnamed namespace
171 RCSResourceObject::Builder::Builder(const std::string& uri, const std::string& type,
172 const std::string& interface) :
175 m_interfaces{ interface },
176 m_properties{ OC_DISCOVERABLE | OC_OBSERVABLE },
177 m_resourceAttributes{ }
181 RCSResourceObject::Builder& RCSResourceObject::Builder::addInterface(
182 const std::string& interface)
184 return addInterface(std::string{ interface });
187 RCSResourceObject::Builder& RCSResourceObject::Builder::addInterface(
188 std::string&& interface)
190 m_interfaces.push_back(std::move(interface));
194 RCSResourceObject::Builder& RCSResourceObject::Builder::addType(const std::string& type)
196 return addType(std::string{ type });
199 RCSResourceObject::Builder& RCSResourceObject::Builder::addType(std::string&& type)
201 m_types.push_back(std::move(type));
205 RCSResourceObject::Builder& RCSResourceObject::Builder::setDiscoverable(
208 m_properties = ::makePropertyFlags(m_properties, OC_DISCOVERABLE, discoverable);
212 RCSResourceObject::Builder& RCSResourceObject::Builder::setObservable(
215 m_properties = ::makePropertyFlags(m_properties, OC_OBSERVABLE, observable);
219 RCSResourceObject::Builder& RCSResourceObject::Builder::setSecureFlag(
222 m_properties = ::makePropertyFlags(m_properties, OC_SECURE, secureFlag);
225 RCSResourceObject::Builder& RCSResourceObject::Builder::setAttributes(
226 const RCSResourceAttributes& attrs)
228 m_resourceAttributes = attrs;
232 RCSResourceObject::Builder& RCSResourceObject::Builder::setAttributes(
233 RCSResourceAttributes&& attrs)
235 m_resourceAttributes = std::move(attrs);
239 RCSResourceObject::Ptr RCSResourceObject::Builder::build()
241 OCResourceHandle handle{ nullptr };
243 RCSResourceObject::Ptr server {
244 new RCSResourceObject{ m_uri, m_properties, std::move(m_resourceAttributes) } };
246 OC::EntityHandler entityHandler{ std::bind(&RCSResourceObject::entityHandler,
247 std::weak_ptr< RCSResourceObject >{ server }, std::placeholders::_1) };
249 typedef OCStackResult (*RegisterResource)(OCResourceHandle&, std::string&,
250 const std::string&, const std::string&, OC::EntityHandler, uint8_t);
252 invokeOCFunc(static_cast<RegisterResource>(OC::OCPlatform::registerResource),
253 handle, m_uri, m_types[0], m_interfaces[0], entityHandler, m_properties);
255 std::for_each(m_interfaces.begin() + 1, m_interfaces.end(),
256 [&handle](const std::string& interfaceName){
257 invokeOCFunc(OC::OCPlatform::bindInterfaceToResource, handle, interfaceName);
260 std::for_each(m_types.begin() + 1, m_types.end(),
261 [&handle](const std::string& typeName){
262 invokeOCFunc(OC::OCPlatform::bindTypeToResource, handle, typeName);
265 server->m_resourceHandle = handle;
266 server->m_interfaces = m_interfaces;
267 server->m_types = m_types;
273 RCSResourceObject::RCSResourceObject(const std::string& uri,
274 uint8_t properties, RCSResourceAttributes&& attrs) :
275 m_properties{ properties },
280 m_resourceAttributes{ std::move(attrs) },
281 m_getRequestHandler{ },
282 m_setRequestHandler{ },
283 m_autoNotifyPolicy{ AutoNotifyPolicy::UPDATED },
284 m_setRequestHandlerPolicy{ SetRequestHandlerPolicy::NEVER },
285 m_attributeUpdatedListeners{ },
288 m_mutexAttributeUpdatedListeners{ }
290 m_lockOwner.reset(new AtomicThreadId);
293 RCSResourceObject::~RCSResourceObject()
295 if (m_resourceHandle)
299 OC::OCPlatform::unregisterResource(m_resourceHandle);
303 OIC_LOG(WARNING, LOG_TAG, "Failed to unregister resource.");
308 template< typename K, typename V >
309 void RCSResourceObject::setAttributeInternal(K&& key, V&& value)
311 bool needToNotify = false;
312 bool valueUpdated = false;
315 WeakGuard lock(*this);
317 if (lock.hasLocked())
320 valueUpdated = testValueUpdated(key, value);
323 m_resourceAttributes[std::forward< K >(key)] = std::forward< V >(value);
326 if (needToNotify) autoNotify(valueUpdated);
328 void RCSResourceObject::setAttribute(const std::string& key,
329 const RCSResourceAttributes::Value& value)
331 setAttributeInternal(key, value);
334 void RCSResourceObject::setAttribute(const std::string& key,
335 RCSResourceAttributes::Value&& value)
337 setAttributeInternal(key, std::move(value));
340 void RCSResourceObject::setAttribute(std::string&& key,
341 const RCSResourceAttributes::Value& value)
343 setAttributeInternal(std::move(key), value);
346 void RCSResourceObject::setAttribute(std::string&& key,
347 RCSResourceAttributes::Value&& value)
349 setAttributeInternal(std::move(key), std::move(value));
352 RCSResourceAttributes::Value RCSResourceObject::getAttributeValue(
353 const std::string& key) const
355 WeakGuard lock(*this);
356 return m_resourceAttributes.at(key);
359 bool RCSResourceObject::removeAttribute(const std::string& key)
361 bool needToNotify = false;
364 WeakGuard lock(*this);
366 if (m_resourceAttributes.erase(key))
369 needToNotify = lock.hasLocked();
373 if (needToNotify) autoNotify(true);
378 bool RCSResourceObject::containsAttribute(const std::string& key) const
380 WeakGuard lock(*this);
381 return m_resourceAttributes.contains(key);
384 RCSResourceAttributes& RCSResourceObject::getAttributes()
387 return m_resourceAttributes;
390 const RCSResourceAttributes& RCSResourceObject::getAttributes() const
393 return m_resourceAttributes;
396 void RCSResourceObject::expectOwnLock() const
398 if (getLockOwner() != std::this_thread::get_id())
400 throw NoLockException{ "Must acquire the lock first using LockGuard." };
404 std::thread::id RCSResourceObject::getLockOwner() const noexcept
409 void RCSResourceObject::setLockOwner(std::thread::id&& id) const noexcept
411 m_lockOwner->store(std::move(id));
414 bool RCSResourceObject::isObservable() const
416 return ::hasProperty(m_properties, OC_OBSERVABLE);
419 bool RCSResourceObject::isDiscoverable() const
421 return ::hasProperty(m_properties, OC_DISCOVERABLE);
424 void RCSResourceObject::setGetRequestHandler(GetRequestHandler h)
426 m_getRequestHandler = std::make_shared< GetRequestHandler >(std::move(h));
429 void RCSResourceObject::setSetRequestHandler(SetRequestHandler h)
431 m_setRequestHandler = std::make_shared< SetRequestHandler >(std::move(h));
434 void RCSResourceObject::notify() const
436 typedef OCStackResult (*NotifyAllObservers)(OCResourceHandle);
438 invokeOCFuncWithResultExpect({ OC_STACK_OK, OC_STACK_NO_OBSERVERS },
439 static_cast< NotifyAllObservers >(OC::OCPlatform::notifyAllObservers),
443 void RCSResourceObject::addAttributeUpdatedListener(const std::string& key,
444 AttributeUpdatedListener h)
446 std::lock_guard< std::mutex > lock(m_mutexAttributeUpdatedListeners);
448 m_attributeUpdatedListeners[key] =
449 std::make_shared< AttributeUpdatedListener >(std::move(h));
452 void RCSResourceObject::addAttributeUpdatedListener(std::string&& key,
453 AttributeUpdatedListener h)
455 std::lock_guard< std::mutex > lock(m_mutexAttributeUpdatedListeners);
457 m_attributeUpdatedListeners[std::move(key)] =
458 std::make_shared< AttributeUpdatedListener >(std::move(h));
461 bool RCSResourceObject::removeAttributeUpdatedListener(const std::string& key)
463 std::lock_guard< std::mutex > lock(m_mutexAttributeUpdatedListeners);
465 return m_attributeUpdatedListeners.erase(key) != 0;
468 bool RCSResourceObject::testValueUpdated(const std::string& key,
469 const RCSResourceAttributes::Value& value) const
471 return m_resourceAttributes.contains(key) == false
472 || m_resourceAttributes.at(key) != value;
475 void RCSResourceObject::setAutoNotifyPolicy(AutoNotifyPolicy policy)
477 m_autoNotifyPolicy = policy;
480 RCSResourceObject::AutoNotifyPolicy RCSResourceObject::getAutoNotifyPolicy() const
482 return m_autoNotifyPolicy;
485 void RCSResourceObject::setSetRequestHandlerPolicy(SetRequestHandlerPolicy policy)
487 m_setRequestHandlerPolicy = policy;
490 auto RCSResourceObject::getSetRequestHandlerPolicy() const -> SetRequestHandlerPolicy
492 return m_setRequestHandlerPolicy;
495 void RCSResourceObject::bindResource(const RCSResourceObject::Ptr& resource)
497 if (!resource || resource.get() == this)
499 throw RCSInvalidParameterException("The resource is invalid!");
502 invokeOCFunc(OC::OCPlatform::bindResource,
503 m_resourceHandle, resource->m_resourceHandle);
505 std::lock_guard< std:: mutex > lock{ m_mutexForBoundResources };
506 m_boundResources.push_back(resource);
509 void RCSResourceObject::unbindResource(const RCSResourceObject::Ptr& resource)
511 if (!resource || resource.get() == this)
513 throw RCSInvalidParameterException("The resource is invalid!");
516 invokeOCFunc(OC::OCPlatform::unbindResource,
517 m_resourceHandle, resource->m_resourceHandle);
519 std::lock_guard< std:: mutex > lock{ m_mutexForBoundResources };
520 m_boundResources.erase(std::find(m_boundResources.begin(), m_boundResources.end(),
524 std::vector< RCSResourceObject::Ptr > RCSResourceObject::getBoundResources() const
526 std::lock_guard< std:: mutex > lock{ m_mutexForBoundResources };
527 return m_boundResources;
530 std::vector< std::string > RCSResourceObject::getInterfaces() const
535 std::vector< std::string > RCSResourceObject::getTypes() const
540 RCSRepresentation RCSResourceObject::toRepresentation() const
542 WeakGuard lock{*this};
543 return RCSRepresentation{ m_uri, m_interfaces, m_types, m_resourceAttributes };
546 void RCSResourceObject::autoNotify(bool isAttributesChanged) const
548 autoNotify(isAttributesChanged, m_autoNotifyPolicy);
551 void RCSResourceObject::autoNotify(
552 bool isAttributesChanged, AutoNotifyPolicy autoNotifyPolicy) const
554 if(autoNotifyPolicy == AutoNotifyPolicy::NEVER) return;
555 if(autoNotifyPolicy == AutoNotifyPolicy::UPDATED &&
556 isAttributesChanged == false) return;
561 OCEntityHandlerResult RCSResourceObject::entityHandler(
562 const std::weak_ptr< RCSResourceObject >& weakRes,
563 const std::shared_ptr< OC::OCResourceRequest >& request)
565 auto resource = weakRes.lock();
567 if (!resource) return OC_EH_ERROR;
569 OIC_LOG(WARNING, LOG_TAG, "entityHandler");
577 if (request->getRequestHandlerFlag() & OC::RequestHandlerFlag::RequestFlag)
579 return resource->handleRequest(request);
582 if (request->getRequestHandlerFlag() & OC::RequestHandlerFlag::ObserverFlag)
584 return resource->handleObserve(request);
587 catch (const std::exception& e)
589 OIC_LOG_V(WARNING, LOG_TAG, "Failed to handle request : %s", e.what());
594 OIC_LOG(WARNING, LOG_TAG, "Failed to handle request.");
601 OCEntityHandlerResult RCSResourceObject::handleRequest(
602 const std::shared_ptr< OC::OCResourceRequest >& request)
604 assert(request != nullptr);
606 if (request->getRequestType() == "GET")
608 return handleRequestGet(request);
611 if (request->getRequestType() == "POST")
613 return handleRequestSet(request);
619 OCEntityHandlerResult RCSResourceObject::handleRequestGet(
620 const std::shared_ptr< OC::OCResourceRequest >& request)
622 assert(request != nullptr);
624 if (requestContainsInterface(request, OC::BATCH_INTERFACE))
626 return handleBatchInterfaceGetRequest(request, this);
629 auto attrs = getAttributesFromOCRequest(request);
631 auto response = invokeHandler(shared_from_this(), attrs, request, m_getRequestHandler);
633 if (response.isSeparate()) return OC_EH_SLOW;
635 return sendResponse(*this, request, response);
638 bool RCSResourceObject::applyAcceptanceMethod(const RCSSetResponse& response,
639 const RCSResourceAttributes& requstAttrs)
641 auto requestHandler = response.getHandler();
643 assert(requestHandler != nullptr);
645 auto replaced = requestHandler->applyAcceptanceMethod(response.getAcceptanceMethod(),
648 OIC_LOG_V(WARNING, LOG_TAG, "replaced num %zu", replaced.size());
649 for (const auto& attrKeyValPair : replaced)
651 std::shared_ptr< AttributeUpdatedListener > foundListener;
653 std::lock_guard< std::mutex > lock(m_mutexAttributeUpdatedListeners);
655 auto it = m_attributeUpdatedListeners.find(attrKeyValPair.first);
656 if (it != m_attributeUpdatedListeners.end())
658 foundListener = it->second;
664 (*foundListener)(attrKeyValPair.second, requstAttrs.at(attrKeyValPair.first));
668 return !replaced.empty();
671 OCEntityHandlerResult RCSResourceObject::handleRequestSet(
672 const std::shared_ptr< OC::OCResourceRequest >& request)
674 assert(request != nullptr);
676 auto attrs = getAttributesFromOCRequest(request);
677 auto response = invokeHandler(shared_from_this(), attrs, request, m_setRequestHandler);
679 if (response.isSeparate()) return OC_EH_SLOW;
681 auto attrsChanged = applyAcceptanceMethod(response, attrs);
685 autoNotify(attrsChanged, m_autoNotifyPolicy);
686 return sendResponse(*this, request, response);
687 } catch (const RCSPlatformException& e) {
688 OIC_LOG_V(ERROR, LOG_TAG, "Error : %s ", e.what());
693 OCEntityHandlerResult RCSResourceObject::handleObserve(
694 const std::shared_ptr< OC::OCResourceRequest >&)
704 RCSResourceObject::LockGuard::LockGuard(const RCSResourceObject::Ptr ptr) :
705 m_resourceObject(*ptr),
706 m_autoNotifyPolicy{ ptr->getAutoNotifyPolicy() },
707 m_isOwningLock{ false }
712 RCSResourceObject::LockGuard::LockGuard(
713 const RCSResourceObject& serverResource) :
714 m_resourceObject(serverResource),
715 m_autoNotifyPolicy{ serverResource.getAutoNotifyPolicy() },
716 m_isOwningLock{ false }
721 RCSResourceObject::LockGuard::LockGuard(
722 const RCSResourceObject::Ptr ptr, AutoNotifyPolicy autoNotifyPolicy) :
723 m_resourceObject(*ptr),
724 m_autoNotifyPolicy { autoNotifyPolicy },
725 m_isOwningLock{ false }
730 RCSResourceObject::LockGuard::LockGuard(
731 const RCSResourceObject& resourceObject, AutoNotifyPolicy autoNotifyPolicy) :
732 m_resourceObject(resourceObject),
733 m_autoNotifyPolicy { autoNotifyPolicy },
734 m_isOwningLock{ false }
739 RCSResourceObject::LockGuard::~LockGuard() noexcept(false)
741 if (!std::uncaught_exception() && m_autoNotifyFunc) m_autoNotifyFunc();
745 m_resourceObject.setLockOwner(std::thread::id{ });
746 m_resourceObject.m_mutex.unlock();
750 void RCSResourceObject::LockGuard::init()
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;
758 m_autoNotifyFunc = ::createAutoNotifyInvoker(&RCSResourceObject::autoNotify,
759 m_resourceObject, m_resourceObject.m_resourceAttributes, m_autoNotifyPolicy);
762 RCSResourceObject::WeakGuard::WeakGuard(
763 const RCSResourceObject& resourceObject) :
764 m_isOwningLock{ false },
765 m_resourceObject(resourceObject)
767 if (m_resourceObject.getLockOwner() != std::this_thread::get_id())
769 m_resourceObject.m_mutex.lock();
770 m_resourceObject.setLockOwner(std::this_thread::get_id());
771 m_isOwningLock = true;
775 RCSResourceObject::WeakGuard::~WeakGuard()
779 m_resourceObject.setLockOwner(std::thread::id{ });
780 m_resourceObject.m_mutex.unlock();
784 bool RCSResourceObject::WeakGuard::hasLocked() const
786 return m_isOwningLock;