avs-device-sdk/ADSL/src/DirectiveRouter.cpp

330 lines
12 KiB
C++

/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
#include <iostream>
#include <set>
#include <sstream>
#include <vector>
#include <AVSCommon/Utils/Logger/Logger.h>
#include "ADSL/DirectiveRouter.h"
/// String to identify log entries originating from this file.
static const std::string TAG("DirectiveRouter");
/**
* Create a LogEntry using this file's TAG and the specified event string.
*
* @param The event string for this @c LogEntry.
*/
#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event)
namespace alexaClientSDK {
namespace adsl {
using namespace avsCommon::avs;
using namespace avsCommon::sdkInterfaces;
using namespace avsCommon::utils;
DirectiveRouter::DirectiveRouter() : RequiresShutdown{"DirectiveRouter"} {
}
bool DirectiveRouter::addDirectiveHandler(std::shared_ptr<DirectiveHandlerInterface> handler) {
std::lock_guard<std::mutex> lock(m_mutex);
if (isShutdown()) {
ACSDK_ERROR(LX("addDirectiveHandlersFailed").d("reason", "isShutdown"));
return false;
}
if (!handler) {
ACSDK_ERROR(LX("addDirectiveHandlersFailed").d("reason", "emptyHandler"));
return false;
}
auto configuration = handler->getConfiguration();
for (auto item : configuration) {
if (BlockingPolicy::NONE == item.second) {
ACSDK_ERROR(LX("addDirectiveHandlersFailed").d("reason", "nonePolicy"));
}
auto it = m_configuration.find(item.first);
if (m_configuration.end() != it) {
ACSDK_ERROR(LX("addDirectiveHandlersFailed")
.d("reason", "alreadySet")
.d("namespace", item.first.nameSpace)
.d("name", item.first.name));
return false;
}
}
for (auto item : configuration) {
HandlerAndPolicy handlerAndPolicy(handler, item.second);
m_configuration[item.first] = handlerAndPolicy;
incrementHandlerReferenceCountLocked(handler);
ACSDK_DEBUG9(LX("addDirectiveHandlers")
.d("action", "added")
.d("namespace", item.first.nameSpace)
.d("name", item.first.name)
.d("handler", handler.get())
.d("policy", item.second));
}
return true;
}
bool DirectiveRouter::removeDirectiveHandlerLocked(std::shared_ptr<DirectiveHandlerInterface> handler) {
if (!handler) {
ACSDK_ERROR(LX("removeDirectiveHandlersFailed").d("reason", "nullptrHandler"));
return false;
}
auto configuration = handler->getConfiguration();
for (auto item : configuration) {
auto it = m_configuration.find(item.first);
if (m_configuration.end() == it || it->second != HandlerAndPolicy(handler, item.second)) {
ACSDK_ERROR(LX("removeDirectiveHandlersFailed")
.d("reason", "notFound")
.d("namespace", item.first.nameSpace)
.d("name", item.first.name)
.d("handler", handler.get())
.d("policy", item.second));
return false;
}
}
/**
* Decrement reference counts. Unfortunately, a simple loop calling @c decrementHandlerReferenceCountLocked()
* would create a race condition because that function temporarily releases @c m_mutex when a count goes to zero.
* Instead, the operation is expanded here with the lock released once we know which handlers to notify.
*/
for (auto item : configuration) {
m_configuration.erase(item.first);
ACSDK_DEBUG9(LX("removeDirectiveHandlers")
.d("action", "removed")
.d("namespace", item.first.nameSpace)
.d("name", item.first.name)
.d("handler", handler.get())
.d("policy", item.second));
auto it = m_handlerReferenceCounts.find(handler);
if (0 == --(it->second)) {
m_handlerReferenceCounts.erase(it);
}
}
return true;
}
bool DirectiveRouter::removeDirectiveHandler(std::shared_ptr<DirectiveHandlerInterface> handler) {
std::unique_lock<std::mutex> lock(m_mutex);
if (!removeDirectiveHandlerLocked(handler)) {
return false;
}
lock.unlock();
ACSDK_DEBUG9(LX("onDeregisteredCalled").d("handler", handler.get()));
handler->onDeregistered();
return true;
}
bool DirectiveRouter::handleDirectiveImmediately(std::shared_ptr<avsCommon::avs::AVSDirective> directive) {
std::unique_lock<std::mutex> lock(m_mutex);
auto handlerAndPolicy = getHandlerAndPolicyLocked(directive);
if (!handlerAndPolicy) {
ACSDK_WARN(LX("handleDirectiveImmediatelyFailed")
.d("messageId", directive->getMessageId())
.d("reason", "noHandlerRegistered"));
return false;
}
ACSDK_INFO(LX("handleDirectiveImmediately").d("messageId", directive->getMessageId()).d("action", "calling"));
HandlerCallScope scope(lock, this, handlerAndPolicy.handler);
handlerAndPolicy.handler->handleDirectiveImmediately(directive);
return true;
}
bool DirectiveRouter::handleDirectiveWithPolicyHandleImmediately(
std::shared_ptr<avsCommon::avs::AVSDirective> directive) {
std::unique_lock<std::mutex> lock(m_mutex);
auto handlerAndPolicy = getHandlerAndPolicyLocked(directive);
if (!handlerAndPolicy) {
ACSDK_WARN(LX("handleDirectiveWithPolicyHandleImmediatelyFailed")
.d("messageId", directive->getMessageId())
.d("reason", "noHandlerRegistered"));
return false;
}
if (BlockingPolicy::HANDLE_IMMEDIATELY != handlerAndPolicy.policy) {
return false;
}
ACSDK_INFO(LX("handleDirectiveWithPolicyHandleImmediately")
.d("messageId", directive->getMessageId())
.d("action", "calling"));
HandlerCallScope scope(lock, this, handlerAndPolicy.handler);
handlerAndPolicy.handler->handleDirectiveImmediately(directive);
return true;
}
bool DirectiveRouter::preHandleDirective(
std::shared_ptr<avsCommon::avs::AVSDirective> directive,
std::unique_ptr<DirectiveHandlerResultInterface> result) {
std::unique_lock<std::mutex> lock(m_mutex);
auto handlerAndPolicy = getHandlerAndPolicyLocked(directive);
if (!handlerAndPolicy) {
ACSDK_WARN(LX("preHandleDirectiveFailed")
.d("messageId", directive->getMessageId())
.d("reason", "noHandlerRegistered"));
return false;
}
ACSDK_INFO(LX("preHandleDirective").d("messageId", directive->getMessageId()).d("action", "calling"));
HandlerCallScope scope(lock, this, handlerAndPolicy.handler);
handlerAndPolicy.handler->preHandleDirective(directive, std::move(result));
return true;
}
bool DirectiveRouter::handleDirective(
std::shared_ptr<avsCommon::avs::AVSDirective> directive,
BlockingPolicy* policyOut) {
if (!policyOut) {
ACSDK_ERROR(
LX("handleDirectiveFailed").d("messageId", directive->getMessageId()).d("reason", "nullptrPolicyOut"));
return false;
}
std::unique_lock<std::mutex> lock(m_mutex);
auto handlerAndPolicy = getHandlerAndPolicyLocked(directive);
if (!handlerAndPolicy) {
ACSDK_WARN(
LX("handleDirectiveFailed").d("messageId", directive->getMessageId()).d("reason", "noHandlerRegistered"));
return false;
}
ACSDK_INFO(LX("handleDirective").d("messageId", directive->getMessageId()).d("action", "calling"));
HandlerCallScope scope(lock, this, handlerAndPolicy.handler);
auto result = handlerAndPolicy.handler->handleDirective(directive->getMessageId());
if (result) {
*policyOut = handlerAndPolicy.policy;
} else {
ACSDK_WARN(LX("messageIdNotRecognized")
.d("handler", handlerAndPolicy.handler.get())
.d("messageId", directive->getMessageId())
.d("reason", "handleDirectiveReturnedFalse"));
}
return result;
}
bool DirectiveRouter::cancelDirective(std::shared_ptr<avsCommon::avs::AVSDirective> directive) {
std::unique_lock<std::mutex> lock(m_mutex);
auto handlerAndPolicy = getHandlerAndPolicyLocked(directive);
if (!handlerAndPolicy) {
ACSDK_WARN(
LX("cancelDirectiveFailed").d("messageId", directive->getMessageId()).d("reason", "noHandlerRegistered"));
return false;
}
ACSDK_INFO(LX("cancelDirective").d("messageId", directive->getMessageId()).d("action", "calling"));
HandlerCallScope scope(lock, this, handlerAndPolicy.handler);
handlerAndPolicy.handler->cancelDirective(directive->getMessageId());
return true;
}
void DirectiveRouter::doShutdown() {
std::vector<std::shared_ptr<avsCommon::sdkInterfaces::DirectiveHandlerInterface>> releasedHandlers;
std::unique_lock<std::mutex> lock(m_mutex);
// Should remove all configurations cleanly.
size_t numConfigurations = m_configuration.size();
for (size_t i = 0; i < numConfigurations && !m_configuration.empty(); ++i) {
auto handler = m_configuration.begin()->second.handler;
if (removeDirectiveHandlerLocked(handler)) {
releasedHandlers.push_back(handler);
}
}
// For good measure
m_configuration.clear();
lock.unlock();
for (auto releasedHandler : releasedHandlers) {
ACSDK_DEBUG9(LX("onDeregisteredCalled").d("handler", releasedHandler.get()));
releasedHandler->onDeregistered();
}
}
DirectiveRouter::HandlerCallScope::HandlerCallScope(
std::unique_lock<std::mutex>& lock,
DirectiveRouter* router,
std::shared_ptr<DirectiveHandlerInterface> handler) :
// Parenthesis are used for initializing @c m_lock to work-around a bug in the C++ specification. see:
// http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1288
m_lock(lock),
m_router{router},
m_handler{handler} {
m_router->incrementHandlerReferenceCountLocked(m_handler);
m_lock.unlock();
}
DirectiveRouter::HandlerCallScope::~HandlerCallScope() {
m_lock.lock();
m_router->decrementHandlerReferenceCountLocked(m_lock, m_handler);
}
HandlerAndPolicy DirectiveRouter::getHandlerAndPolicyLocked(std::shared_ptr<avsCommon::avs::AVSDirective> directive) {
if (!directive) {
ACSDK_WARN(LX("getHandlerAndPolicyLockedFailed").d("reason", "nullptrDirective"));
return HandlerAndPolicy();
}
// First, look for an exact match. If not found, then look for a wildcard handler for the AVS namespace.
auto it = m_configuration.find(NamespaceAndName(directive->getNamespace(), directive->getName()));
if (m_configuration.end() == it) {
it = m_configuration.find(NamespaceAndName(directive->getNamespace(), "*"));
}
if (m_configuration.end() == it) {
return HandlerAndPolicy();
}
return it->second;
}
void DirectiveRouter::incrementHandlerReferenceCountLocked(std::shared_ptr<DirectiveHandlerInterface> handler) {
const auto it = m_handlerReferenceCounts.find(handler);
if (it != m_handlerReferenceCounts.end()) {
it->second++;
} else {
m_handlerReferenceCounts[handler] = 1;
}
}
void DirectiveRouter::decrementHandlerReferenceCountLocked(
std::unique_lock<std::mutex>& lock,
std::shared_ptr<DirectiveHandlerInterface> handler) {
const auto it = m_handlerReferenceCounts.find(handler);
if (it != m_handlerReferenceCounts.end()) {
if (0 == --(it->second)) {
m_handlerReferenceCounts.erase(it);
ACSDK_DEBUG9(LX("onDeregisteredCalled").d("handler", handler.get()));
lock.unlock();
handler->onDeregistered();
lock.lock();
}
} else {
ACSDK_ERROR(LX("removeHandlerReferenceLockedFailed").d("reason", "handlerNotFound"));
}
}
} // namespace adsl
} // namespace alexaClientSDK