Commit f909cb5b authored by David Zeuthen's avatar David Zeuthen

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.
parent b690e637
...@@ -2490,8 +2490,9 @@ g_dbus_proxy_get_object_path ...@@ -2490,8 +2490,9 @@ g_dbus_proxy_get_object_path
g_dbus_proxy_get_interface_name g_dbus_proxy_get_interface_name
g_dbus_proxy_get_default_timeout g_dbus_proxy_get_default_timeout
g_dbus_proxy_set_default_timeout g_dbus_proxy_set_default_timeout
g_dbus_proxy_get_cached_property_names
g_dbus_proxy_get_cached_property 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_set_interface_info
g_dbus_proxy_get_interface_info g_dbus_proxy_get_interface_info
g_dbus_proxy_call g_dbus_proxy_call
......
...@@ -490,66 +490,149 @@ g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy, ...@@ -490,66 +490,149 @@ g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy,
return names; 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: * g_dbus_proxy_get_cached_property:
* @proxy: A #GDBusProxy. * @proxy: A #GDBusProxy.
* @property_name: Property name. * @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 * Looks up the value for a property from the cache. This call does no
* in response to <literal>org.freedesktop.DBus.Properties.PropertiesChanged</literal> * blocking IO.
* D-Bus signals (which also causes #GDBusProxy::g-properties-changed to be emitted).
* *
* However, for properties for which said D-Bus signal is not emitted, you * If @proxy has an expected interface (see
* can catch other signals and modify the returned variant accordingly (remember to emit * #GDBusProxy:g-interface-info), then @property_name (for existence)
* #GDBusProxy::g-properties-changed yourself). * is checked against it.
* *
* Returns: A reference to the #GVariant instance that holds the value for @property_name or * Returns: A reference to the #GVariant instance that holds the value
* %NULL if @error is set. Free the reference with g_variant_unref(). * 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 * Since: 2.26
*/ */
GVariant * GVariant *
g_dbus_proxy_get_cached_property (GDBusProxy *proxy, g_dbus_proxy_get_cached_property (GDBusProxy *proxy,
const gchar *property_name, const gchar *property_name)
GError **error)
{ {
GVariant *value; GVariant *value;
g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL); g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
g_return_val_if_fail (property_name != NULL, 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); value = g_hash_table_lookup (proxy->priv->properties, property_name);
if (value == NULL) if (value == NULL)
{ {
g_set_error (error, const GDBusPropertyInfo *info;
G_IO_ERROR, info = lookup_property_info_or_warn (proxy, property_name);
G_IO_ERROR_FAILED, /* no difference */
_("No property with name %s"),
property_name);
goto out; goto out;
} }
g_variant_ref (value); g_variant_ref (value);
out: out:
return value; 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
* <literal>org.freedesktop.DBus.Properties.PropertiesChanged</literal>
* 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
* <literal>ChatroomParticipants</literal> 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 <literal>ChatroomParticipantJoined(String
* name)</literal> and <literal>ChatroomParticipantParted(String
* name)</literal>.
*
* 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 static void
...@@ -1306,13 +1389,15 @@ lookup_method_info_or_warn (GDBusProxy *proxy, ...@@ -1306,13 +1389,15 @@ lookup_method_info_or_warn (GDBusProxy *proxy,
{ {
const GDBusMethodInfo *info; const GDBusMethodInfo *info;
if (!proxy->priv->expected_interface) if (proxy->priv->expected_interface == NULL)
return NULL; return NULL;
info = g_dbus_interface_info_lookup_method (proxy->priv->expected_interface, method_name); info = g_dbus_interface_info_lookup_method (proxy->priv->expected_interface, method_name);
if (!info) if (info == NULL)
g_warning ("Trying to invoke method %s which isn't in expected interface %s", {
method_name, proxy->priv->expected_interface->name); g_warning ("Trying to invoke method %s which isn't in expected interface %s",
method_name, proxy->priv->expected_interface->name);
}
return info; return info;
} }
...@@ -1586,9 +1671,12 @@ g_dbus_proxy_call_sync (GDBusProxy *proxy, ...@@ -1586,9 +1671,12 @@ g_dbus_proxy_call_sync (GDBusProxy *proxy,
if (proxy->priv->expected_interface) if (proxy->priv->expected_interface)
{ {
expected_method_info = g_dbus_interface_info_lookup_method (proxy->priv->expected_interface, target_method_name); expected_method_info = g_dbus_interface_info_lookup_method (proxy->priv->expected_interface, target_method_name);
if (!expected_method_info) 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); g_warning ("Trying to invoke method `%s' which isn't in expected interface `%s'",
target_method_name,
target_interface_name);
}
} }
else else
{ {
......
...@@ -122,8 +122,10 @@ GDBusInterfaceInfo *g_dbus_proxy_get_interface_info (GDBusProxy *pr ...@@ -122,8 +122,10 @@ GDBusInterfaceInfo *g_dbus_proxy_get_interface_info (GDBusProxy *pr
void g_dbus_proxy_set_interface_info (GDBusProxy *proxy, void g_dbus_proxy_set_interface_info (GDBusProxy *proxy,
GDBusInterfaceInfo *info); GDBusInterfaceInfo *info);
GVariant *g_dbus_proxy_get_cached_property (GDBusProxy *proxy, 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, const gchar *property_name,
GError **error); GVariant *value);
gchar **g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy, gchar **g_dbus_proxy_get_cached_property_names (GDBusProxy *proxy,
GError **error); GError **error);
void g_dbus_proxy_call (GDBusProxy *proxy, void g_dbus_proxy_call (GDBusProxy *proxy,
......
...@@ -1663,6 +1663,7 @@ g_dbus_proxy_new ...@@ -1663,6 +1663,7 @@ g_dbus_proxy_new
g_dbus_proxy_new_finish g_dbus_proxy_new_finish
g_dbus_proxy_new_sync g_dbus_proxy_new_sync
g_dbus_proxy_get_cached_property g_dbus_proxy_get_cached_property
g_dbus_proxy_set_cached_property
g_dbus_proxy_get_cached_property_names g_dbus_proxy_get_cached_property_names
g_dbus_proxy_get_connection g_dbus_proxy_get_connection
g_dbus_proxy_get_default_timeout g_dbus_proxy_get_default_timeout
......
...@@ -160,7 +160,7 @@ accounts_user_get_user_name (AccountsUser *user) ...@@ -160,7 +160,7 @@ accounts_user_get_user_name (AccountsUser *user)
GVariant *value; GVariant *value;
const gchar *ret; const gchar *ret;
g_return_val_if_fail (ACCOUNTS_IS_USER (user), NULL); 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); ret = g_variant_get_string (value, NULL);
g_variant_unref (value); g_variant_unref (value);
return ret; return ret;
...@@ -172,7 +172,7 @@ accounts_user_get_real_name (AccountsUser *user) ...@@ -172,7 +172,7 @@ accounts_user_get_real_name (AccountsUser *user)
GVariant *value; GVariant *value;
const gchar *ret; const gchar *ret;
g_return_val_if_fail (ACCOUNTS_IS_USER (user), NULL); 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); ret = g_variant_get_string (value, NULL);
g_variant_unref (value); g_variant_unref (value);
return ret; return ret;
...@@ -184,7 +184,7 @@ accounts_user_get_automatic_login (AccountsUser *user) ...@@ -184,7 +184,7 @@ accounts_user_get_automatic_login (AccountsUser *user)
GVariant *value; GVariant *value;
gboolean ret; gboolean ret;
g_return_val_if_fail (ACCOUNTS_IS_USER (user), FALSE); 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); ret = g_variant_get_boolean (value);
g_variant_unref (value); g_variant_unref (value);
return ret; return ret;
......
...@@ -32,7 +32,7 @@ print_properties (GDBusProxy *proxy) ...@@ -32,7 +32,7 @@ print_properties (GDBusProxy *proxy)
const gchar *key = property_names[n]; const gchar *key = property_names[n];
GVariant *value; GVariant *value;
gchar *value_str; 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); value_str = g_variant_print (value, TRUE);
g_print (" %s -> %s\n", key, value_str); g_print (" %s -> %s\n", key, value_str);
g_variant_unref (value); g_variant_unref (value);
......
...@@ -531,8 +531,7 @@ test_peer (void) ...@@ -531,8 +531,7 @@ test_peer (void)
g_assert_no_error (error); g_assert_no_error (error);
g_assert (proxy != NULL); g_assert (proxy != NULL);
error = NULL; error = NULL;
value = g_dbus_proxy_get_cached_property (proxy, "PeerProperty", &error); value = g_dbus_proxy_get_cached_property (proxy, "PeerProperty");
g_assert_no_error (error);
g_assert_cmpstr (g_variant_get_string (value, NULL), ==, "ThePropertyValue"); g_assert_cmpstr (g_variant_get_string (value, NULL), ==, "ThePropertyValue");
/* try invoking a method */ /* try invoking a method */
......
...@@ -150,13 +150,11 @@ test_properties (GDBusConnection *connection, ...@@ -150,13 +150,11 @@ test_properties (GDBusConnection *connection,
* *
* No need to test all properties - GVariant has already been tested * No need to test all properties - GVariant has already been tested
*/ */
variant = g_dbus_proxy_get_cached_property (proxy, "y", &error); variant = g_dbus_proxy_get_cached_property (proxy, "y");
g_assert_no_error (error);
g_assert (variant != NULL); g_assert (variant != NULL);
g_assert_cmpint (g_variant_get_byte (variant), ==, 1); g_assert_cmpint (g_variant_get_byte (variant), ==, 1);
g_variant_unref (variant); g_variant_unref (variant);
variant = g_dbus_proxy_get_cached_property (proxy, "o", &error); variant = g_dbus_proxy_get_cached_property (proxy, "o");
g_assert_no_error (error);
g_assert (variant != NULL); g_assert (variant != NULL);
g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "/some/path"); g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "/some/path");
g_variant_unref (variant); g_variant_unref (variant);
...@@ -180,11 +178,20 @@ test_properties (GDBusConnection *connection, ...@@ -180,11 +178,20 @@ test_properties (GDBusConnection *connection,
g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
g_variant_unref (result); g_variant_unref (result);
_g_assert_signal_received (proxy, "g-properties-changed"); _g_assert_signal_received (proxy, "g-properties-changed");
variant = g_dbus_proxy_get_cached_property (proxy, "y", &error); variant = g_dbus_proxy_get_cached_property (proxy, "y");
g_assert_no_error (error);
g_assert (variant != NULL); g_assert (variant != NULL);
g_assert_cmpint (g_variant_get_byte (variant), ==, 42); g_assert_cmpint (g_variant_get_byte (variant), ==, 42);
g_variant_unref (variant); 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 = ...@@ -352,6 +359,7 @@ static const gchar *frob_dbus_interface_xml =
" <method name='Sleep'>" " <method name='Sleep'>"
" <arg type='i' name='timeout' direction='in'/>" " <arg type='i' name='timeout' direction='in'/>"
" </method>" " </method>"
" <property name='y' type='y' access='readwrite'/>"
" </interface>" " </interface>"
"</node>"; "</node>";
static GDBusInterfaceInfo *frob_dbus_interface_info; static GDBusInterfaceInfo *frob_dbus_interface_info;
...@@ -367,6 +375,12 @@ on_proxy_appeared (GDBusConnection *connection, ...@@ -367,6 +375,12 @@ on_proxy_appeared (GDBusConnection *connection,
test_properties (connection, name, name_owner, proxy); test_properties (connection, name, name_owner, proxy);
test_signals (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 */ /* Now repeat the method tests, with an expected interface set */
g_dbus_proxy_set_interface_info (proxy, frob_dbus_interface_info); g_dbus_proxy_set_interface_info (proxy, frob_dbus_interface_info);
test_methods (connection, name, name_owner, proxy); test_methods (connection, name, name_owner, proxy);
...@@ -376,6 +390,24 @@ on_proxy_appeared (GDBusConnection *connection, ...@@ -376,6 +390,24 @@ on_proxy_appeared (GDBusConnection *connection,
*/ */
test_bogus_method_return (connection, name, name_owner, proxy); 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); g_main_loop_quit (loop);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment