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"
26 #include "RequestHandler.h"
27 #include "AssertUtils.h"
28 #include "AtomicHelper.h"
29 #include "ResourceAttributesConverter.h"
30 #include "ResourceAttributesUtils.h"
31 #include "RCSRequest.h"
32 #include "RCSRepresentation.h"
33 #include "InterfaceHandler.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 OCEntityHandlerResult sendResponse(const std::shared_ptr< OC::OCResourceRequest >& ocRequest,
60 const std::shared_ptr< OC::OCResourceResponse >& ocResponse)
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 OIC_LOG_V(WARNING, LOG_TAG, "Error (%s)", e.what());
80 RCSResourceAttributes getAttributesFromOCRequest(
81 const 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(const RCSResourceObject::Ptr& resObj, RCSResourceAttributes& attrs,
90 const std::shared_ptr< OC::OCResourceRequest >& ocRequest,
91 std::shared_ptr< HANDLER > handler)
95 return (*handler)(RCSRequest{ resObj, ocRequest }, attrs);
98 return RESPONSE::defaultAction();
101 typedef void (RCSResourceObject::* AutoNotifyFunc)
102 (bool, RCSResourceObject::AutoNotifyPolicy) const;
104 std::function<void()> createAutoNotifyInvoker(AutoNotifyFunc autoNotifyFunc,
105 const RCSResourceObject& resourceObject,
106 const RCSResourceAttributes& resourceAttributes,
107 RCSResourceObject::AutoNotifyPolicy autoNotifyPolicy)
109 if(autoNotifyPolicy == RCSResourceObject::AutoNotifyPolicy::UPDATED)
111 auto&& compareAttributesFunc =
112 std::bind(std::not_equal_to<RCSResourceAttributes>(),
114 std::cref(resourceAttributes));
115 return std::bind(autoNotifyFunc,
116 &resourceObject, std::move(compareAttributesFunc), autoNotifyPolicy);
118 else if(autoNotifyPolicy == RCSResourceObject::AutoNotifyPolicy::ALWAYS)
120 return std::bind(autoNotifyFunc,
121 &resourceObject, true, autoNotifyPolicy);
126 } // unnamed namespace
133 RCSResourceObject::Builder::Builder(const std::string& uri, const std::string& type,
134 const std::string& interface) :
137 m_interfaces{ interface },
138 m_defaultInterface { BASELINE_INTERFACE },
139 m_properties{ OC_DISCOVERABLE | OC_OBSERVABLE },
140 m_resourceAttributes{ }
144 RCSResourceObject::Builder& RCSResourceObject::Builder::addInterface(
145 const std::string& interface)
147 return addInterface(std::string{ interface });
150 RCSResourceObject::Builder& RCSResourceObject::Builder::addInterface(
151 std::string&& interface)
153 m_interfaces.push_back(std::move(interface));
157 RCSResourceObject::Builder& RCSResourceObject::Builder::addType(const std::string& type)
159 return addType(std::string{ type });
162 RCSResourceObject::Builder& RCSResourceObject::Builder::addType(std::string&& type)
164 m_types.push_back(std::move(type));
168 RCSResourceObject::Builder& RCSResourceObject::Builder::setDefaultInterface(
169 std::string interface)
171 if (std::find(m_interfaces.begin(), m_interfaces.end(), interface) ==
174 throw RCSBadRequestException{"The interface should be added, first."};
177 m_defaultInterface = std::move(interface);
182 RCSResourceObject::Builder& RCSResourceObject::Builder::setDiscoverable(
185 m_properties = ::makePropertyFlags(m_properties, OC_DISCOVERABLE, discoverable);
189 RCSResourceObject::Builder& RCSResourceObject::Builder::setObservable(
192 m_properties = ::makePropertyFlags(m_properties, OC_OBSERVABLE, observable);
196 RCSResourceObject::Builder& RCSResourceObject::Builder::setSecureFlag(
199 m_properties = ::makePropertyFlags(m_properties, OC_SECURE, secureFlag);
202 RCSResourceObject::Builder& RCSResourceObject::Builder::setAttributes(
203 const RCSResourceAttributes& attrs)
205 m_resourceAttributes = attrs;
209 RCSResourceObject::Builder& RCSResourceObject::Builder::setAttributes(
210 RCSResourceAttributes&& attrs)
212 m_resourceAttributes = std::move(attrs);
216 RCSResourceObject::Ptr RCSResourceObject::Builder::build()
218 OCResourceHandle handle{ nullptr };
220 RCSResourceObject::Ptr server {
221 new RCSResourceObject{ m_uri, m_properties, std::move(m_resourceAttributes) } };
223 OC::EntityHandler entityHandler{ std::bind(&RCSResourceObject::entityHandler,
224 std::weak_ptr< RCSResourceObject >{ server }, std::placeholders::_1) };
226 typedef OCStackResult (*RegisterResource)(OCResourceHandle&, std::string&,
227 const std::string&, const std::string&, OC::EntityHandler, uint8_t);
229 invokeOCFunc(static_cast<RegisterResource>(OC::OCPlatform::registerResource),
230 handle, m_uri, m_types[0], m_interfaces[0], entityHandler, m_properties);
232 std::for_each(m_interfaces.begin() + 1, m_interfaces.end(),
233 [&handle](const std::string& interfaceName){
234 invokeOCFunc(OC::OCPlatform::bindInterfaceToResource, handle, interfaceName);
237 std::for_each(m_types.begin() + 1, m_types.end(),
238 [&handle](const std::string& typeName){
239 invokeOCFunc(OC::OCPlatform::bindTypeToResource, handle, typeName);
242 server->init(handle, m_interfaces, m_types, m_defaultInterface);
248 RCSResourceObject::RCSResourceObject(const std::string& uri,
249 uint8_t properties, RCSResourceAttributes&& attrs) :
250 m_properties{ properties },
254 m_defaultInterface{ },
256 m_resourceAttributes{ std::move(attrs) },
257 m_getRequestHandler{ },
258 m_setRequestHandler{ },
259 m_autoNotifyPolicy{ AutoNotifyPolicy::UPDATED },
260 m_setRequestHandlerPolicy{ SetRequestHandlerPolicy::NEVER },
261 m_attributeUpdatedListeners{ },
264 m_mutexAttributeUpdatedListeners{ }
266 m_lockOwner.reset(new AtomicThreadId);
269 void RCSResourceObject::init(OCResourceHandle handle,
270 const std::vector< std::string >& interfaces,
271 const std::vector< std::string >& types,
272 const std::string& defaultInterface)
274 m_resourceHandle = handle;
275 m_interfaces = interfaces;
277 m_defaultInterface = defaultInterface;
279 for (const auto& itf : interfaces)
281 m_interfaceHandlers.insert({ itf, getDefaultInterfaceHandler(itf,
282 m_defaultInterface) });
286 RCSResourceObject::~RCSResourceObject()
288 if (m_resourceHandle)
292 OC::OCPlatform::unregisterResource(m_resourceHandle);
296 OIC_LOG(WARNING, LOG_TAG, "Failed to unregister resource.");
301 template< typename K, typename V >
302 void RCSResourceObject::setAttributeInternal(K&& key, V&& value)
304 bool needToNotify = false;
305 bool valueUpdated = false;
308 WeakGuard lock(*this);
310 if (lock.hasLocked())
313 valueUpdated = testValueUpdated(key, value);
316 m_resourceAttributes[std::forward< K >(key)] = std::forward< V >(value);
319 if (needToNotify) autoNotify(valueUpdated);
321 void RCSResourceObject::setAttribute(const std::string& key,
322 const RCSResourceAttributes::Value& value)
324 setAttributeInternal(key, value);
327 void RCSResourceObject::setAttribute(const std::string& key,
328 RCSResourceAttributes::Value&& value)
330 setAttributeInternal(key, std::move(value));
333 void RCSResourceObject::setAttribute(std::string&& key,
334 const RCSResourceAttributes::Value& value)
336 setAttributeInternal(std::move(key), value);
339 void RCSResourceObject::setAttribute(std::string&& key,
340 RCSResourceAttributes::Value&& value)
342 setAttributeInternal(std::move(key), std::move(value));
345 RCSResourceAttributes::Value RCSResourceObject::getAttributeValue(
346 const std::string& key) const
348 WeakGuard lock(*this);
349 return m_resourceAttributes.at(key);
352 bool RCSResourceObject::removeAttribute(const std::string& key)
354 bool needToNotify = false;
357 WeakGuard lock(*this);
359 if (m_resourceAttributes.erase(key))
362 needToNotify = lock.hasLocked();
366 if (needToNotify) autoNotify(true);
371 bool RCSResourceObject::containsAttribute(const std::string& key) const
373 WeakGuard lock(*this);
374 return m_resourceAttributes.contains(key);
377 RCSResourceAttributes& RCSResourceObject::getAttributes()
380 return m_resourceAttributes;
383 const RCSResourceAttributes& RCSResourceObject::getAttributes() const
386 return m_resourceAttributes;
389 void RCSResourceObject::expectOwnLock() const
391 if (getLockOwner() != std::this_thread::get_id())
393 throw NoLockException{ "Must acquire the lock first using LockGuard." };
397 std::thread::id RCSResourceObject::getLockOwner() const noexcept
402 void RCSResourceObject::setLockOwner(std::thread::id&& id) const noexcept
404 m_lockOwner->store(std::move(id));
407 bool RCSResourceObject::isObservable() const
409 return ::hasProperty(m_properties, OC_OBSERVABLE);
412 bool RCSResourceObject::isDiscoverable() const
414 return ::hasProperty(m_properties, OC_DISCOVERABLE);
417 void RCSResourceObject::setGetRequestHandler(GetRequestHandler h)
419 m_getRequestHandler = std::make_shared< GetRequestHandler >(std::move(h));
422 void RCSResourceObject::setSetRequestHandler(SetRequestHandler h)
424 m_setRequestHandler = std::make_shared< SetRequestHandler >(std::move(h));
427 void RCSResourceObject::notify() const
429 typedef OCStackResult (*NotifyAllObservers)(OCResourceHandle);
431 invokeOCFuncWithResultExpect({ OC_STACK_OK, OC_STACK_NO_OBSERVERS },
432 static_cast< NotifyAllObservers >(OC::OCPlatform::notifyAllObservers),
436 void RCSResourceObject::addAttributeUpdatedListener(const std::string& key,
437 AttributeUpdatedListener h)
439 std::lock_guard< std::mutex > lock(m_mutexAttributeUpdatedListeners);
441 m_attributeUpdatedListeners[key] =
442 std::make_shared< AttributeUpdatedListener >(std::move(h));
445 void RCSResourceObject::addAttributeUpdatedListener(std::string&& key,
446 AttributeUpdatedListener h)
448 std::lock_guard< std::mutex > lock(m_mutexAttributeUpdatedListeners);
450 m_attributeUpdatedListeners[std::move(key)] =
451 std::make_shared< AttributeUpdatedListener >(std::move(h));
454 bool RCSResourceObject::removeAttributeUpdatedListener(const std::string& key)
456 std::lock_guard< std::mutex > lock(m_mutexAttributeUpdatedListeners);
458 return m_attributeUpdatedListeners.erase(key) != 0;
461 bool RCSResourceObject::testValueUpdated(const std::string& key,
462 const RCSResourceAttributes::Value& value) const
464 return m_resourceAttributes.contains(key) == false
465 || m_resourceAttributes.at(key) != value;
468 void RCSResourceObject::setAutoNotifyPolicy(AutoNotifyPolicy policy)
470 m_autoNotifyPolicy = policy;
473 RCSResourceObject::AutoNotifyPolicy RCSResourceObject::getAutoNotifyPolicy() const
475 return m_autoNotifyPolicy;
478 void RCSResourceObject::setSetRequestHandlerPolicy(SetRequestHandlerPolicy policy)
480 m_setRequestHandlerPolicy = policy;
483 auto RCSResourceObject::getSetRequestHandlerPolicy() const -> SetRequestHandlerPolicy
485 return m_setRequestHandlerPolicy;
488 void RCSResourceObject::bindResource(const RCSResourceObject::Ptr& resource)
490 if (!resource || resource.get() == this)
492 throw RCSInvalidParameterException("The resource is invalid!");
495 invokeOCFunc(OC::OCPlatform::bindResource,
496 m_resourceHandle, resource->m_resourceHandle);
498 std::lock_guard< std:: mutex > lock{ m_mutexForBoundResources };
499 m_boundResources.push_back(resource);
502 void RCSResourceObject::unbindResource(const RCSResourceObject::Ptr& resource)
504 if (!resource || resource.get() == this)
506 throw RCSInvalidParameterException("The resource is invalid!");
509 invokeOCFunc(OC::OCPlatform::unbindResource,
510 m_resourceHandle, resource->m_resourceHandle);
512 std::lock_guard< std:: mutex > lock{ m_mutexForBoundResources };
513 m_boundResources.erase(std::find(m_boundResources.begin(), m_boundResources.end(),
517 std::vector< RCSResourceObject::Ptr > RCSResourceObject::getBoundResources() const
519 std::lock_guard< std:: mutex > lock{ m_mutexForBoundResources };
520 return m_boundResources;
523 std::string RCSResourceObject::getUri() const
528 std::string RCSResourceObject::getDefaultInterface() const
530 return m_defaultInterface;
533 std::vector< std::string > RCSResourceObject::getInterfaces() const
538 std::vector< std::string > RCSResourceObject::getTypes() const
543 RCSRepresentation RCSResourceObject::toRepresentation() const
545 WeakGuard lock{*this};
546 return RCSRepresentation{ m_uri, m_interfaces, m_types, m_resourceAttributes };
549 void RCSResourceObject::autoNotify(bool isAttributesChanged) const
551 autoNotify(isAttributesChanged, m_autoNotifyPolicy);
554 void RCSResourceObject::autoNotify(
555 bool isAttributesChanged, AutoNotifyPolicy autoNotifyPolicy) const
557 if(autoNotifyPolicy == AutoNotifyPolicy::NEVER) return;
558 if(autoNotifyPolicy == AutoNotifyPolicy::UPDATED &&
559 isAttributesChanged == false) return;
564 OCEntityHandlerResult RCSResourceObject::entityHandler(
565 const std::weak_ptr< RCSResourceObject >& weakRes,
566 const std::shared_ptr< OC::OCResourceRequest >& request)
568 auto resource = weakRes.lock();
570 if (!resource) return OC_EH_ERROR;
572 OIC_LOG(WARNING, LOG_TAG, "entityHandler");
580 if (request->getRequestHandlerFlag() & OC::RequestHandlerFlag::RequestFlag)
582 return resource->handleRequest(request);
585 if (request->getRequestHandlerFlag() & OC::RequestHandlerFlag::ObserverFlag)
587 return resource->handleObserve(request);
590 catch (const std::exception& e)
592 OIC_LOG_V(WARNING, LOG_TAG, "Failed to handle request : %s", e.what());
597 OIC_LOG(WARNING, LOG_TAG, "Failed to handle request.");
604 OCEntityHandlerResult RCSResourceObject::handleRequest(
605 const std::shared_ptr< OC::OCResourceRequest >& request)
607 assert(request != nullptr);
609 RCSRequest rcsRequest{ shared_from_this(), request };
611 if (rcsRequest.getInterface() != "" && m_interfaceHandlers.find(
612 rcsRequest.getInterface()) == m_interfaceHandlers.end())
617 if (request->getRequestType() == "GET")
619 return handleRequestGet(rcsRequest);
622 if (request->getRequestType() == "POST")
624 return handleRequestSet(rcsRequest);
630 OCEntityHandlerResult RCSResourceObject::handleRequestGet(
631 const RCSRequest& request)
633 if (!findInterfaceHandler(request.getInterface()).isGetSupported())
638 auto attrs = getAttributesFromOCRequest(request.getOCRequest());
640 auto response = invokeHandler(shared_from_this(), attrs, request.getOCRequest(),
641 m_getRequestHandler);
643 if (response.isSeparate()) return OC_EH_SLOW;
645 return sendResponse(request, response,
646 findInterfaceHandler(request.getInterface()).getGetResponseBuilder());
649 RCSResourceAttributes RCSResourceObject::applyAcceptanceMethod(
650 const RCSSetResponse& response, const RCSResourceAttributes& requestAttrs)
652 auto requestHandler = response.getHandler();
654 assert(requestHandler != nullptr);
656 RCSResourceAttributes result;
658 auto replaced = requestHandler->applyAcceptanceMethod(response.getAcceptanceMethod(),
659 *this, requestAttrs);
661 OIC_LOG_V(WARNING, LOG_TAG, "replaced num %zu", replaced.size());
662 for (const auto& attrKeyValPair : replaced)
664 std::shared_ptr< AttributeUpdatedListener > foundListener;
666 std::lock_guard< std::mutex > lock(m_mutexAttributeUpdatedListeners);
668 auto it = m_attributeUpdatedListeners.find(attrKeyValPair.first);
669 if (it != m_attributeUpdatedListeners.end())
671 foundListener = it->second;
677 (*foundListener)(attrKeyValPair.second, requestAttrs.at(attrKeyValPair.first));
680 result[attrKeyValPair.first] = attrKeyValPair.second;
686 OCEntityHandlerResult RCSResourceObject::handleRequestSet(
687 const RCSRequest& request)
689 if (!findInterfaceHandler(request.getInterface()).isSetSupported())
694 auto attrs = getAttributesFromOCRequest(request.getOCRequest());
696 auto response = invokeHandler(shared_from_this(), attrs, request.getOCRequest(),
697 m_setRequestHandler);
699 if (response.isSeparate()) return OC_EH_SLOW;
701 auto replaced = applyAcceptanceMethod(response, attrs);
703 autoNotify(!replaced.empty(), m_autoNotifyPolicy);
705 return sendResponse(request, response,
706 findInterfaceHandler(request.getInterface()).getSetResponseBuilder());
709 OCEntityHandlerResult RCSResourceObject::handleObserve(
710 const std::shared_ptr< OC::OCResourceRequest >&)
720 InterfaceHandler RCSResourceObject::findInterfaceHandler(
721 const std::string& interfaceName) const
723 auto it = m_interfaceHandlers.find(interfaceName);
725 if (it != m_interfaceHandlers.end()) return it->second;
727 assert(m_interfaceHandlers.find(m_defaultInterface) != m_interfaceHandlers.end());
729 return m_interfaceHandlers.find(m_defaultInterface)->second;
732 template <typename RESPONSE, typename RESPONSE_BUILDER>
733 OCEntityHandlerResult RCSResourceObject::sendResponse(
734 const RCSRequest& request, const RESPONSE& response,
735 const RESPONSE_BUILDER& resBuilder)
737 auto reqHandler = response.getHandler();
738 auto ocResponse = std::make_shared< OC::OCResourceResponse >();
740 ocResponse->setResponseResult(OC_EH_OK);
741 ocResponse->setErrorCode(reqHandler->getErrorCode());
743 if (reqHandler->hasCustomRepresentation())
745 ocResponse->setResourceRepresentation(reqHandler->getRepresentation());
749 ocResponse->setResourceRepresentation(
750 RCSRepresentation::toOCRepresentation(
751 resBuilder(request, *this)));
754 return ::sendResponse(request.getOCRequest(), ocResponse);
758 RCSResourceObject::LockGuard::LockGuard(const RCSResourceObject::Ptr ptr) :
759 m_resourceObject(*ptr),
760 m_autoNotifyPolicy{ ptr->getAutoNotifyPolicy() },
761 m_isOwningLock{ false }
766 RCSResourceObject::LockGuard::LockGuard(
767 const RCSResourceObject& serverResource) :
768 m_resourceObject(serverResource),
769 m_autoNotifyPolicy{ serverResource.getAutoNotifyPolicy() },
770 m_isOwningLock{ false }
775 RCSResourceObject::LockGuard::LockGuard(
776 const RCSResourceObject::Ptr ptr, AutoNotifyPolicy autoNotifyPolicy) :
777 m_resourceObject(*ptr),
778 m_autoNotifyPolicy { autoNotifyPolicy },
779 m_isOwningLock{ false }
784 RCSResourceObject::LockGuard::LockGuard(
785 const RCSResourceObject& resourceObject, AutoNotifyPolicy autoNotifyPolicy) :
786 m_resourceObject(resourceObject),
787 m_autoNotifyPolicy { autoNotifyPolicy },
788 m_isOwningLock{ false }
793 RCSResourceObject::LockGuard::~LockGuard() noexcept(false)
795 if (!std::uncaught_exception() && m_autoNotifyFunc) m_autoNotifyFunc();
799 m_resourceObject.setLockOwner(std::thread::id{ });
800 m_resourceObject.m_mutex.unlock();
804 void RCSResourceObject::LockGuard::init()
806 if (m_resourceObject.getLockOwner() != std::this_thread::get_id())
808 m_resourceObject.m_mutex.lock();
809 m_resourceObject.setLockOwner(std::this_thread::get_id());
810 m_isOwningLock = true;
812 m_autoNotifyFunc = ::createAutoNotifyInvoker(&RCSResourceObject::autoNotify,
813 m_resourceObject, m_resourceObject.m_resourceAttributes, m_autoNotifyPolicy);
816 RCSResourceObject::WeakGuard::WeakGuard(
817 const RCSResourceObject& resourceObject) :
818 m_isOwningLock{ false },
819 m_resourceObject(resourceObject)
821 if (m_resourceObject.getLockOwner() != std::this_thread::get_id())
823 m_resourceObject.m_mutex.lock();
824 m_resourceObject.setLockOwner(std::this_thread::get_id());
825 m_isOwningLock = true;
829 RCSResourceObject::WeakGuard::~WeakGuard()
833 m_resourceObject.setLockOwner(std::thread::id{ });
834 m_resourceObject.m_mutex.unlock();
838 bool RCSResourceObject::WeakGuard::hasLocked() const
840 return m_isOwningLock;