// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. #if !defined (COMMONAPI_INTERNAL_COMPILATION) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef COMMONAPI_DBUS_DBUSSTUBADAPTERHELPER_HPP_ #define COMMONAPI_DBUS_DBUSSTUBADAPTERHELPER_HPP_ #include #include #include #include #include #include #include #include #include #include #include #include namespace CommonAPI { namespace DBus { class StubDispatcherBase { public: virtual ~StubDispatcherBase() { } }; struct DBusAttributeDispatcherStruct { StubDispatcherBase* getter; StubDispatcherBase* setter; DBusAttributeDispatcherStruct(StubDispatcherBase* g, StubDispatcherBase* s) { getter = g; setter = s; } }; typedef std::unordered_map StubAttributeTable; template class DBusStubAdapterHelper: public virtual DBusStubAdapter { public: typedef typename StubClass_::StubAdapterType StubAdapterType; typedef typename StubClass_::RemoteEventHandlerType RemoteEventHandlerType; class StubDispatcher: public StubDispatcherBase { public: virtual ~StubDispatcher() {} virtual bool dispatchDBusMessage(const DBusMessage &_message, const std::shared_ptr &_stub, DBusStubAdapterHelper &_helper) = 0; virtual void appendGetAllReply(const DBusMessage &_message, const std::shared_ptr &_stub, DBusStubAdapterHelper &_helper, DBusOutputStream &_output) { (void)_message; (void)_stub; (void)_helper; (void)_output; } }; // interfaceMemberName, interfaceMemberSignature typedef std::pair DBusInterfaceMemberPath; typedef std::unordered_map StubDispatcherTable; DBusStubAdapterHelper(const DBusAddress &_address, const std::shared_ptr &_connection, const std::shared_ptr &_stub, const bool _isManaging): DBusStubAdapter(_address, _connection, _isManaging), stub_(_stub), remoteEventHandler_(nullptr) { } virtual ~DBusStubAdapterHelper() { DBusStubAdapter::deinit(); stub_.reset(); } virtual void init(std::shared_ptr instance) { DBusStubAdapter::init(instance); std::shared_ptr stubAdapter = std::dynamic_pointer_cast(instance); remoteEventHandler_ = stub_->initStubAdapter(stubAdapter); } virtual void deinit() { DBusStubAdapter::deinit(); stub_.reset(); } inline RemoteEventHandlerType* getRemoteEventHandler() { return remoteEventHandler_; } protected: virtual bool onInterfaceDBusMessage(const DBusMessage& dbusMessage) { const char* interfaceMemberName = dbusMessage.getMember(); const char* interfaceMemberSignature = dbusMessage.getSignature(); assert(interfaceMemberName); assert(interfaceMemberSignature); DBusInterfaceMemberPath dbusInterfaceMemberPath(interfaceMemberName, interfaceMemberSignature); auto findIterator = getStubDispatcherTable().find(dbusInterfaceMemberPath); const bool foundInterfaceMemberHandler = (findIterator != getStubDispatcherTable().end()); bool dbusMessageHandled = false; if (foundInterfaceMemberHandler) { StubDispatcher* stubDispatcher = static_cast(findIterator->second); dbusMessageHandled = stubDispatcher->dispatchDBusMessage(dbusMessage, stub_, *this); } return dbusMessageHandled; } virtual bool onInterfaceDBusFreedesktopPropertiesMessage(const DBusMessage &_message) { DBusInputStream input(_message); if (_message.hasMemberName("Get")) { return handleFreedesktopGet(_message, input); } else if (_message.hasMemberName("Set")) { return handleFreedesktopSet(_message, input); } else if (_message.hasMemberName("GetAll")) { return handleFreedesktopGetAll(_message, input); } return false; } virtual const StubDispatcherTable& getStubDispatcherTable() = 0; virtual const StubAttributeTable& getStubAttributeTable() = 0; std::shared_ptr stub_; RemoteEventHandlerType* remoteEventHandler_; private: bool handleFreedesktopGet(const DBusMessage &_message, DBusInputStream &_input) { std::string interfaceName; std::string attributeName; _input >> interfaceName; _input >> attributeName; if (_input.hasError()) { return false; } auto attributeDispatcherIterator = getStubAttributeTable().find(attributeName); if (attributeDispatcherIterator == getStubAttributeTable().end()) { return false; } StubDispatcher* getterDispatcher = static_cast(attributeDispatcherIterator->second.getter); assert(getterDispatcher != NULL); // all attributes have at least a getter return (getterDispatcher->dispatchDBusMessage(_message, stub_, *this)); } bool handleFreedesktopSet(const DBusMessage& dbusMessage, DBusInputStream& dbusInputStream) { std::string interfaceName; std::string attributeName; dbusInputStream >> interfaceName; dbusInputStream >> attributeName; if(dbusInputStream.hasError()) { return false; } auto attributeDispatcherIterator = getStubAttributeTable().find(attributeName); if(attributeDispatcherIterator == getStubAttributeTable().end()) { return false; } StubDispatcher *setterDispatcher = static_cast(attributeDispatcherIterator->second.setter); if (setterDispatcher == NULL) { // readonly attributes do not have a setter return false; } return setterDispatcher->dispatchDBusMessage(dbusMessage, stub_, *this); } bool handleFreedesktopGetAll(const DBusMessage& dbusMessage, DBusInputStream& dbusInputStream) { std::string interfaceName; dbusInputStream >> interfaceName; if(dbusInputStream.hasError()) { return false; } DBusMessage dbusMessageReply = dbusMessage.createMethodReturn("a{sv}"); DBusOutputStream dbusOutputStream(dbusMessageReply); dbusOutputStream.beginWriteMap(); std::shared_ptr clientId = std::make_shared(std::string(dbusMessage.getSender())); for(auto attributeDispatcherIterator = getStubAttributeTable().begin(); attributeDispatcherIterator != getStubAttributeTable().end(); attributeDispatcherIterator++) { //To prevent the destruction of the stub whilst still handling a message if (stub_) { StubDispatcher* getterDispatcher = static_cast(attributeDispatcherIterator->second.getter); assert(getterDispatcher != NULL); // all attributes have at least a getter dbusOutputStream.align(8); dbusOutputStream << attributeDispatcherIterator->first; getterDispatcher->appendGetAllReply(dbusMessage, stub_, *this, dbusOutputStream); } } dbusOutputStream.endWriteMap(); dbusOutputStream.flush(); return getDBusConnection()->sendDBusMessage(dbusMessageReply); } }; template< class > struct DBusStubSignalHelper; template class In_, class... InArgs_> struct DBusStubSignalHelper> { static inline bool sendSignal(const char* objectPath, const char* interfaceName, const char* signalName, const char* signalSignature, const std::shared_ptr& dbusConnection, const InArgs_&... inArgs) { DBusMessage dbusMessage = DBusMessage::createSignal( objectPath, interfaceName, signalName, signalSignature); if (sizeof...(InArgs_) > 0) { DBusOutputStream outputStream(dbusMessage); const bool success = DBusSerializableArguments::serialize(outputStream, inArgs...); if (!success) { return false; } outputStream.flush(); } const bool dbusMessageSent = dbusConnection->sendDBusMessage(dbusMessage); return dbusMessageSent; } template static bool sendSignal(const DBusStub_ &_stub, const char *_name, const char *_signature, const InArgs_&... inArgs) { return(sendSignal(_stub.getDBusAddress().getObjectPath().c_str(), _stub.getDBusAddress().getInterface().c_str(), _name, _signature, _stub.getDBusConnection(), inArgs...)); } template static bool sendSignal(const char *_target, const DBusStub_ &_stub, const char *_name, const char *_signature, const InArgs_&... inArgs) { DBusMessage dbusMessage = DBusMessage::createSignal( _stub.getDBusAddress().getObjectPath().c_str(), _stub.getDBusAddress().getInterface().c_str(), _name, _signature); dbusMessage.setDestination(_target); if (sizeof...(InArgs_) > 0) { DBusOutputStream outputStream(dbusMessage); const bool success = DBusSerializableArguments::serialize(outputStream, inArgs...); if (!success) { return false; } outputStream.flush(); } return _stub.getDBusConnection()->sendDBusMessage(dbusMessage); } }; template< class, class, class > class DBusMethodStubDispatcher; template < typename StubClass_, template class In_, class... InArgs_, template class DeplIn_, class... DeplIn_Args> class DBusMethodStubDispatcher, DeplIn_ >: public DBusStubAdapterHelper::StubDispatcher { public: typedef DBusStubAdapterHelper DBusStubAdapterHelperType; typedef void (StubClass_::*StubFunctor_)(std::shared_ptr, InArgs_...); DBusMethodStubDispatcher(StubFunctor_ stubFunctor, std::tuple _in): stubFunctor_(stubFunctor) { initialize(typename make_sequence_range::type(), _in); } bool dispatchDBusMessage(const DBusMessage& dbusMessage, const std::shared_ptr& stub, DBusStubAdapterHelperType& dbusStubAdapterHelper) { return handleDBusMessage(dbusMessage, stub, dbusStubAdapterHelper, typename make_sequence_range::type()); } private: template inline void initialize(index_sequence, std::tuple &_in) { in_ = std::make_tuple(std::get(_in)...); } template inline bool handleDBusMessage(const DBusMessage& dbusMessage, const std::shared_ptr& stub, DBusStubAdapterHelperType& dbusStubAdapterHelper, index_sequence) { (void)dbusStubAdapterHelper; if (sizeof...(InArgs_) > 0) { DBusInputStream dbusInputStream(dbusMessage); const bool success = DBusSerializableArguments...>::deserialize(dbusInputStream, std::get(in_)...); if (!success) return false; } std::shared_ptr clientId = std::make_shared(std::string(dbusMessage.getSender())); (stub.get()->*stubFunctor_)(clientId, std::move(std::get(in_).getValue())...); return true; } StubFunctor_ stubFunctor_; std::tuple...> in_; }; template< class, class, class, class, class> class DBusMethodWithReplyStubDispatcher; template < typename StubClass_, template class In_, class... InArgs_, template class Out_, class... OutArgs_, template class DeplIn_, class... DeplIn_Args, template class DeplOut_, class... DeplOutArgs_> class DBusMethodWithReplyStubDispatcher< StubClass_, In_, Out_, DeplIn_, DeplOut_ >: public DBusStubAdapterHelper::StubDispatcher { public: typedef DBusStubAdapterHelper DBusStubAdapterHelperType; typedef std::function ReplyType_t; typedef void (StubClass_::*StubFunctor_)( std::shared_ptr, InArgs_..., ReplyType_t); DBusMethodWithReplyStubDispatcher(StubFunctor_ stubFunctor, const char* dbusReplySignature, std::tuple _inDepArgs, std::tuple _outDepArgs): stubFunctor_(stubFunctor), dbusReplySignature_(dbusReplySignature), out_(_outDepArgs), currentCall_(0) { initialize(typename make_sequence_range::type(), _inDepArgs); } bool dispatchDBusMessage(const DBusMessage& dbusMessage, const std::shared_ptr& stub, DBusStubAdapterHelperType& dbusStubAdapterHelper) { connection_ = dbusStubAdapterHelper.getDBusConnection(); return handleDBusMessage( dbusMessage, stub, dbusStubAdapterHelper, typename make_sequence_range::type(), typename make_sequence_range::type()); } bool sendReply(CommonAPI::CallId_t _call, std::tuple...> args = std::make_tuple()) { return sendReplyInternal(_call, typename make_sequence_range::type(), args); } private: template inline void initialize(index_sequence, std::tuple &_in) { in_ = std::make_tuple(std::get(_in)...); } template inline bool handleDBusMessage(const DBusMessage& dbusMessage, const std::shared_ptr& stub, DBusStubAdapterHelperType& dbusStubAdapterHelper, index_sequence, index_sequence) { (void)dbusStubAdapterHelper; if (sizeof...(DeplIn_Args) > 0) { DBusInputStream dbusInputStream(dbusMessage); const bool success = DBusSerializableArguments...>::deserialize(dbusInputStream, std::get(in_)...); if (!success) return false; } std::shared_ptr clientId = std::make_shared(std::string(dbusMessage.getSender())); DBusMessage reply = dbusMessage.createMethodReturn(dbusReplySignature_); CommonAPI::CallId_t call; { std::lock_guard lock(mutex_); call = currentCall_++; pending_[call] = reply; } (stub.get()->*stubFunctor_)( clientId, std::move(std::get(in_).getValue())..., [call, this](OutArgs_... _args){ this->sendReply(call, std::make_tuple(CommonAPI::Deployable( _args, std::get(out_) )...)); } ); return true; } template bool sendReplyInternal(CommonAPI::CallId_t _call, index_sequence, std::tuple...> args) { std::lock_guard lock(mutex_); auto reply = pending_.find(_call); if (reply != pending_.end()) { if (sizeof...(DeplOutArgs_) > 0) { DBusOutputStream output(reply->second); if (!DBusSerializableArguments...>::serialize( output, std::get(args)...)) { (void)args; pending_.erase(_call); return false; } output.flush(); } bool isSuccessful = connection_->sendDBusMessage(reply->second); pending_.erase(_call); return isSuccessful; } return false; } StubFunctor_ stubFunctor_; const char* dbusReplySignature_; std::tuple...> in_; std::tuple out_; CommonAPI::CallId_t currentCall_; std::map pending_; std::mutex mutex_; // protects pending_ std::shared_ptr connection_; }; template< class, class, class, class > class DBusMethodWithReplyAdapterDispatcher; template < typename StubClass_, typename StubAdapterClass_, template class In_, class... InArgs_, template class Out_, class... OutArgs_> class DBusMethodWithReplyAdapterDispatcher, Out_ >: public DBusStubAdapterHelper::StubDispatcher { public: typedef DBusStubAdapterHelper DBusStubAdapterHelperType; typedef void (StubAdapterClass_::*StubFunctor_)(std::shared_ptr, InArgs_..., OutArgs_&...); typedef typename CommonAPI::Stub StubType; DBusMethodWithReplyAdapterDispatcher(StubFunctor_ stubFunctor, const char* dbusReplySignature): stubFunctor_(stubFunctor), dbusReplySignature_(dbusReplySignature) { } bool dispatchDBusMessage(const DBusMessage& dbusMessage, const std::shared_ptr& stub, DBusStubAdapterHelperType& dbusStubAdapterHelper) { std::tuple argTuple; return handleDBusMessage( dbusMessage, stub, dbusStubAdapterHelper, typename make_sequence_range::type(), typename make_sequence_range::type(),argTuple); } private: template inline bool handleDBusMessage(const DBusMessage& dbusMessage, const std::shared_ptr& stub, DBusStubAdapterHelperType& dbusStubAdapterHelper, index_sequence, index_sequence, std::tuple argTuple) const { (void)argTuple; if (sizeof...(InArgs_) > 0) { DBusInputStream dbusInputStream(dbusMessage); const bool success = DBusSerializableArguments::deserialize(dbusInputStream, std::get(argTuple)...); if (!success) return false; } std::shared_ptr clientId = std::make_shared(std::string(dbusMessage.getSender())); (stub->StubType::getStubAdapter().get()->*stubFunctor_)(clientId, std::move(std::get(argTuple))..., std::get(argTuple)...); DBusMessage dbusMessageReply = dbusMessage.createMethodReturn(dbusReplySignature_); if (sizeof...(OutArgs_) > 0) { DBusOutputStream dbusOutputStream(dbusMessageReply); const bool success = DBusSerializableArguments::serialize(dbusOutputStream, std::get(argTuple)...); if (!success) return false; dbusOutputStream.flush(); } return dbusStubAdapterHelper.getDBusConnection()->sendDBusMessage(dbusMessageReply); } StubFunctor_ stubFunctor_; const char* dbusReplySignature_; }; template class DBusGetAttributeStubDispatcher: public virtual DBusStubAdapterHelper::StubDispatcher { public: typedef DBusStubAdapterHelper DBusStubAdapterHelperType; typedef const AttributeType_& (StubClass_::*GetStubFunctor)(std::shared_ptr); DBusGetAttributeStubDispatcher(GetStubFunctor _getStubFunctor, const char *_signature, AttributeDepl_ *_depl = nullptr): getStubFunctor_(_getStubFunctor), signature_(_signature), depl_(_depl) { } virtual ~DBusGetAttributeStubDispatcher() {}; bool dispatchDBusMessage(const DBusMessage& dbusMessage, const std::shared_ptr& stub, DBusStubAdapterHelperType& dbusStubAdapterHelper) { return sendAttributeValueReply(dbusMessage, stub, dbusStubAdapterHelper); } void appendGetAllReply(const DBusMessage& dbusMessage, const std::shared_ptr& stub, DBusStubAdapterHelperType& dbusStubAdapterHelper, DBusOutputStream &_output) { (void)dbusStubAdapterHelper; std::shared_ptr clientId = std::make_shared(std::string(dbusMessage.getSender())); auto varDepl = CommonAPI::DBus::VariantDeployment(true, depl_); // presuming FreeDesktop variant deployment, as support for "legacy" service only _output << CommonAPI::Deployable, CommonAPI::DBus::VariantDeployment>((stub.get()->*getStubFunctor_)(clientId), &varDepl); _output.flush(); } protected: virtual bool sendAttributeValueReply(const DBusMessage& dbusMessage, const std::shared_ptr& stub, DBusStubAdapterHelperType& dbusStubAdapterHelper) { DBusMessage dbusMessageReply = dbusMessage.createMethodReturn(signature_); DBusOutputStream dbusOutputStream(dbusMessageReply); std::shared_ptr clientId = std::make_shared(std::string(dbusMessage.getSender())); dbusOutputStream << CommonAPI::Deployable((stub.get()->*getStubFunctor_)(clientId), depl_); dbusOutputStream.flush(); return dbusStubAdapterHelper.getDBusConnection()->sendDBusMessage(dbusMessageReply); } GetStubFunctor getStubFunctor_; const char* signature_; AttributeDepl_ *depl_; }; template class DBusSetAttributeStubDispatcher: public virtual DBusGetAttributeStubDispatcher { public: typedef typename DBusGetAttributeStubDispatcher::DBusStubAdapterHelperType DBusStubAdapterHelperType; typedef typename DBusStubAdapterHelperType::RemoteEventHandlerType RemoteEventHandlerType; typedef typename DBusGetAttributeStubDispatcher::GetStubFunctor GetStubFunctor; typedef bool (RemoteEventHandlerType::*OnRemoteSetFunctor)(std::shared_ptr, AttributeType_); typedef void (RemoteEventHandlerType::*OnRemoteChangedFunctor)(); DBusSetAttributeStubDispatcher(GetStubFunctor getStubFunctor, OnRemoteSetFunctor onRemoteSetFunctor, OnRemoteChangedFunctor onRemoteChangedFunctor, const char* dbusSignature, AttributeDepl_ *_depl = nullptr) : DBusGetAttributeStubDispatcher(getStubFunctor, dbusSignature, _depl), onRemoteSetFunctor_(onRemoteSetFunctor), onRemoteChangedFunctor_(onRemoteChangedFunctor) { } virtual ~DBusSetAttributeStubDispatcher() {}; bool dispatchDBusMessage(const DBusMessage& dbusMessage, const std::shared_ptr& stub, DBusStubAdapterHelperType& dbusStubAdapterHelper) { bool attributeValueChanged; if (!setAttributeValue(dbusMessage, stub, dbusStubAdapterHelper, attributeValueChanged)) return false; if (attributeValueChanged) notifyOnRemoteChanged(dbusStubAdapterHelper); return true; } protected: virtual AttributeType_ retrieveAttributeValue(const DBusMessage& dbusMessage, bool& errorOccured) { errorOccured = false; DBusInputStream dbusInputStream(dbusMessage); CommonAPI::Deployable attributeValue(this->depl_); dbusInputStream >> attributeValue; if (dbusInputStream.hasError()) { errorOccured = true; } return attributeValue.getValue(); } inline bool setAttributeValue(const DBusMessage& dbusMessage, const std::shared_ptr& stub, DBusStubAdapterHelperType& dbusStubAdapterHelper, bool& attributeValueChanged) { bool errorOccured; CommonAPI::Deployable attributeValue( retrieveAttributeValue(dbusMessage, errorOccured), this->depl_); if(errorOccured) { return false; } std::shared_ptr clientId = std::make_shared(std::string(dbusMessage.getSender())); attributeValueChanged = (dbusStubAdapterHelper.getRemoteEventHandler()->*onRemoteSetFunctor_)(clientId, std::move(attributeValue.getValue())); return this->sendAttributeValueReply(dbusMessage, stub, dbusStubAdapterHelper); } inline void notifyOnRemoteChanged(DBusStubAdapterHelperType& dbusStubAdapterHelper) { (dbusStubAdapterHelper.getRemoteEventHandler()->*onRemoteChangedFunctor_)(); } inline const AttributeType_& getAttributeValue(std::shared_ptr clientId, const std::shared_ptr& stub) { return (stub.get()->*(this->getStubFunctor_))(clientId); } const OnRemoteSetFunctor onRemoteSetFunctor_; const OnRemoteChangedFunctor onRemoteChangedFunctor_; }; template class DBusSetObservableAttributeStubDispatcher: public virtual DBusSetAttributeStubDispatcher { public: typedef typename DBusSetAttributeStubDispatcher::DBusStubAdapterHelperType DBusStubAdapterHelperType; typedef typename DBusStubAdapterHelperType::StubAdapterType StubAdapterType; typedef typename DBusSetAttributeStubDispatcher::GetStubFunctor GetStubFunctor; typedef typename DBusSetAttributeStubDispatcher::OnRemoteSetFunctor OnRemoteSetFunctor; typedef typename DBusSetAttributeStubDispatcher::OnRemoteChangedFunctor OnRemoteChangedFunctor; typedef typename CommonAPI::Stub StubType; typedef void (StubAdapterType::*FireChangedFunctor)(const AttributeType_&); DBusSetObservableAttributeStubDispatcher(GetStubFunctor getStubFunctor, OnRemoteSetFunctor onRemoteSetFunctor, OnRemoteChangedFunctor onRemoteChangedFunctor, FireChangedFunctor fireChangedFunctor, const char* dbusSignature, AttributeDepl_ *_depl = nullptr) : DBusGetAttributeStubDispatcher( getStubFunctor, dbusSignature, _depl), DBusSetAttributeStubDispatcher( getStubFunctor, onRemoteSetFunctor, onRemoteChangedFunctor, dbusSignature, _depl), fireChangedFunctor_(fireChangedFunctor) { } virtual ~DBusSetObservableAttributeStubDispatcher() {}; bool dispatchDBusMessage(const DBusMessage& dbusMessage, const std::shared_ptr& stub, DBusStubAdapterHelperType& dbusStubAdapterHelper) { bool attributeValueChanged; if (!this->setAttributeValue(dbusMessage, stub, dbusStubAdapterHelper, attributeValueChanged)) return false; if (attributeValueChanged) { std::shared_ptr clientId = std::make_shared(std::string(dbusMessage.getSender())); fireAttributeValueChanged(clientId, dbusStubAdapterHelper, stub); this->notifyOnRemoteChanged(dbusStubAdapterHelper); } return true; } protected: virtual void fireAttributeValueChanged(std::shared_ptr _client, DBusStubAdapterHelperType &_helper, const std::shared_ptr _stub) { (void)_helper; (_stub->StubType::getStubAdapter().get()->*fireChangedFunctor_)(this->getAttributeValue(_client, _stub)); } const FireChangedFunctor fireChangedFunctor_; }; } // namespace DBus } // namespace CommonAPI #endif // COMMONAPI_DBUS_DBUSSTUBADAPTERHELPER_HPP_