Add a new way of using ServiceProviderImpl without InterfaceFactory.
Instead, just take a factory function. This has a number of advantages:
* Classes don't have to implement InterfaceFactory<I> in order to
"provide" services.
* Classes can (easily) instantiate impls in different ways (or
instantiate different impls for the same service).
For now, the new method is called |AddServiceNew<I>()|, but I plan to
get rid of |AddService<I>()| and rename |AddServiceNew| to |AddService|.
Also, add a bunch of tests.
R=vardhan@google.com
Review URL: https://codereview.chromium.org/1973653002 .
diff --git a/mojo/public/cpp/application/application_connection.h b/mojo/public/cpp/application/application_connection.h
index 3507974..8979d0c 100644
--- a/mojo/public/cpp/application/application_connection.h
+++ b/mojo/public/cpp/application/application_connection.h
@@ -12,6 +12,7 @@
namespace mojo {
+struct ConnectionContext;
class ServiceConnector;
// Represents a connection to another application. An instance of this class is
@@ -34,6 +35,8 @@
// connection->AddService<Bar>(&my_foo_and_bar_factory_);
//
// The InterfaceFactory must outlive the ApplicationConnection.
+//
+// TODO(vtl): Don't get too attached to this class. I'm going to remove it.
class ApplicationConnection {
public:
virtual ~ApplicationConnection();
@@ -47,6 +50,8 @@
Interface::Name_);
}
+ virtual const ConnectionContext& GetConnectionContext() const = 0;
+
// Returns the URL that was used by the source application to establish a
// connection to the destination application.
//
diff --git a/mojo/public/cpp/application/lib/service_provider_impl.cc b/mojo/public/cpp/application/lib/service_provider_impl.cc
index 5e5da65..e7978b6 100644
--- a/mojo/public/cpp/application/lib/service_provider_impl.cc
+++ b/mojo/public/cpp/application/lib/service_provider_impl.cc
@@ -14,15 +14,15 @@
}
ServiceProviderImpl::ServiceProviderImpl(
- InterfaceRequest<ServiceProvider> request)
- : binding_(this, request.Pass()), fallback_service_provider_(nullptr) {
-}
+ InterfaceRequest<ServiceProvider> service_provider_request)
+ : binding_(this, service_provider_request.Pass()),
+ fallback_service_provider_(nullptr) {}
-ServiceProviderImpl::~ServiceProviderImpl() {
-}
+ServiceProviderImpl::~ServiceProviderImpl() {}
-void ServiceProviderImpl::Bind(InterfaceRequest<ServiceProvider> request) {
- binding_.Bind(request.Pass());
+void ServiceProviderImpl::Bind(
+ InterfaceRequest<ServiceProvider> service_provider_request) {
+ binding_.Bind(service_provider_request.Pass());
}
void ServiceProviderImpl::Close() {
diff --git a/mojo/public/cpp/application/lib/service_registry.cc b/mojo/public/cpp/application/lib/service_registry.cc
index 6be0e10..3c34628 100644
--- a/mojo/public/cpp/application/lib/service_registry.cc
+++ b/mojo/public/cpp/application/lib/service_registry.cc
@@ -34,6 +34,10 @@
service_connector_registry_.RemoveServiceConnectorForName(interface_name);
}
+const ConnectionContext& ServiceRegistry::GetConnectionContext() const {
+ return connection_context_;
+}
+
const std::string& ServiceRegistry::GetConnectionURL() {
return connection_context_.connection_url;
}
diff --git a/mojo/public/cpp/application/lib/service_registry.h b/mojo/public/cpp/application/lib/service_registry.h
index 50e5a43..1389d37 100644
--- a/mojo/public/cpp/application/lib/service_registry.h
+++ b/mojo/public/cpp/application/lib/service_registry.h
@@ -29,6 +29,7 @@
// ApplicationConnection overrides.
void SetServiceConnectorForName(ServiceConnector* service_connector,
const std::string& interface_name) override;
+ const ConnectionContext& GetConnectionContext() const override;
const std::string& GetConnectionURL() override;
const std::string& GetRemoteApplicationURL() override;
diff --git a/mojo/public/cpp/application/service_provider_impl.h b/mojo/public/cpp/application/service_provider_impl.h
index 1afa14b..b885924 100644
--- a/mojo/public/cpp/application/service_provider_impl.h
+++ b/mojo/public/cpp/application/service_provider_impl.h
@@ -5,27 +5,57 @@
#ifndef MOJO_PUBLIC_APPLICATION_SERVICE_PROVIDER_IMPL_H_
#define MOJO_PUBLIC_APPLICATION_SERVICE_PROVIDER_IMPL_H_
+#include <functional>
#include <string>
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/connection_context.h"
#include "mojo/public/cpp/application/lib/interface_factory_connector.h"
#include "mojo/public/cpp/application/lib/service_connector_registry.h"
+#include "mojo/public/cpp/application/service_connector.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/interfaces/application/service_provider.mojom.h"
namespace mojo {
-// Implements a registry that can be used to expose services to another app.
+// An implementation of |ServiceProvider|, which can be customized appropriately
+// (to select what services it provides).
class ServiceProviderImpl : public ServiceProvider {
public:
+ // A |InterfaceRequestHandler<Interface>| is simply a function that handles an
+ // interface request for |Interface|. If it determines (e.g., based on the
+ // given |ConnectionContext|) that the request should be "accepted", then it
+ // should "connect" ("take ownership of") request. Otherwise, it can simply
+ // drop |interface_request| (as implied by the interface).
+ template <typename Interface>
+ using InterfaceRequestHandler =
+ std::function<void(const ConnectionContext& connection_context,
+ InterfaceRequest<Interface> interface_request)>;
+
+ // Constructs this service provider implementation in an unbound state.
ServiceProviderImpl();
- explicit ServiceProviderImpl(InterfaceRequest<ServiceProvider> request);
+
+ // Constructs this service provider implementation, binding it to the given
+ // interface request.
+ // TODO(vtl): This should take a |ConnectionContext|, to provide
+ // |InterfaceRequestHandler<I>|s.
+ explicit ServiceProviderImpl(
+ InterfaceRequest<ServiceProvider> service_provider_request);
+
~ServiceProviderImpl() override;
- void Bind(InterfaceRequest<ServiceProvider> request);
- // Disconnect this service provider and put it in a state where it can be
- // rebound to a new request.
+ // Binds this service provider implementation to the given interface request.
+ // This may only be called if this object is unbound.
+ // TODO(vtl): This should take a |ConnectionContext|, to provide
+ // |InterfaceRequestHandler<I>|s.
+ void Bind(InterfaceRequest<ServiceProvider> service_provider_request);
+
+ // Disconnect this service provider implementation and put it in a state where
+ // it can be rebound to a new request (i.e., restores this object to an
+ // unbound state). This may be called even if this object is already unbound.
void Close();
+ // TODO(vtl): Remove this.
template <typename Interface>
void AddService(InterfaceFactory<Interface>* factory,
const std::string& interface_name = Interface::Name_) {
@@ -35,18 +65,70 @@
interface_name);
}
- // ServiceProviderImpl uses the fallback_service_provider_ whenever someone
- // asks a service that doesn't exist in the service_connector_registry_.
+ // Adds a supported service with the given |name|, using the given
+ // |interface_request_handler| (see above for information about
+ // |InterfaceRequestHandler<Interface>|). |interface_request_handler| should
+ // remain valid for the lifetime of this object.
//
- // Note: ServiceProviderImpl does not take ownership of |fallback|. The caller
- // must ensure that |fallback| outlives the ServiceProviderImpl.
+ // A typical usage may be:
//
- void set_fallback_service_provider(ServiceProvider* fallback) {
- fallback_service_provider_ = fallback;
+ // service_provider_impl_->AddServiceNew<Foobar>(
+ // [](const ConnectionContext& connection_context,
+ // InterfaceRequest<FooBar> foobar_request) {
+ // // |FoobarImpl| owns itself.
+ // new FoobarImpl(std::move(foobar_request));
+ // });
+ // TODO(vtl): Remove the AddService() above and rename this to AddService().
+ template <typename Interface>
+ void AddServiceNew(
+ InterfaceRequestHandler<Interface> interface_request_handler,
+ const std::string& name = Interface::Name_) {
+ service_connector_registry_.SetServiceConnectorForName(
+ std::unique_ptr<ServiceConnector>(new ServiceConnectorImpl<Interface>(
+ std::move(interface_request_handler))),
+ name);
+ }
+
+ // This uses the provided |fallback_service_provider| for connection requests
+ // for services that are not known (haven't been added). (Set it to null to
+ // not have any fallback.) A fallback must outlive this object (or until it is
+ // "cleared" or replaced by a different fallback.
+ void set_fallback_service_provider(
+ ServiceProvider* fallback_service_provider) {
+ fallback_service_provider_ = fallback_service_provider;
}
private:
- // Overridden from ServiceProvider:
+ // Objects of this class are used to adapt a generic (untyped) connection
+ // request (i.e., |ServiceConnector::ConnectToService()|) to the type-safe
+ // |InterfaceRequestHandler<Interface>|.
+ template <typename Interface>
+ class ServiceConnectorImpl : public ServiceConnector {
+ public:
+ explicit ServiceConnectorImpl(
+ InterfaceRequestHandler<Interface> interface_request_handler)
+ : interface_request_handler_(std::move(interface_request_handler)) {}
+ ~ServiceConnectorImpl() override {}
+
+ void ConnectToService(ApplicationConnection* application_connection,
+ const std::string& interface_name,
+ ScopedMessagePipeHandle client_handle) override {
+ // TODO(vtl): This should be given a |const ConnectionContext&|, instead
+ // of an |ApplicationConnection*| -- which may be null!
+ interface_request_handler_(
+ application_connection
+ ? application_connection->GetConnectionContext()
+ : ConnectionContext(),
+ InterfaceRequest<Interface>(client_handle.Pass()));
+ }
+
+ private:
+ const InterfaceRequestHandler<Interface> interface_request_handler_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceConnectorImpl);
+ };
+
+ // Overridden from |ServiceProvider|:
void ConnectToService(const String& service_name,
ScopedMessagePipeHandle client_handle) override;
diff --git a/mojo/public/cpp/application/tests/BUILD.gn b/mojo/public/cpp/application/tests/BUILD.gn
index 7766a7d..284f54d 100644
--- a/mojo/public/cpp/application/tests/BUILD.gn
+++ b/mojo/public/cpp/application/tests/BUILD.gn
@@ -8,6 +8,7 @@
testonly = true
sources = [
+ "service_provider_impl_unittest.cc",
"service_registry_unittest.cc",
]
@@ -18,6 +19,9 @@
mojo_sdk_deps = [
"mojo/public/cpp/application:standalone",
"mojo/public/cpp/environment:standalone",
+ "mojo/public/cpp/system",
"mojo/public/cpp/utility",
+ "mojo/public/interfaces/application",
+ "mojo/public/interfaces/bindings/tests:test_interfaces",
]
}
diff --git a/mojo/public/cpp/application/tests/service_provider_impl_unittest.cc b/mojo/public/cpp/application/tests/service_provider_impl_unittest.cc
new file mode 100644
index 0000000..e0a4f12
--- /dev/null
+++ b/mojo/public/cpp/application/tests/service_provider_impl_unittest.cc
@@ -0,0 +1,257 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/application/service_provider_impl.h"
+
+#include <utility>
+
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/public/interfaces/bindings/tests/ping_service.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+class ServiceProviderImplTest : public testing::Test {
+ public:
+ ServiceProviderImplTest() {}
+ ~ServiceProviderImplTest() override { loop_.RunUntilIdle(); }
+
+ RunLoop& loop() { return loop_; }
+
+ protected:
+ void QuitLoop(bool ok) {
+ EXPECT_TRUE(ok);
+ loop_.Quit();
+ }
+
+ private:
+ Environment env_;
+ RunLoop loop_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceProviderImplTest);
+};
+
+class PingServiceImpl : public test::PingService {
+ public:
+ PingServiceImpl(InterfaceRequest<test::PingService> ping_service_request)
+ : strong_binding_(this, std::move(ping_service_request)) {}
+ ~PingServiceImpl() override {}
+
+ // |test::PingService|:
+ void Ping(const PingCallback& callback) override { callback.Run(); }
+
+ private:
+ StrongBinding<test::PingService> strong_binding_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(PingServiceImpl);
+};
+
+TEST_F(ServiceProviderImplTest, Basic) {
+ const char kPing1[] = "Ping1";
+ const char kPing2[] = "Ping2";
+ const char kPing3[] = "Ping3";
+
+ ServiceProviderPtr sp;
+ ServiceProviderImpl impl(GetProxy(&sp));
+
+ impl.AddServiceNew<test::PingService>(
+ [](const ConnectionContext& connection_context,
+ InterfaceRequest<test::PingService> ping_service_request) {
+ new PingServiceImpl(std::move(ping_service_request));
+ },
+ kPing1);
+
+ impl.AddServiceNew<test::PingService>(
+ [](const ConnectionContext& connection_context,
+ InterfaceRequest<test::PingService> ping_service_request) {
+ new PingServiceImpl(std::move(ping_service_request));
+ },
+ kPing2);
+
+ {
+ test::PingServicePtr ping1;
+ ConnectToService(sp.get(), GetProxy(&ping1), kPing1);
+ ping1.set_connection_error_handler([this] { QuitLoop(false); });
+ ping1->Ping([this] { QuitLoop(true); });
+ loop().Run();
+ }
+ loop().RunUntilIdle(); // Run stuff caused by destructors.
+
+ {
+ test::PingServicePtr ping2;
+ ConnectToService(sp.get(), GetProxy(&ping2), kPing2);
+ ping2.set_connection_error_handler([this] { QuitLoop(false); });
+ ping2->Ping([this] { QuitLoop(true); });
+ loop().Run();
+ }
+ loop().RunUntilIdle(); // Run stuff caused by destructors.
+
+ // "Ping3" isn't actually registered!
+ {
+ test::PingServicePtr ping3;
+ ConnectToService(sp.get(), GetProxy(&ping3), kPing3);
+ ping3.set_connection_error_handler([this] { QuitLoop(true); });
+ ping3->Ping([this] { QuitLoop(false); });
+ loop().Run();
+ }
+ loop().RunUntilIdle(); // Run stuff caused by destructors.
+
+ sp.reset();
+ loop().RunUntilIdle();
+}
+
+TEST_F(ServiceProviderImplTest, CloseAndRebind) {
+ const char kPing[] = "Ping";
+
+ ServiceProviderPtr sp1;
+ ServiceProviderImpl impl(GetProxy(&sp1));
+
+ impl.AddServiceNew<test::PingService>(
+ [](const ConnectionContext& connection_context,
+ InterfaceRequest<test::PingService> ping_service_request) {
+ new PingServiceImpl(std::move(ping_service_request));
+ },
+ kPing);
+
+ {
+ test::PingServicePtr ping;
+ ConnectToService(sp1.get(), GetProxy(&ping), kPing);
+ ping.set_connection_error_handler([this] { QuitLoop(false); });
+ ping->Ping([this] { QuitLoop(true); });
+ loop().Run();
+ }
+ loop().RunUntilIdle(); // Run stuff caused by destructors.
+
+ impl.Close();
+ sp1.reset();
+ loop().RunUntilIdle();
+
+ ServiceProviderPtr sp2;
+ impl.Bind(GetProxy(&sp2));
+
+ {
+ test::PingServicePtr ping;
+ ConnectToService(sp2.get(), GetProxy(&ping), kPing);
+ ping.set_connection_error_handler([this] { QuitLoop(false); });
+ ping->Ping([this] { QuitLoop(true); });
+ loop().Run();
+ }
+ loop().RunUntilIdle(); // Run stuff caused by destructors.
+
+ // Can close multiple times.
+ impl.Close();
+ impl.Close();
+ sp2.reset();
+ loop().RunUntilIdle();
+}
+
+TEST_F(ServiceProviderImplTest, Bind) {
+ const char kPing[] = "Ping";
+
+ ServiceProviderPtr sp;
+ ServiceProviderImpl impl;
+
+ impl.Bind(GetProxy(&sp));
+
+ impl.AddServiceNew<test::PingService>(
+ [](const ConnectionContext& connection_context,
+ InterfaceRequest<test::PingService> request) {
+ new PingServiceImpl(std::move(request));
+ },
+ kPing);
+
+ {
+ test::PingServicePtr ping;
+ ConnectToService(sp.get(), GetProxy(&ping), kPing);
+ ping.set_connection_error_handler([this] { QuitLoop(false); });
+ ping->Ping([this] { QuitLoop(true); });
+ loop().Run();
+ }
+ loop().RunUntilIdle(); // Run stuff caused by destructors.
+
+ sp.reset();
+ loop().RunUntilIdle();
+}
+
+class FauxServiceProvider : public ServiceProvider {
+ public:
+ explicit FauxServiceProvider(
+ std::function<void(const std::string& service_name)>
+ on_connect_to_service)
+ : on_connect_to_service_(on_connect_to_service) {}
+ ~FauxServiceProvider() override {}
+
+ // |ServiceProvider|:
+ void ConnectToService(const String& service_name,
+ ScopedMessagePipeHandle client_handle) override {
+ on_connect_to_service_(service_name.get());
+ }
+
+ private:
+ std::function<void(const std::string& service_name)> on_connect_to_service_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(FauxServiceProvider);
+};
+
+TEST_F(ServiceProviderImplTest, FallbackServiceProvider) {
+ const char kWhatever[] = "Whatever";
+
+ ServiceProviderPtr sp;
+ ServiceProviderImpl impl(GetProxy(&sp));
+
+ {
+ test::PingServicePtr ping;
+ ConnectToService(sp.get(), GetProxy(&ping), kWhatever);
+ ping.set_connection_error_handler([this] { QuitLoop(true); });
+ ping->Ping([this] { QuitLoop(false); });
+ loop().Run();
+ }
+ loop().RunUntilIdle(); // Run stuff caused by destructors.
+
+ bool was_run = false;
+ FauxServiceProvider fallback_sp(
+ [this, &kWhatever, &was_run](const std::string& service_name) {
+ EXPECT_EQ(kWhatever, service_name);
+ was_run = true;
+ });
+ impl.set_fallback_service_provider(&fallback_sp);
+
+ {
+ test::PingServicePtr ping;
+ ConnectToService(sp.get(), GetProxy(&ping), kWhatever);
+ ping.set_connection_error_handler([this] { QuitLoop(true); });
+ EXPECT_FALSE(was_run);
+ ping->Ping([this] { QuitLoop(false); });
+ loop().Run();
+ EXPECT_TRUE(was_run);
+ }
+ loop().RunUntilIdle(); // Run stuff caused by destructors.
+
+ // Clear the fallback.
+ impl.set_fallback_service_provider(nullptr);
+ was_run = false;
+
+ {
+ test::PingServicePtr ping;
+ ConnectToService(sp.get(), GetProxy(&ping), kWhatever);
+ ping.set_connection_error_handler([this] { QuitLoop(true); });
+ ping->Ping([this] { QuitLoop(false); });
+ loop().Run();
+ }
+ loop().RunUntilIdle(); // Run stuff caused by destructors.
+
+ sp.reset();
+ loop().RunUntilIdle();
+
+ EXPECT_FALSE(was_run);
+}
+
+} // namespace
+} // namespace mojo
diff --git a/services/log/log_impl_unittest.cc b/services/log/log_impl_unittest.cc
index 2511972..8500ce1 100644
--- a/services/log/log_impl_unittest.cc
+++ b/services/log/log_impl_unittest.cc
@@ -11,6 +11,7 @@
#include "mojo/public/c/environment/logger.h"
#include "mojo/public/cpp/application/application_impl.h"
#include "mojo/public/cpp/application/application_test_base.h"
+#include "mojo/public/cpp/application/connection_context.h"
#include "mojo/public/cpp/system/time.h"
#include "mojo/services/log/interfaces/entry.mojom.h"
#include "mojo/services/log/interfaces/log.mojom.h"
@@ -27,18 +28,28 @@
// We need to supply a ApplicationConnection to LogImpl::Create().
class TestApplicationConnection : public ApplicationConnection {
public:
+ TestApplicationConnection()
+ : connection_context_(ConnectionContext::Type::INCOMING,
+ "mojo:log_impl_unittest",
+ "mojo:log") {}
+
+ const ConnectionContext& GetConnectionContext() const override {
+ return connection_context_;
+ }
+
const std::string& GetConnectionURL() override {
- static std::string kConnectionURL = "mojo:log";
- return kConnectionURL;
+ return connection_context_.connection_url;
}
const std::string& GetRemoteApplicationURL() override {
- static std::string kRemoteApplicationURL = "mojo:log_impl_unittest";
- return kRemoteApplicationURL;
+ return connection_context_.remote_url;
}
void SetServiceConnectorForName(ServiceConnector* service_connector,
const std::string& name) override {}
+
+ private:
+ const ConnectionContext connection_context_;
};
// Tests the Log service implementation by calling its AddEntry and verifying