Summary: The plan is to get SonarState to take update events and aggregate them into an object, that can be displayed in the UI so you can see what sonar is currently trying to do, and what if anything is failing. This is pretty rubbish right now, as it just uses a single string to represent state, this is just the initial version I'll iterate on. I also need to pass the SonarState into the SonarWebSocketImpl, since that's where a lot of the heavy lifting is done, but as long as something is logging state updates here, it proves the concept. SonarStateUpdateListener is the interface that will be implemented by android and iOS implementations. These implementations will notify an activity or screen that the state has changed and they should reflect that. Reviewed By: danielbuechele Differential Revision: D9150551 fbshipit-source-id: 8dd585ca503d32e684ff843d66a59a50a420b4c0
217 lines
6.0 KiB
C++
217 lines
6.0 KiB
C++
/*
|
|
* Copyright (c) 2018-present, Facebook, Inc.
|
|
*
|
|
* This source code is licensed under the MIT license found in the LICENSE
|
|
* file in the root directory of this source tree.
|
|
*
|
|
*/
|
|
|
|
#include "SonarClient.h"
|
|
#include "SonarConnectionImpl.h"
|
|
#include "SonarResponderImpl.h"
|
|
#include "SonarState.h"
|
|
#include "SonarStep.h"
|
|
#include "SonarWebSocketImpl.h"
|
|
|
|
#ifdef __ANDROID__
|
|
#include <android/log.h>
|
|
#define SONAR_LOG(message) \
|
|
__android_log_print(ANDROID_LOG_INFO, "sonar", "sonar: %s", message)
|
|
#else
|
|
#define SONAR_LOG(message) printf("sonar: %s\n", message)
|
|
#endif
|
|
|
|
#if FB_SONARKIT_ENABLED
|
|
|
|
namespace facebook {
|
|
namespace sonar {
|
|
|
|
static SonarClient* kInstance;
|
|
|
|
using folly::dynamic;
|
|
|
|
void SonarClient::init(SonarInitConfig config) {
|
|
kInstance =
|
|
new SonarClient(std::make_unique<SonarWebSocketImpl>(std::move(config)));
|
|
}
|
|
|
|
SonarClient* SonarClient::instance() {
|
|
return kInstance;
|
|
}
|
|
|
|
void SonarClient::setStateListener(
|
|
std::shared_ptr<SonarStateUpdateListener> stateListener) {
|
|
SONAR_LOG("Setting state listener");
|
|
sonarState_->setUpdateListener(stateListener);
|
|
}
|
|
|
|
void SonarClient::addPlugin(std::shared_ptr<SonarPlugin> plugin) {
|
|
SONAR_LOG(("SonarClient::addPlugin " + plugin->identifier()).c_str());
|
|
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
performAndReportError([this, plugin]() {
|
|
if (plugins_.find(plugin->identifier()) != plugins_.end()) {
|
|
throw std::out_of_range(
|
|
"plugin " + plugin->identifier() + " already added.");
|
|
}
|
|
plugins_[plugin->identifier()] = plugin;
|
|
if (connected_) {
|
|
refreshPlugins();
|
|
}
|
|
});
|
|
}
|
|
|
|
void SonarClient::removePlugin(std::shared_ptr<SonarPlugin> plugin) {
|
|
SONAR_LOG(("SonarClient::removePlugin " + plugin->identifier()).c_str());
|
|
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
performAndReportError([this, plugin]() {
|
|
if (plugins_.find(plugin->identifier()) == plugins_.end()) {
|
|
throw std::out_of_range("plugin " + plugin->identifier() + " not added.");
|
|
}
|
|
disconnect(plugin);
|
|
plugins_.erase(plugin->identifier());
|
|
if (connected_) {
|
|
refreshPlugins();
|
|
}
|
|
});
|
|
}
|
|
|
|
std::shared_ptr<SonarPlugin> SonarClient::getPlugin(
|
|
const std::string& identifier) {
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
if (plugins_.find(identifier) == plugins_.end()) {
|
|
return nullptr;
|
|
}
|
|
return plugins_.at(identifier);
|
|
}
|
|
|
|
bool SonarClient::hasPlugin(const std::string& identifier) {
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
return plugins_.find(identifier) != plugins_.end();
|
|
}
|
|
|
|
void SonarClient::disconnect(std::shared_ptr<SonarPlugin> plugin) {
|
|
const auto& conn = connections_.find(plugin->identifier());
|
|
if (conn != connections_.end()) {
|
|
connections_.erase(plugin->identifier());
|
|
plugin->didDisconnect();
|
|
}
|
|
}
|
|
|
|
void SonarClient::refreshPlugins() {
|
|
dynamic message = dynamic::object("method", "refreshPlugins");
|
|
socket_->sendMessage(message);
|
|
}
|
|
|
|
void SonarClient::onConnected() {
|
|
SONAR_LOG("SonarClient::onConnected");
|
|
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
connected_ = true;
|
|
}
|
|
|
|
void SonarClient::onDisconnected() {
|
|
SONAR_LOG("SonarClient::onDisconnected");
|
|
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
connected_ = false;
|
|
performAndReportError([this]() {
|
|
for (const auto& iter : plugins_) {
|
|
disconnect(iter.second);
|
|
}
|
|
});
|
|
}
|
|
|
|
void SonarClient::onMessageReceived(const dynamic& message) {
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
performAndReportError([this, &message]() {
|
|
const auto& method = message["method"];
|
|
const auto& params = message.getDefault("params");
|
|
|
|
std::unique_ptr<SonarResponderImpl> responder;
|
|
if (message.find("id") != message.items().end()) {
|
|
responder.reset(
|
|
new SonarResponderImpl(socket_.get(), message["id"].getInt()));
|
|
}
|
|
|
|
if (method == "getPlugins") {
|
|
dynamic identifiers = dynamic::array();
|
|
for (const auto& elem : plugins_) {
|
|
identifiers.push_back(elem.first);
|
|
}
|
|
dynamic response = dynamic::object("plugins", identifiers);
|
|
responder->success(response);
|
|
return;
|
|
}
|
|
|
|
if (method == "init") {
|
|
const auto identifier = params["plugin"].getString();
|
|
if (plugins_.find(identifier) == plugins_.end()) {
|
|
throw std::out_of_range(
|
|
"plugin " + identifier + " not found for method " +
|
|
method.getString());
|
|
}
|
|
const auto plugin = plugins_.at(identifier);
|
|
auto& conn = connections_[plugin->identifier()];
|
|
conn = std::make_shared<SonarConnectionImpl>(
|
|
socket_.get(), plugin->identifier());
|
|
plugin->didConnect(conn);
|
|
return;
|
|
}
|
|
|
|
if (method == "deinit") {
|
|
const auto identifier = params["plugin"].getString();
|
|
if (plugins_.find(identifier) == plugins_.end()) {
|
|
throw std::out_of_range(
|
|
"plugin " + identifier + " not found for method " +
|
|
method.getString());
|
|
}
|
|
const auto plugin = plugins_.at(identifier);
|
|
disconnect(plugin);
|
|
return;
|
|
}
|
|
|
|
if (method == "execute") {
|
|
const auto identifier = params["api"].getString();
|
|
if (connections_.find(identifier) == connections_.end()) {
|
|
throw std::out_of_range(
|
|
"connection " + identifier + " not found for method " +
|
|
method.getString());
|
|
}
|
|
const auto& conn = connections_.at(params["api"].getString());
|
|
conn->call(
|
|
params["method"].getString(),
|
|
params.getDefault("params"),
|
|
std::move(responder));
|
|
return;
|
|
}
|
|
|
|
dynamic response =
|
|
dynamic::object("message", "Received unknown method: " + method);
|
|
responder->error(response);
|
|
});
|
|
}
|
|
|
|
void SonarClient::performAndReportError(const std::function<void()>& func) {
|
|
try {
|
|
func();
|
|
} catch (std::exception& e) {
|
|
if (connected_) {
|
|
dynamic message = dynamic::object(
|
|
"error",
|
|
dynamic::object("message", e.what())("stacktrace", "<none>"));
|
|
socket_->sendMessage(message);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string SonarClient::getState() {
|
|
return sonarState_->getState();
|
|
}
|
|
|
|
} // namespace sonar
|
|
} // namespace facebook
|
|
|
|
#endif
|