From 91e94815b4210afc8df9cddc5ff57d6b8a3ef1aa Mon Sep 17 00:00:00 2001 From: John Knox Date: Tue, 7 Aug 2018 09:42:03 -0700 Subject: [PATCH] Add state tracking plumbing 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 --- android/src/main/cpp/sonar.cpp | 60 +++++++++++++++++++ .../sonar/android/SonarClientImpl.java | 10 ++++ .../com/facebook/sonar/core/SonarClient.java | 6 ++ .../sonar/core/SonarStateUpdateListener.java | 7 +++ xplat/Sonar/SonarClient.cpp | 12 ++++ xplat/Sonar/SonarClient.h | 9 +++ xplat/Sonar/SonarState.cpp | 54 +++++++++++++++++ xplat/Sonar/SonarState.h | 37 ++++++++++++ xplat/Sonar/SonarStateUpdateListener.h | 14 +++++ xplat/Sonar/SonarStep.cpp | 25 ++++++++ xplat/Sonar/SonarStep.h | 25 ++++++++ 11 files changed, 259 insertions(+) create mode 100644 android/src/main/java/com/facebook/sonar/core/SonarStateUpdateListener.java create mode 100644 xplat/Sonar/SonarState.cpp create mode 100644 xplat/Sonar/SonarState.h create mode 100644 xplat/Sonar/SonarStateUpdateListener.h create mode 100644 xplat/Sonar/SonarStep.cpp create mode 100644 xplat/Sonar/SonarStep.h diff --git a/android/src/main/cpp/sonar.cpp b/android/src/main/cpp/sonar.cpp index 22d7f403c..9161d30df 100644 --- a/android/src/main/cpp/sonar.cpp +++ b/android/src/main/cpp/sonar.cpp @@ -22,6 +22,7 @@ #include #include #include +#include using namespace facebook; using namespace facebook::sonar; @@ -198,6 +199,37 @@ class JSonarPlugin : public jni::JavaClass { } }; +class JSonarStateUpdateListener : public jni::JavaClass { + public: + constexpr static auto kJavaDescriptor = "Lcom/facebook/sonar/core/SonarStateUpdateListener;"; + + void onUpdate() { + static const auto method = javaClassStatic()->getMethod("onUpdate"); + method(self()); + } + void onStepStarted(std::string step) { + static const auto method = javaClassStatic()->getMethod("onStepStarted"); + method(self(), step); + } + void onStepSuccess(std::string step) { + static const auto method = javaClassStatic()->getMethod("onStepSuccess"); + method(self(), step); + } + void onStepFailed(std::string step, std::string errorMessage) { + static const auto method = javaClassStatic()->getMethod("onStepFailed"); + method(self(), step, errorMessage); + } +}; + +class AndroidSonarStateUpdateListener : public SonarStateUpdateListener { + public: + AndroidSonarStateUpdateListener(jni::alias_ref stateListener); + void onUpdate(); + + private: + jni::global_ref jStateListener; +}; + class JSonarPluginWrapper : public SonarPlugin { public: jni::global_ref jplugin; @@ -229,7 +261,10 @@ class JSonarClient : public jni::HybridClass { makeNativeMethod("stop", JSonarClient::stop), makeNativeMethod("addPlugin", JSonarClient::addPlugin), makeNativeMethod("removePlugin", JSonarClient::removePlugin), + makeNativeMethod("subscribeForUpdates", JSonarClient::subscribeForUpdates), + makeNativeMethod("unsubscribe", JSonarClient::unsubscribe), makeNativeMethod("getPlugin", JSonarClient::getPlugin), + makeNativeMethod("getState", JSonarClient::getState), }); } @@ -256,6 +291,22 @@ class JSonarClient : public jni::HybridClass { client->removePlugin(client->getPlugin(plugin->identifier())); } + void subscribeForUpdates(jni::alias_ref stateListener) { + auto client = SonarClient::instance(); + mStateListener = std::make_shared(stateListener); + client->setStateListener(mStateListener); + } + + void unsubscribe() { + auto client = SonarClient::instance(); + mStateListener = nullptr; + client->setStateListener(nullptr); + } + + std::string getState() { + return SonarClient::instance()->getState(); + } + jni::alias_ref getPlugin(const std::string& identifier) { auto plugin = SonarClient::instance()->getPlugin(identifier); if (plugin) { @@ -295,6 +346,7 @@ class JSonarClient : public jni::HybridClass { private: friend HybridBase; + std::shared_ptr mStateListener = nullptr; JSonarClient() {} }; @@ -309,3 +361,11 @@ jint JNI_OnLoad(JavaVM* vm, void*) { JEventBase::registerNatives(); }); } + +AndroidSonarStateUpdateListener::AndroidSonarStateUpdateListener(jni::alias_ref stateListener) { + jStateListener = jni::make_global(stateListener); +} + +void AndroidSonarStateUpdateListener::onUpdate() { + jStateListener->onUpdate(); +} diff --git a/android/src/main/java/com/facebook/sonar/android/SonarClientImpl.java b/android/src/main/java/com/facebook/sonar/android/SonarClientImpl.java index 72c250d62..3d55e2108 100644 --- a/android/src/main/java/com/facebook/sonar/android/SonarClientImpl.java +++ b/android/src/main/java/com/facebook/sonar/android/SonarClientImpl.java @@ -13,6 +13,7 @@ import com.facebook.soloader.SoLoader; import com.facebook.sonar.BuildConfig; import com.facebook.sonar.core.SonarClient; import com.facebook.sonar.core.SonarPlugin; +import com.facebook.sonar.core.SonarStateUpdateListener; @DoNotStrip class SonarClientImpl implements SonarClient { @@ -55,4 +56,13 @@ class SonarClientImpl implements SonarClient { @Override public native void stop(); + + @Override + public native void subscribeForUpdates(SonarStateUpdateListener stateListener); + + @Override + public native void unsubscribe(); + + @Override + public native String getState(); } diff --git a/android/src/main/java/com/facebook/sonar/core/SonarClient.java b/android/src/main/java/com/facebook/sonar/core/SonarClient.java index 8c91c3599..2cbf33ece 100644 --- a/android/src/main/java/com/facebook/sonar/core/SonarClient.java +++ b/android/src/main/java/com/facebook/sonar/core/SonarClient.java @@ -17,4 +17,10 @@ public interface SonarClient { void start(); void stop(); + + void subscribeForUpdates(SonarStateUpdateListener stateListener); + + void unsubscribe(); + + String getState(); } diff --git a/android/src/main/java/com/facebook/sonar/core/SonarStateUpdateListener.java b/android/src/main/java/com/facebook/sonar/core/SonarStateUpdateListener.java new file mode 100644 index 000000000..c790fdb6f --- /dev/null +++ b/android/src/main/java/com/facebook/sonar/core/SonarStateUpdateListener.java @@ -0,0 +1,7 @@ +package com.facebook.sonar.core; + +public interface SonarStateUpdateListener { + + void onUpdate(); + +} diff --git a/xplat/Sonar/SonarClient.cpp b/xplat/Sonar/SonarClient.cpp index a7d88ee09..2040f9ab9 100644 --- a/xplat/Sonar/SonarClient.cpp +++ b/xplat/Sonar/SonarClient.cpp @@ -9,6 +9,8 @@ #include "SonarClient.h" #include "SonarConnectionImpl.h" #include "SonarResponderImpl.h" +#include "SonarState.h" +#include "SonarStep.h" #include "SonarWebSocketImpl.h" #ifdef __ANDROID__ @@ -37,6 +39,12 @@ SonarClient* SonarClient::instance() { return kInstance; } +void SonarClient::setStateListener( + std::shared_ptr stateListener) { + SONAR_LOG("Setting state listener"); + sonarState_->setUpdateListener(stateListener); +} + void SonarClient::addPlugin(std::shared_ptr plugin) { SONAR_LOG(("SonarClient::addPlugin " + plugin->identifier()).c_str()); @@ -198,6 +206,10 @@ void SonarClient::performAndReportError(const std::function& func) { } } +std::string SonarClient::getState() { + return sonarState_->getState(); +} + } // namespace sonar } // namespace facebook diff --git a/xplat/Sonar/SonarClient.h b/xplat/Sonar/SonarClient.h index 6297d8bb5..d569ab3ff 100644 --- a/xplat/Sonar/SonarClient.h +++ b/xplat/Sonar/SonarClient.h @@ -11,9 +11,11 @@ #include #include #include +#include #include #include #include +#include "SonarStep.h" namespace facebook { namespace sonar { @@ -39,6 +41,7 @@ class SonarClient : public SonarWebSocket::Callbacks { */ SonarClient(std::unique_ptr socket) : socket_(std::move(socket)) { + sonarState_ = std::make_unique(); socket_->setCallbacks(this); } @@ -62,8 +65,13 @@ class SonarClient : public SonarWebSocket::Callbacks { void refreshPlugins(); + void setStateListener( + std::shared_ptr stateListener); + std::shared_ptr getPlugin(const std::string& identifier); + std::string getState(); + template std::shared_ptr

getPlugin(const std::string& identifier) { return std::static_pointer_cast

(getPlugin(identifier)); @@ -78,6 +86,7 @@ class SonarClient : public SonarWebSocket::Callbacks { std::map> plugins_; std::map> connections_; std::mutex mutex_; + std::unique_ptr sonarState_; void performAndReportError(const std::function& func); void disconnect(std::shared_ptr plugin); diff --git a/xplat/Sonar/SonarState.cpp b/xplat/Sonar/SonarState.cpp new file mode 100644 index 000000000..0660e1b36 --- /dev/null +++ b/xplat/Sonar/SonarState.cpp @@ -0,0 +1,54 @@ +/* + * 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 "SonarState.h" +#include "SonarStateUpdateListener.h" +#include "SonarStep.h" + +/* Class responsible for collecting state updates and combining them into a + * view of the current state of the sonar client. */ + +SonarState::SonarState() { + stateUpdates = ""; +} +void SonarState::setUpdateListener( + std::shared_ptr listener) { + mListener = listener; +} + +void SonarState::started(std::string step) { + stateUpdates = stateUpdates + "[Started] " + step + "\n"; + if (mListener) { + mListener->onUpdate(); + } +} + +void SonarState::success(std::string step) { + stateUpdates = stateUpdates + "[Success] " + step + "\n"; + if (mListener) { + mListener->onUpdate(); + } +} + +void SonarState::failed(std::string step, std::string errorMessage) { + stateUpdates = stateUpdates + "[Failed] " + step + "\n"; + if (mListener) { + mListener->onUpdate(); + } +} + +// TODO: Currently returns string, but should really provide a better +// representation of the current state so the UI can show it in a more intuitive +// way +std::string SonarState::getState() { + return stateUpdates; +} + +std::shared_ptr SonarState::start(std::string step_name) { + started(step_name); + return std::make_shared(step_name, this); +} diff --git a/xplat/Sonar/SonarState.h b/xplat/Sonar/SonarState.h new file mode 100644 index 000000000..8d09d08fb --- /dev/null +++ b/xplat/Sonar/SonarState.h @@ -0,0 +1,37 @@ +/* + * 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. + * + */ + +#pragma once + +#include +#include + +class SonarStep; +class SonarStateUpdateListener; + +class SonarState { + friend SonarStep; + + public: + SonarState(); + void setUpdateListener(std::shared_ptr); + std::string getState(); + + /* To record a state update, call start() with the name of the step to get a + SonarStep object. Call complete on this to register it successful, + the absense of the completion call when it is destructed will register as a + step failure. */ + std::shared_ptr start(std::string step); + + private: + void success(std::string); + void failed(std::string, std::string); + void started(std::string); + std::shared_ptr mListener = nullptr; + std::string stateUpdates; +}; diff --git a/xplat/Sonar/SonarStateUpdateListener.h b/xplat/Sonar/SonarStateUpdateListener.h new file mode 100644 index 000000000..a69143b1f --- /dev/null +++ b/xplat/Sonar/SonarStateUpdateListener.h @@ -0,0 +1,14 @@ +/* + * 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. + * + */ + +#pragma once + +class SonarStateUpdateListener { + public: + virtual void onUpdate() = 0; +}; diff --git a/xplat/Sonar/SonarStep.cpp b/xplat/Sonar/SonarStep.cpp new file mode 100644 index 000000000..74475394f --- /dev/null +++ b/xplat/Sonar/SonarStep.cpp @@ -0,0 +1,25 @@ +/* + * 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 "SonarStep.h" +#include "SonarState.h" + +void SonarStep::complete() { + isCompleted = true; + state->success(name); +} + +SonarStep::SonarStep(std::string step, SonarState* s) { + state = s; + name = step; +} + +SonarStep::~SonarStep() { + if (!isCompleted) { + state->failed(name, ""); + } +} diff --git a/xplat/Sonar/SonarStep.h b/xplat/Sonar/SonarStep.h new file mode 100644 index 000000000..37bfef11a --- /dev/null +++ b/xplat/Sonar/SonarStep.h @@ -0,0 +1,25 @@ +/* + * 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. + * + */ + +#pragma once + +#include + +class SonarState; + +class SonarStep { + public: + void complete(); + SonarStep(std::string name, SonarState* state); + ~SonarStep(); + + private: + std::string name; + bool isCompleted = false; + SonarState* state; +};