From f909cb5b2713c8cd5f587c7a70e468d29bdcd429 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Wed, 12 May 2010 20:43:40 -0400 Subject: [PATCH] GDBusProxy: Remove error in get_cached_property() and add set_cached_property() This makes it possible to use the cached properties mechanism even if constructing the proxy with the DO_NOT_LOAD_PROPERTIES flag. This is useful for cases where you obtain the and track object properties out-of-band. For example, in udisks, the plan is to have something like this Manager.GetObjects (out ao paths, out aa{sa{sv}} all_properties); Manager.ObjectAdded (o path, a{sa{sv}} all_properties); Manager.ObjectChanged (o path, a{sa{sv}} all_properties); Manager.ObjectRemoved (o path, a{sa{sv}} all_properties); E.g. the first GetObjects() call will return *all* data about *all* exported objects. Further, this way a client will only need to listen these three signals (three AddMatch) on the Manager object and it will never need to do GetAll() etc (e.g. can use DO_NOT_LOAD_PROPERTIES). (Of course this only works if the client is interested in all objects... while this is true for udisks it is generally not true for other D-Bus services). Also use expected_interface to check for programming errors. --- docs/reference/gio/gio-sections.txt | 3 +- gio/gdbusproxy.c | 162 ++++++++++++++++++++++++------- gio/gdbusproxy.h | 4 +- gio/gio.symbols | 1 + gio/tests/gdbus-example-proxy-subclass.c | 6 +- gio/tests/gdbus-example-watch-proxy.c | 2 +- gio/tests/gdbus-peer.c | 3 +- gio/tests/gdbus-proxy.c | 44 +++++++-- 8 files changed, 174 insertions(+), 51 deletions(-) diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index 001217e..1085674 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -2490,8 +2490,9 @@ g_dbus_proxy_get_object_path g_dbus_proxy_get_interface_name g_dbus_proxy_get_default_timeout g_dbus_proxy_set_default_timeout -g_dbus_proxy_get_cached_property_names g_dbus_proxy_get_cached_property +g_dbus_proxy_set_cached_property +g_dbus_proxy_get_cached_property_names g_dbus_proxy_set_interface_info g_dbus_proxy_get_interface_info g_dbus_proxy_call diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c index 9d1d653..c1c97d2 100644 --- a/gio/gdbusproxy.c +++ b/gio/gdbusproxy.c @@ -490,66 +490,149 @@ g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy, return names; } +static const GDBusPropertyInfo * +lookup_property_info_or_warn (GDBusProxy *proxy, + const gchar *property_name) +{ + const GDBusPropertyInfo *info; + + if (proxy->priv->expected_interface == NULL) + return NULL; + + info = g_dbus_interface_info_lookup_property (proxy->priv->expected_interface, property_name); + if (info == NULL) + { + g_warning ("Trying to lookup property %s which isn't in expected interface %s", + property_name, + proxy->priv->expected_interface->name); + } + + return info; +} + /** * g_dbus_proxy_get_cached_property: * @proxy: A #GDBusProxy. * @property_name: Property name. - * @error: Return location for error or %NULL. - * - * Looks up the value for a property from the cache. This call does no blocking IO. * - * Normally you will not need to modify the returned variant since it is updated automatically - * in response to org.freedesktop.DBus.Properties.PropertiesChanged - * D-Bus signals (which also causes #GDBusProxy::g-properties-changed to be emitted). + * Looks up the value for a property from the cache. This call does no + * blocking IO. * - * However, for properties for which said D-Bus signal is not emitted, you - * can catch other signals and modify the returned variant accordingly (remember to emit - * #GDBusProxy::g-properties-changed yourself). + * If @proxy has an expected interface (see + * #GDBusProxy:g-interface-info), then @property_name (for existence) + * is checked against it. * - * Returns: A reference to the #GVariant instance that holds the value for @property_name or - * %NULL if @error is set. Free the reference with g_variant_unref(). + * Returns: A reference to the #GVariant instance that holds the value + * for @property_name or %NULL if the value is not in the cache. The + * returned reference must be freed with g_variant_unref(). * * Since: 2.26 */ GVariant * g_dbus_proxy_get_cached_property (GDBusProxy *proxy, - const gchar *property_name, - GError **error) + const gchar *property_name) { GVariant *value; g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL); g_return_val_if_fail (property_name != NULL, NULL); - value = NULL; - - if (proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES) - { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - _("Properties are not available (proxy created with G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)")); - goto out; - } - value = g_hash_table_lookup (proxy->priv->properties, property_name); if (value == NULL) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - _("No property with name %s"), - property_name); + const GDBusPropertyInfo *info; + info = lookup_property_info_or_warn (proxy, property_name); + /* no difference */ goto out; } g_variant_ref (value); out: - return value; } +/** + * g_dbus_proxy_set_cached_property: + * @proxy: A #GDBusProxy + * @property_name: Property name. + * @value: Value for the property or %NULL to remove it from the cache. + * + * If @value is not %NULL, sets the cached value for the property with + * name @property_name to the value in @value. + * + * If @value is %NULL, then the cached value is removed from the + * property cache. + * + * If @proxy has an expected interface (see + * #GDBusProxy:g-interface-info), then @property_name (for existence) + * and @value (for the type) is checked against it. + * + * If the @value #GVariant is floating, it is consumed. This allows + * convenient 'inline' use of g_variant_new(), e.g. + * |[ + * g_dbus_proxy_set_cached_property (proxy, + * "SomeProperty", + * g_variant_new ("(si)", + * "A String", + * 42)); + * ]| + * + * Normally you will not need to use this method since @proxy is + * tracking changes using the + * org.freedesktop.DBus.Properties.PropertiesChanged + * D-Bus signal. However, for performance reasons an object may decide + * to not use this signal for some properties and instead use a + * proprietary out-of-band mechanism to transmit changes. + * + * As a concrete example, consider an object with a property + * ChatroomParticipants which is an array of + * strings. Instead of transmitting the same (long) array every time + * the property changes, it is more efficient to only transmit the + * delta using e.g. signals ChatroomParticipantJoined(String + * name) and ChatroomParticipantParted(String + * name). + * + * Since: 2.26 + */ +void +g_dbus_proxy_set_cached_property (GDBusProxy *proxy, + const gchar *property_name, + GVariant *value) +{ + const GDBusPropertyInfo *info; + + g_return_if_fail (G_IS_DBUS_PROXY (proxy)); + g_return_if_fail (property_name != NULL); + + if (value != NULL) + { + info = lookup_property_info_or_warn (proxy, property_name); + if (info != NULL) + { + if (g_strcmp0 (info->signature, g_variant_get_type_string (value)) != 0) + { + g_warning (_("Trying to set property %s of type %s but according to the expected " + "interface the type is %s"), + property_name, + g_variant_get_type_string (value), + info->signature); + goto out; + } + } + g_hash_table_insert (proxy->priv->properties, + g_strdup (property_name), + g_variant_ref_sink (value)); + } + else + { + g_hash_table_remove (proxy->priv->properties, property_name); + } + + out: + ; +} + /* ---------------------------------------------------------------------------------------------------- */ static void @@ -1306,13 +1389,15 @@ lookup_method_info_or_warn (GDBusProxy *proxy, { const GDBusMethodInfo *info; - if (!proxy->priv->expected_interface) + if (proxy->priv->expected_interface == NULL) return NULL; info = g_dbus_interface_info_lookup_method (proxy->priv->expected_interface, method_name); - if (!info) - g_warning ("Trying to invoke method %s which isn't in expected interface %s", - method_name, proxy->priv->expected_interface->name); + if (info == NULL) + { + g_warning ("Trying to invoke method %s which isn't in expected interface %s", + method_name, proxy->priv->expected_interface->name); + } return info; } @@ -1586,9 +1671,12 @@ g_dbus_proxy_call_sync (GDBusProxy *proxy, if (proxy->priv->expected_interface) { expected_method_info = g_dbus_interface_info_lookup_method (proxy->priv->expected_interface, target_method_name); - if (!expected_method_info) - g_warning ("Trying to invoke method %s which isn't in expected interface %s", - target_method_name, target_interface_name); + if (expected_method_info == NULL) + { + g_warning ("Trying to invoke method `%s' which isn't in expected interface `%s'", + target_method_name, + target_interface_name); + } } else { diff --git a/gio/gdbusproxy.h b/gio/gdbusproxy.h index ee9f62e..fcf3f96 100644 --- a/gio/gdbusproxy.h +++ b/gio/gdbusproxy.h @@ -122,8 +122,10 @@ GDBusInterfaceInfo *g_dbus_proxy_get_interface_info (GDBusProxy *pr void g_dbus_proxy_set_interface_info (GDBusProxy *proxy, GDBusInterfaceInfo *info); GVariant *g_dbus_proxy_get_cached_property (GDBusProxy *proxy, + const gchar *property_name); +void g_dbus_proxy_set_cached_property (GDBusProxy *proxy, const gchar *property_name, - GError **error); + GVariant *value); gchar **g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy, GError **error); void g_dbus_proxy_call (GDBusProxy *proxy, diff --git a/gio/gio.symbols b/gio/gio.symbols index e772748..7f1302e 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -1663,6 +1663,7 @@ g_dbus_proxy_new g_dbus_proxy_new_finish g_dbus_proxy_new_sync g_dbus_proxy_get_cached_property +g_dbus_proxy_set_cached_property g_dbus_proxy_get_cached_property_names g_dbus_proxy_get_connection g_dbus_proxy_get_default_timeout diff --git a/gio/tests/gdbus-example-proxy-subclass.c b/gio/tests/gdbus-example-proxy-subclass.c index 6c09909..110cbdb 100644 --- a/gio/tests/gdbus-example-proxy-subclass.c +++ b/gio/tests/gdbus-example-proxy-subclass.c @@ -160,7 +160,7 @@ accounts_user_get_user_name (AccountsUser *user) GVariant *value; const gchar *ret; g_return_val_if_fail (ACCOUNTS_IS_USER (user), NULL); - value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (user), "UserName", NULL); + value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (user), "UserName"); ret = g_variant_get_string (value, NULL); g_variant_unref (value); return ret; @@ -172,7 +172,7 @@ accounts_user_get_real_name (AccountsUser *user) GVariant *value; const gchar *ret; g_return_val_if_fail (ACCOUNTS_IS_USER (user), NULL); - value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (user), "RealName", NULL); + value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (user), "RealName"); ret = g_variant_get_string (value, NULL); g_variant_unref (value); return ret; @@ -184,7 +184,7 @@ accounts_user_get_automatic_login (AccountsUser *user) GVariant *value; gboolean ret; g_return_val_if_fail (ACCOUNTS_IS_USER (user), FALSE); - value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (user), "AutomaticLogin", NULL); + value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (user), "AutomaticLogin"); ret = g_variant_get_boolean (value); g_variant_unref (value); return ret; diff --git a/gio/tests/gdbus-example-watch-proxy.c b/gio/tests/gdbus-example-watch-proxy.c index db543a8..00768bc 100644 --- a/gio/tests/gdbus-example-watch-proxy.c +++ b/gio/tests/gdbus-example-watch-proxy.c @@ -32,7 +32,7 @@ print_properties (GDBusProxy *proxy) const gchar *key = property_names[n]; GVariant *value; gchar *value_str; - value = g_dbus_proxy_get_cached_property (proxy, key, NULL); + value = g_dbus_proxy_get_cached_property (proxy, key); value_str = g_variant_print (value, TRUE); g_print (" %s -> %s\n", key, value_str); g_variant_unref (value); diff --git a/gio/tests/gdbus-peer.c b/gio/tests/gdbus-peer.c index e96666a..8571e73 100644 --- a/gio/tests/gdbus-peer.c +++ b/gio/tests/gdbus-peer.c @@ -531,8 +531,7 @@ test_peer (void) g_assert_no_error (error); g_assert (proxy != NULL); error = NULL; - value = g_dbus_proxy_get_cached_property (proxy, "PeerProperty", &error); - g_assert_no_error (error); + value = g_dbus_proxy_get_cached_property (proxy, "PeerProperty"); g_assert_cmpstr (g_variant_get_string (value, NULL), ==, "ThePropertyValue"); /* try invoking a method */ diff --git a/gio/tests/gdbus-proxy.c b/gio/tests/gdbus-proxy.c index 5df6ad5..a0fabd8 100644 --- a/gio/tests/gdbus-proxy.c +++ b/gio/tests/gdbus-proxy.c @@ -150,13 +150,11 @@ test_properties (GDBusConnection *connection, * * No need to test all properties - GVariant has already been tested */ - variant = g_dbus_proxy_get_cached_property (proxy, "y", &error); - g_assert_no_error (error); + variant = g_dbus_proxy_get_cached_property (proxy, "y"); g_assert (variant != NULL); g_assert_cmpint (g_variant_get_byte (variant), ==, 1); g_variant_unref (variant); - variant = g_dbus_proxy_get_cached_property (proxy, "o", &error); - g_assert_no_error (error); + variant = g_dbus_proxy_get_cached_property (proxy, "o"); g_assert (variant != NULL); g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "/some/path"); g_variant_unref (variant); @@ -180,11 +178,20 @@ test_properties (GDBusConnection *connection, g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); g_variant_unref (result); _g_assert_signal_received (proxy, "g-properties-changed"); - variant = g_dbus_proxy_get_cached_property (proxy, "y", &error); - g_assert_no_error (error); + variant = g_dbus_proxy_get_cached_property (proxy, "y"); g_assert (variant != NULL); g_assert_cmpint (g_variant_get_byte (variant), ==, 42); g_variant_unref (variant); + + g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_byte (142)); + variant = g_dbus_proxy_get_cached_property (proxy, "y"); + g_assert (variant != NULL); + g_assert_cmpint (g_variant_get_byte (variant), ==, 142); + g_variant_unref (variant); + + g_dbus_proxy_set_cached_property (proxy, "y", NULL); + variant = g_dbus_proxy_get_cached_property (proxy, "y"); + g_assert (variant == NULL); } /* ---------------------------------------------------------------------------------------------------- */ @@ -352,6 +359,7 @@ static const gchar *frob_dbus_interface_xml = " " " " " " + " " " " ""; static GDBusInterfaceInfo *frob_dbus_interface_info; @@ -367,6 +375,12 @@ on_proxy_appeared (GDBusConnection *connection, test_properties (connection, name, name_owner, proxy); test_signals (connection, name, name_owner, proxy); + /* This is obviously wrong but expected interface is not set so we don't fail... */ + g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_string ("error_me_out!")); + g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_byte (42)); + g_dbus_proxy_set_cached_property (proxy, "does-not-exist", g_variant_new_string ("something")); + g_dbus_proxy_set_cached_property (proxy, "does-not-exist", NULL); + /* Now repeat the method tests, with an expected interface set */ g_dbus_proxy_set_interface_info (proxy, frob_dbus_interface_info); test_methods (connection, name, name_owner, proxy); @@ -376,6 +390,24 @@ on_proxy_appeared (GDBusConnection *connection, */ test_bogus_method_return (connection, name, name_owner, proxy); + /* Also check that we complain if setting a cached property of the wrong type */ + if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR)) + { + g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_string ("error_me_out!")); + } + g_test_trap_assert_stderr ("*Trying to set property y of type s but according to the expected interface the type is y*"); + g_test_trap_assert_failed(); + + if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR)) + { + g_dbus_proxy_set_cached_property (proxy, "does-not-exist", g_variant_new_string ("something")); + } + g_test_trap_assert_stderr ("*Trying to lookup property does-not-exist which isn't in expected interface com.example.Frob*"); + g_test_trap_assert_failed(); + + /* this should work, however (since the type is correct) */ + g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_byte (42)); + g_main_loop_quit (loop); } -- 2.7.4