// token.cpp /******************************************************************************* * Copyright (c) 2013-2019 Frank Pagliughi * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Frank Pagliughi - initial implementation and documentation * Frank Pagliughi - MQTT v5 support *******************************************************************************/ #include "mqtt/token.h" #include "mqtt/async_client.h" #include #include namespace mqtt { // -------------------------------------------------------------------------- // Constructors token::token(Type typ, iasync_client& cli, const_string_collection_ptr topics) : type_(typ), cli_(&cli), rc_(0), reasonCode_(ReasonCode::SUCCESS), msgId_(MQTTAsync_token(0)), topics_(topics), userContext_(nullptr), listener_(nullptr), nExpected_(0), complete_(false) { } token::token(Type typ, iasync_client& cli, const_string_collection_ptr topics, void* userContext, iaction_listener& cb) : type_(typ), cli_(&cli), rc_(0), reasonCode_(ReasonCode::SUCCESS), msgId_(MQTTAsync_token(0)), topics_(topics), userContext_(userContext), listener_(&cb), nExpected_(0), complete_(false) { } token::token(Type typ, iasync_client& cli, MQTTAsync_token tok) : type_(typ), cli_(&cli), rc_(0), reasonCode_(ReasonCode::SUCCESS), msgId_(tok), userContext_(nullptr), listener_(nullptr), nExpected_(0), complete_(false) { } // -------------------------------------------------------------------------- // Class static callbacks. // These are the callbacks directly from the C library. // The 'context' is a raw pointer to the token object. void token::on_success(void* context, MQTTAsync_successData* rsp) { if (context) static_cast(context)->on_success(rsp); } void token::on_success5(void* context, MQTTAsync_successData5* rsp) { if (context) static_cast(context)->on_success5(rsp); } void token::on_failure(void* context, MQTTAsync_failureData* rsp) { if (context) static_cast(context)->on_failure(rsp); } void token::on_failure5(void* context, MQTTAsync_failureData5* rsp) { if (context) static_cast(context)->on_failure5(rsp); } // -------------------------------------------------------------------------- // Object callbacks // // The success callback for MQTT v3 connections // void token::on_success(MQTTAsync_successData* rsp) { unique_lock g(lock_); iaction_listener* listener = listener_; if (rsp) { msgId_ = rsp->token; switch (type_) { case Type::CONNECT: connRsp_.reset(new connect_response(rsp)); break; case Type::SUBSCRIBE: subRsp_.reset(new subscribe_response(nExpected_, rsp)); break; case Type::UNSUBSCRIBE: unsubRsp_.reset(new unsubscribe_response(rsp)); break; default: // The others don't have responses break; } } rc_ = MQTTASYNC_SUCCESS; complete_ = true; g.unlock(); // Note: callback always completes before the object is signaled. if (listener) listener->on_success(*this); cond_.notify_all(); cli_->remove_token(this); } // // The success callback for MQTT v5 connections // void token::on_success5(MQTTAsync_successData5* rsp) { unique_lock g(lock_); iaction_listener* listener = listener_; if (rsp) { msgId_ = rsp->token; reasonCode_ = ReasonCode(rsp->reasonCode); switch (type_) { case Type::CONNECT: connRsp_.reset(new connect_response(rsp)); break; case Type::SUBSCRIBE: subRsp_.reset(new subscribe_response(rsp)); break; case Type::UNSUBSCRIBE: unsubRsp_.reset(new unsubscribe_response(rsp)); break; default: // The others don't have responses break; } } rc_ = MQTTASYNC_SUCCESS; complete_ = true; g.unlock(); // Note: callback always completes before the object is signaled. if (listener) listener->on_success(*this); cond_.notify_all(); cli_->remove_token(this); } // // The failure callback for MQTT v3 connections // void token::on_failure(MQTTAsync_failureData* rsp) { unique_lock g(lock_); iaction_listener* listener = listener_; if (rsp) { msgId_ = rsp->token; rc_ = rsp->code; // HACK: For backward compatability with v3 connections reasonCode_ = ReasonCode(MQTTPP_V3_CODE); if (rsp->message) errMsg_ = string(rsp->message); } else { rc_ = -1; } complete_ = true; g.unlock(); // Note: callback always completes before the obect is signaled. if (listener) listener->on_failure(*this); cond_.notify_all(); cli_->remove_token(this); } // // The failure callback for MQTT v5 connections // void token::on_failure5(MQTTAsync_failureData5* rsp) { unique_lock g(lock_); iaction_listener* listener = listener_; if (rsp) { msgId_ = rsp->token; reasonCode_ = ReasonCode(rsp->reasonCode); //props_ = properties(rsp->properties); rc_ = rsp->code; if (rsp->message) errMsg_ = string(rsp->message); } else { rc_ = -1; } complete_ = true; g.unlock(); // Note: callback always completes before the obect is signaled. if (listener) listener->on_failure(*this); cond_.notify_all(); cli_->remove_token(this); } // -------------------------------------------------------------------------- // API void token::reset() { guard g(lock_); complete_ = false; rc_ = MQTTASYNC_SUCCESS; reasonCode_ = ReasonCode::SUCCESS; errMsg_.clear(); } void token::wait() { unique_lock g(lock_); cond_.wait(g, [this]{return complete_;}); check_ret(); } connect_response token::get_connect_response() const { if (type_ != Type::CONNECT) throw bad_cast(); unique_lock g(lock_); cond_.wait(g, [this]{return complete_;}); check_ret(); if (!connRsp_) throw missing_response("connect"); return *connRsp_; } subscribe_response token::get_subscribe_response() const { if (type_ != Type::SUBSCRIBE) throw bad_cast(); unique_lock g(lock_); cond_.wait(g, [this]{return complete_;}); check_ret(); if (!subRsp_) throw missing_response("subscribe"); return *subRsp_; } unsubscribe_response token::get_unsubscribe_response() const { if (type_ != Type::UNSUBSCRIBE) throw bad_cast(); unique_lock g(lock_); cond_.wait(g, [this]{return complete_;}); check_ret(); if (!unsubRsp_) throw missing_response("unsubscribe"); return *unsubRsp_; } ///////////////////////////////////////////////////////////////////////////// // end namespace mqtt }