r6881 - in dumbhippo/trunk/client: common/ddm common/hippo linux/src



Author: otaylor
Date: 2007-11-08 14:39:56 -0600 (Thu, 08 Nov 2007)
New Revision: 6881

Modified:
   dumbhippo/trunk/client/common/ddm/ddm-data-fetch.c
   dumbhippo/trunk/client/common/ddm/ddm-data-model-dbus.c
   dumbhippo/trunk/client/common/ddm/ddm-data-model.c
   dumbhippo/trunk/client/common/ddm/ddm-data-model.h
   dumbhippo/trunk/client/common/ddm/ddm-data-query-internal.h
   dumbhippo/trunk/client/common/ddm/ddm-data-query.c
   dumbhippo/trunk/client/common/ddm/ddm-data-query.h
   dumbhippo/trunk/client/common/ddm/ddm-work-item.c
   dumbhippo/trunk/client/common/ddm/hippo-dbus-helper.c
   dumbhippo/trunk/client/common/ddm/static-file-backend.c
   dumbhippo/trunk/client/common/ddm/test-ddm.c
   dumbhippo/trunk/client/common/ddm/test-multipart-fetch.c
   dumbhippo/trunk/client/common/ddm/test-notification.c
   dumbhippo/trunk/client/common/ddm/test-static-file-backend.c
   dumbhippo/trunk/client/common/hippo/hippo-connection.c
   dumbhippo/trunk/client/common/hippo/hippo-connection.h
   dumbhippo/trunk/client/common/hippo/hippo-data-model-backend.c
   dumbhippo/trunk/client/linux/src/hippo-dbus-model-client.c
   dumbhippo/trunk/client/linux/src/hippo-dbus-model.c
   dumbhippo/trunk/client/linux/src/hippo-platform-impl.c
Log:
ddm-data-mode.c hippo-data-model-backend.c ddm-dbus-model.c: Replace
  the idea of connected with "ready" ... a model is initially not
  ready, deterministically becomes ready and emits ::ready at that
  point. ::ready can be emitted later to mean start over.

ddm-data-model.[ch] hippo-data-model-backend.c ddm-dbus-model.c: Add 
  ddm_data_model_get_{global,self}_resource special-casing to avoid
  tedious duplicated logic in every handler for ::ready		    

ddm-data-model.[ch] test-multipart-fetch.c test-notification.c
  test-static-file-backend.c: Rename
  ddm_data_model_query_resource() to query_resource_by_id(), make
  query_resource() take a DDMDataResource instead.

hippo-dbus-model.c ddm-data-model-dbus.c: Change to the D-BUS protocol
  ConnectedChanged signal removed, replaced with Ready. Connected
  property replaced with Ready. SelfId and WebBaseUrl properties removed.

ddm-data-query.[ch] ddm-data-query-internal.h: Add 
  ddm_data_query_error_async() to have a generic way to error a query
  without causing unexpected reentrancy.

ddm-data-model-dbus.c: Remove queuing of outgoing queries; queries before
  connection are report errors.

hippo-connection.[ch] hippo-platform-impl.c: Add a REDIRECTING state,
  so that the redircection from the load balancer doesn't look like a
  disconnection.

hippo-dbus-model-client.c: Reference the resource we store in a
  DataClientConnection.

ddm-data-query.c ddm-work-item.c: Move detection of completely failed
  queries (resources end up with class_id == NULL) into ddm-work-item.c.

ddm-work-item.c static-file-backend.c: Fix a cut-and-pasted bug bug with
  descending while iterating a fetch.

ddm-data-fetch.c: Fix a bug in ddm_data_fetch_merge()

hippo-dbus-helper.c: Call the 'unavailable' call back if the service is
  unavailable on startup (needs some more work to handle activation
  properly)

test-ddm.c: Adapt to the new C API.


Modified: dumbhippo/trunk/client/common/ddm/ddm-data-fetch.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-data-fetch.c	2007-11-08 20:33:40 UTC (rev 6880)
+++ dumbhippo/trunk/client/common/ddm/ddm-data-fetch.c	2007-11-08 20:39:56 UTC (rev 6881)
@@ -503,7 +503,7 @@
             fetch_property_copy(&result->properties[total_properties], &other->properties[j]);
             j++;
         } else {
-            fetch_property_merge(&result->properties[total_properties], &fetch->properties[i], &fetch->properties[i]);
+            fetch_property_merge(&result->properties[total_properties], &fetch->properties[i], &other->properties[j]);
             i++;
             j++;
         }

Modified: dumbhippo/trunk/client/common/ddm/ddm-data-model-dbus.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-data-model-dbus.c	2007-11-08 20:33:40 UTC (rev 6880)
+++ dumbhippo/trunk/client/common/ddm/ddm-data-model-dbus.c	2007-11-08 20:39:56 UTC (rev 6881)
@@ -13,33 +13,16 @@
 
 #include <dbus/dbus.h>
 
-typedef enum {
-    PENDING_REQUEST_QUERY,
-    PENDING_REQUEST_UPDATE
-} PendingRequestType;
-
 typedef struct {
-    PendingRequestType type;
-    DDMDataQuery *query;
-} PendingRequest;
-
-typedef struct {
     int             refcount;
     char           *path;
     DDMDataModel   *ddm_model;
     DBusConnection *connection;
     HippoDBusProxy *engine_proxy;
+    gboolean        engine_ready;
     HippoDBusProxy *engine_props_proxy;
-    GSList *pending_requests;
 } DBusModel;
 
-static void ddm_dbus_send_query   (DDMDataModel *ddm_model,
-                                   DDMDataQuery *query,
-                                   void         *backend_data);
-static void ddm_dbus_send_update  (DDMDataModel *ddm_model,
-                                   DDMDataQuery *query,
-                                   void         *backend_data);
-
 static void
 model_ref(DBusModel *dbus_model)
 {
@@ -70,59 +53,6 @@
     return dbus_model;
 }
 
-static void
-model_add_pending_query(DBusModel    *dbus_model,
-                        DDMDataQuery *query)
-{
-    PendingRequest *pr;
-
-    g_debug("delaying Query to org.freedesktop.od.Engine until we connect");
-    
-    pr = g_new0(PendingRequest, 1);
-    pr->type = PENDING_REQUEST_QUERY;
-    pr->query = query;
-
-    dbus_model->pending_requests = g_slist_append(dbus_model->pending_requests, pr);
-}
-
-static void
-model_add_pending_update(DBusModel    *dbus_model,
-                         DDMDataQuery *query)
-{
-    PendingRequest *pr;
-
-    g_debug("delaying Update to org.freedesktop.od.Engine until we connect");
-    
-    pr = g_new0(PendingRequest, 1);
-    pr->type = PENDING_REQUEST_UPDATE;
-    pr->query = query;
-    
-    dbus_model->pending_requests = g_slist_append(dbus_model->pending_requests, pr);
-}
-
-static void
-model_send_pending(DBusModel *dbus_model)
-{
-    g_debug("Sending %d pending requests to org.freedesktop.od.Engine",
-            g_slist_length(dbus_model->pending_requests));
-    
-    while (dbus_model->pending_requests != NULL) {
-        PendingRequest *pr = dbus_model->pending_requests->data;
-        dbus_model->pending_requests = g_slist_remove(dbus_model->pending_requests,
-                                                      dbus_model->pending_requests->data);
-        
-        if (pr->type == PENDING_REQUEST_QUERY) {
-            ddm_dbus_send_query(dbus_model->ddm_model, pr->query, NULL);
-            g_free(pr);
-        } else if (pr->type == PENDING_REQUEST_UPDATE) {
-            ddm_dbus_send_update(dbus_model->ddm_model, pr->query, NULL);
-            g_free(pr);
-        } else {
-            g_warning("unknown pending request type");
-        }
-    }
-}
-
 static gboolean
 read_data_attributes(DBusMessageIter    *prop_struct_iter,
                      DDMDataUpdate      *update_type_p,
@@ -497,19 +427,97 @@
     { NULL }
 };
 
+/* Common handling if an error occurs in the initialization path or we lose the
+ * connection to the server; we change the state to be offline and if we were
+ * still in the "not yet ready" state, signal the end of initialization
+ */
 static void
-handle_get_connected_reply(DBusMessage *reply,
-                           void        *data)
+dbus_model_set_offline(DBusModel *dbus_model)
 {
+    DDMDataResource *global_resource;
+    DDMDataValue value;
+
+    global_resource = ddm_data_model_ensure_resource(dbus_model->ddm_model,
+                                                     DDM_GLOBAL_RESOURCE, DDM_GLOBAL_RESOURCE_CLASS);
+    ddm_data_model_set_global_resource(dbus_model->ddm_model, global_resource);
+    
+    value.type = DDM_DATA_BOOLEAN;
+    value.u.string = FALSE;
+
+    ddm_data_resource_update_property(global_resource,
+                                      ddm_qname_get(DDM_GLOBAL_RESOURCE_CLASS, "online"),
+                                      DDM_DATA_UPDATE_REPLACE,
+                                      DDM_DATA_CARDINALITY_1,
+                                      FALSE, NULL,
+                                      &value);
+    
+    if (!ddm_data_model_is_ready(dbus_model->ddm_model))
+        ddm_data_model_signal_ready(dbus_model->ddm_model);
+}
+
+static void
+on_initial_query_success(DDMDataResource *resource,
+                         gpointer         user_data)
+{
+    DBusModel *dbus_model = user_data;
+    DDMDataResource *self_resource = NULL;
+
+    ddm_data_resource_get(resource,
+                          "self", DDM_DATA_RESOURCE, &self_resource,
+                          NULL);
+
+    ddm_data_model_set_self_resource(dbus_model->ddm_model, self_resource);
+
+    ddm_data_model_signal_ready(dbus_model->ddm_model);
+}
+
+
+static void
+on_initial_query_error(DDMDataError  error,
+                       const char   *message,
+                       gpointer      user_data)
+{
+    DBusModel *dbus_model = user_data;
+
+    dbus_model_set_offline(dbus_model);
+}
+
+static void
+dbus_model_do_initial_query(DBusModel *dbus_model)
+{
+    DDMDataQuery *query;
+    DDMDataResource *global_resource;
+
+    dbus_model->engine_ready = TRUE;
+
+    ddm_data_model_reset(dbus_model->ddm_model);
+
+    global_resource = ddm_data_model_ensure_resource(dbus_model->ddm_model,
+                                                     DDM_GLOBAL_RESOURCE, DDM_GLOBAL_RESOURCE_CLASS);
+    ddm_data_model_set_global_resource(dbus_model->ddm_model, global_resource);
+
+    query = ddm_data_model_query_resource(dbus_model->ddm_model, global_resource,
+                                          "self +;webBaseUrl;online");
+    ddm_data_query_set_single_handler(query, on_initial_query_success, dbus_model);
+    ddm_data_query_set_error_handler(query, on_initial_query_error, dbus_model);
+}
+
+static void
+handle_get_ready_reply(DBusMessage *reply,
+                       void        *data)
+{
     DBusModel *dbus_model = data;
     DBusMessageIter variant_iter;
     DBusMessageIter toplevel_iter;
-    dbus_bool_t connected;
+    dbus_bool_t ready;
 
     if (dbus_model->ddm_model == NULL) /* happens if the reply comes in after we nuke the model */
         return;
     
     if (dbus_message_get_type(reply) != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
+        g_debug("Get of ready state failed");
+
+        dbus_model_set_offline(dbus_model);
         return;        
     }
 
@@ -522,21 +530,17 @@
     dbus_message_iter_recurse(&toplevel_iter, &variant_iter);
 
     if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_BOOLEAN) {
-        g_warning("Expecting Connected prop to have type boolean");
+        g_warning("Expecting Ready prop to have type boolean");
         return;
     }
 
-    connected = FALSE;
-    dbus_message_iter_get_basic(&variant_iter, &connected);
+    ready = FALSE;
+    dbus_message_iter_get_basic(&variant_iter, &ready);
 
-    /* protect against a race / duplicate effort */
-    if (dbus_model->connection == NULL ||
-        !dbus_connection_get_is_connected(dbus_model->connection))
-        connected = FALSE;
-    
-    ddm_data_model_set_connected(dbus_model->ddm_model, connected);
-    if (connected)
-        model_send_pending(dbus_model);
+    g_debug("Got Ready state, ready=%d", ready);
+        
+    if (ready)
+        dbus_model_do_initial_query(dbus_model);
 }
 
 static void
@@ -555,6 +559,7 @@
                                                     unique_name,
                                                     "/org/freedesktop/od/data_model",
                                                     "org.freedesktop.od.Model");
+    dbus_model->engine_ready = FALSE;
     
     dbus_model->engine_props_proxy = hippo_dbus_proxy_new(dbus_model->connection,
                                                           unique_name,
@@ -562,11 +567,11 @@
                                                           DBUS_INTERFACE_PROPERTIES);
 
     iface = "org.freedesktop.od.Model";
-    method = "Connected";
+    method = "Ready";
     model_ref(dbus_model);
     hippo_dbus_proxy_call_method_async(dbus_model->engine_props_proxy,
                                        "Get",
-                                       handle_get_connected_reply,
+                                       handle_get_ready_reply,
                                        dbus_model,
                                        (GFreeFunc) model_unref,
                                        DBUS_TYPE_STRING,
@@ -584,17 +589,23 @@
 {
     DBusModel *dbus_model = data;
 
-    g_debug("org.freedesktop.od.Engine went away");
-    
-    g_assert(dbus_model->engine_props_proxy != NULL);
-    
-    hippo_dbus_proxy_unref(dbus_model->engine_props_proxy);
-    dbus_model->engine_props_proxy = NULL;
+    if (unique_name)
+        g_debug("org.freedesktop.od.Engine went away");
+    else
+        g_debug("org.freedesktop.od.Engine unavailable on startup");
 
-    hippo_dbus_proxy_unref(dbus_model->engine_proxy);
-    dbus_model->engine_proxy = NULL;
+    if (dbus_model->engine_props_proxy != NULL) {
+        hippo_dbus_proxy_unref(dbus_model->engine_props_proxy);
+        dbus_model->engine_props_proxy = NULL;
+    }
+
+    if (dbus_model->engine_proxy != NULL) {
+        hippo_dbus_proxy_unref(dbus_model->engine_proxy);
+        dbus_model->engine_proxy = NULL;
+        dbus_model->engine_ready = FALSE;
+    }
     
-    ddm_data_model_set_connected(dbus_model->ddm_model, FALSE);
+    dbus_model_set_offline(dbus_model);
 }
 
 static HippoDBusServiceTracker engine_tracker = {
@@ -604,35 +615,24 @@
 };
 
 static void
-handle_connected_changed(DBusConnection *connection,
-                         DBusMessage    *message,
-                         void           *data)
+handle_ready(DBusConnection *connection,
+             DBusMessage    *message,
+             void           *data)
 {
     DBusModel *dbus_model = data;
-    dbus_bool_t is_connected;
-    const char *self_id;
 
-    is_connected = FALSE;
-    self_id = NULL;
-
     if (!dbus_message_get_args(message, NULL,
-                               DBUS_TYPE_BOOLEAN, &is_connected,
-                               DBUS_TYPE_STRING, &self_id,
                                DBUS_TYPE_INVALID)) {
-        g_warning("bad args to ConnectedChanged signal");
+        g_warning("bad args to Ready signal");
         return;
     }
 
-    if (is_connected)
-        ddm_data_model_reset(dbus_model->ddm_model);
-    ddm_data_model_set_connected(dbus_model->ddm_model, is_connected);
-    if (is_connected)
-        model_send_pending(dbus_model);
+    g_debug("Got Ready signal from data model engine");
+    dbus_model_do_initial_query(dbus_model);
 }
 
 static HippoDBusSignalTracker engine_signal_handlers[] = {
-    { "org.freedesktop.od.Model", "ConnectedChanged",
-      handle_connected_changed },
+    { "org.freedesktop.od.Model", "Ready", handle_ready },
     { NULL, NULL, NULL }
 };
 
@@ -884,17 +884,24 @@
     dbus_model = get_dbus_model(ddm_model);
 
     if (dbus_model->engine_proxy == NULL) {
-        model_add_pending_query(dbus_model, query);
+        ddm_data_query_error_async (query,
+                                    DDM_DATA_ERROR_NO_CONNECTION,
+                                    "No connection to data model engine");
         return;
     }
 
+    if (!dbus_model->engine_ready) {
+        ddm_data_query_error_async (query,
+                                    DDM_DATA_ERROR_NO_CONNECTION,
+                                    "Data model engine is not ready");
+        return;
+    }
+
     g_debug("sending Query to org.freedesktop.od.Engine %s#%s fetch %s",
             ddm_data_query_get_qname(query)->uri,
             ddm_data_query_get_qname(query)->name,
             ddm_data_query_get_fetch_string(query));
     
-    g_assert(dbus_model->engine_proxy != NULL); /* since connection != NULL */
-
     qd = g_new(QueryData, 1);
 
     model_ref(dbus_model);
@@ -979,14 +986,21 @@
     dbus_model = get_dbus_model(ddm_model);
 
     if (dbus_model->engine_proxy == NULL) {
-        model_add_pending_update(dbus_model, query);
+        ddm_data_query_error_async (query,
+                                    DDM_DATA_ERROR_NO_CONNECTION,
+                                    "No connection to data model engine");
         return;
     }
 
+    if (!dbus_model->engine_ready) {
+        ddm_data_query_error_async (query,
+                                    DDM_DATA_ERROR_NO_CONNECTION,
+                                    "Data model engine is not ready");
+        return;
+    }
+
     g_debug("sending Update to org.freedesktop.od.Engine");
     
-    g_assert(dbus_model->engine_proxy != NULL); /* since connection != NULL */
-
     qd = g_new(QueryData, 1);
 
     model_ref(dbus_model);
@@ -1007,7 +1021,7 @@
     ddm_dbus_remove_model,
     ddm_dbus_send_query,
     ddm_dbus_send_update,
-    NULL,
+    NULL
 };
 
 const DDMDataModelBackend*

Modified: dumbhippo/trunk/client/common/ddm/ddm-data-model.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-data-model.c	2007-11-08 20:33:40 UTC (rev 6880)
+++ dumbhippo/trunk/client/common/ddm/ddm-data-model.c	2007-11-08 20:39:56 UTC (rev 6881)
@@ -29,6 +29,9 @@
     GHashTable *resources;
     GHashTable *changed_resources;
 
+    DDMDataResource *global_resource;
+    DDMDataResource *self_resource;
+
     GQueue *work_items;
 
     gint64 next_query_serial;
@@ -36,6 +39,7 @@
 
     guint flush_idle;
 
+    guint ready : 1;
     guint connected : 1;
 };
 
@@ -45,6 +49,7 @@
 
 enum {
     CONNECTED_CHANGED,
+    READY,
     LAST_SIGNAL
 };
 
@@ -82,6 +87,15 @@
                       NULL, NULL,
                       g_cclosure_marshal_VOID__BOOLEAN,
                       G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+    
+    signals[READY] =
+        g_signal_new ("ready",
+                      G_TYPE_FROM_CLASS (object_class),
+                      G_SIGNAL_RUN_LAST,
+                      0,
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE, 0);
 }
 
 static void
@@ -112,10 +126,14 @@
     /* FIXME: cancel and remove everything still in here */
     g_queue_free(model->work_items);
     
-    /* FIXME: actually clean up the resources */
     g_hash_table_destroy(model->resources);
     g_hash_table_destroy(model->changed_resources);
 
+    if (model->global_resource)
+        ddm_data_resource_unref(model->global_resource);
+    if (model->self_resource)
+        ddm_data_resource_unref(model->self_resource);
+
     g_object_unref(model->local_client);
 
     G_OBJECT_CLASS(ddm_data_model_parent_class)->finalize(object);
@@ -163,6 +181,14 @@
     return model->connected;
 }
 
+gboolean
+ddm_data_model_is_ready(DDMDataModel   *model)
+{
+    g_return_val_if_fail(DDM_IS_DATA_MODEL(model), FALSE);
+
+    return model->ready;
+}
+
 static GHashTable *
 params_from_valist(va_list vap)
 {
@@ -234,8 +260,10 @@
         GSList *results;
         
         if (resource_id == NULL) {
-            /* FIXME: ERROR ASYNC HERE */
-            return NULL;
+            ddm_data_query_error_async (query,
+                                        DDM_DATA_ERROR_BAD_REQUEST,
+                                        "resourceId parameter is required for http://mugshot.org/p/system#getResource";);
+            return query;
         }
 
         /* The reason why we ensure (without specifying a class_id) here is so that
@@ -322,13 +350,29 @@
 }
 
 DDMDataQuery *
-ddm_data_model_query_resource(DDMDataModel *model,
-                              const char     *resource_id,
-                              const char     *fetch)
+ddm_data_model_query_resource (DDMDataModel    *model,
+                               DDMDataResource *resource,
+                               const char      *fetch)
 {
     g_return_val_if_fail (DDM_IS_DATA_MODEL(model), NULL);
+    g_return_val_if_fail (resource != NULL, NULL);
+    g_return_val_if_fail (fetch != NULL, NULL);
 
     return data_model_query_internal(model, "http://mugshot.org/p/system#getResource";, fetch, FALSE,
+                                     "resourceId", ddm_data_resource_get_resource_id(resource),
+                                     NULL);
+}
+
+DDMDataQuery *
+ddm_data_model_query_resource_by_id(DDMDataModel *model,
+                                    const char     *resource_id,
+                                    const char     *fetch)
+{
+    g_return_val_if_fail (DDM_IS_DATA_MODEL(model), NULL);
+    g_return_val_if_fail (resource_id != NULL, NULL);
+    g_return_val_if_fail (fetch != NULL, NULL);
+
+    return data_model_query_internal(model, "http://mugshot.org/p/system#getResource";, fetch, FALSE,
                                      "resourceId", resource_id,
                                      NULL);
 }
@@ -467,6 +511,16 @@
 ddm_data_model_reset (DDMDataModel *model)
 {
     g_hash_table_foreach_remove(model->resources, model_reset_foreach, NULL);
+
+    if (model->global_resource != NULL && !ddm_data_resource_is_local(model->global_resource)) {
+        ddm_data_resource_unref(model->global_resource);
+        model->global_resource = NULL;
+    }
+    
+    if (model->self_resource != NULL && !ddm_data_resource_is_local(model->self_resource)) {
+        ddm_data_resource_unref(model->self_resource);
+        model->self_resource = NULL;
+    }
 }
 
 void
@@ -482,6 +536,13 @@
 }
 
 void
+ddm_data_model_signal_ready (DDMDataModel *model)
+{
+    model->ready = TRUE;
+    g_signal_emit(G_OBJECT(model), signals[READY], 0);
+}
+
+void
 _ddm_data_model_mark_changed(DDMDataModel    *model,
                              DDMDataResource *resource)
 {
@@ -686,3 +747,46 @@
     return model->local_client;
 }
 
+void
+ddm_data_model_set_global_resource (DDMDataModel    *model,
+                                    DDMDataResource *global_resource)
+{
+    if (global_resource == model->global_resource)
+        return;
+
+    if (model->global_resource)
+        ddm_data_resource_unref(model->global_resource);
+    
+    model->global_resource = global_resource;
+    
+    if (model->global_resource)
+        ddm_data_resource_ref(model->global_resource);
+}
+
+void
+ddm_data_model_set_self_resource (DDMDataModel    *model,
+                                  DDMDataResource *self_resource)
+{
+    if (self_resource == model->self_resource)
+        return;
+
+    if (model->self_resource)
+        ddm_data_resource_unref(model->self_resource);
+    
+    model->self_resource = self_resource;
+    
+    if (model->self_resource)
+        ddm_data_resource_ref(model->self_resource);
+}
+
+DDMDataResource *
+ddm_data_model_get_global_resource(DDMDataModel *model)
+{
+    return model->global_resource;
+}
+
+DDMDataResource *
+ddm_data_model_get_self_resource(DDMDataModel *model)
+{
+    return model->self_resource;
+}

Modified: dumbhippo/trunk/client/common/ddm/ddm-data-model.h
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-data-model.h	2007-11-08 20:33:40 UTC (rev 6880)
+++ dumbhippo/trunk/client/common/ddm/ddm-data-model.h	2007-11-08 20:39:56 UTC (rev 6881)
@@ -39,6 +39,11 @@
 /* Used testing purposes; you can't call query or update on such a backend */
 DDMDataModel *ddm_data_model_new_no_backend     (void);
 gboolean      ddm_data_model_get_connected      (DDMDataModel   *model);
+gboolean      ddm_data_model_is_ready           (DDMDataModel   *model);
+
+DDMDataResource *ddm_data_model_get_global_resource (DDMDataModel *model);
+DDMDataResource *ddm_data_model_get_self_resource   (DDMDataModel *model);
+
 DDMDataQuery *ddm_data_model_query              (DDMDataModel   *model,
                                                  const char     *method,
                                                  const char     *fetch,
@@ -47,9 +52,14 @@
                                                  const char     *method,
                                                  const char     *fetch,
                                                  GHashTable     *params);
-DDMDataQuery *ddm_data_model_query_resource     (DDMDataModel   *model,
-                                                 const char     *resource_id,
-                                                 const char     *fetch);
+
+DDMDataQuery *ddm_data_model_query_resource       (DDMDataModel    *model,
+                                                   DDMDataResource *resource,
+                                                   const char      *fetch);
+DDMDataQuery *ddm_data_model_query_resource_by_id (DDMDataModel    *model,
+                                                   const char      *resource_id,
+                                                   const char      *fetch);
+
 DDMDataQuery *ddm_data_model_update             (DDMDataModel   *model,
                                                  const char     *method,
                                                  ...) G_GNUC_NULL_TERMINATED;
@@ -89,7 +99,13 @@
 
 void ddm_data_model_set_connected  (DDMDataModel *model,
                                     gboolean      connected);
+void ddm_data_model_signal_ready   (DDMDataModel *model);
 
+void ddm_data_model_set_global_resource (DDMDataModel    *model,
+                                         DDMDataResource *global_resource);
+void ddm_data_model_set_self_resource   (DDMDataModel    *model,
+                                         DDMDataResource *self_resource);
+
 G_END_DECLS
 
 #endif /* __DDM_DATA_MODEL_H__ */

Modified: dumbhippo/trunk/client/common/ddm/ddm-data-query-internal.h
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-data-query-internal.h	2007-11-08 20:33:40 UTC (rev 6880)
+++ dumbhippo/trunk/client/common/ddm/ddm-data-query-internal.h	2007-11-08 20:39:56 UTC (rev 6881)
@@ -18,6 +18,13 @@
                                          GHashTable     *params,
                                          gint64          serial);
 
+/* Like ddm_data_query_error_async(), but for use when a
+ * QueryResponse work item is already in the work queue.
+ */
+void _ddm_data_query_mark_error (DDMDataQuery *query,
+                                 DDMDataError  error,
+                                 const char   *message);
+
 /* Called when we short-circuit a getResource response and throw
  * it immediately on the work-item pile
  */

Modified: dumbhippo/trunk/client/common/ddm/ddm-data-query.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-data-query.c	2007-11-08 20:33:40 UTC (rev 6880)
+++ dumbhippo/trunk/client/common/ddm/ddm-data-query.c	2007-11-08 20:39:56 UTC (rev 6881)
@@ -19,7 +19,10 @@
     char *fetch_string;
     DDMDataFetch *fetch;
     GHashTable *params;
+    
     GSList *results;
+    DDMDataError error;
+    char *error_message;
 
     HandlerType handler_type;
     union {
@@ -87,6 +90,12 @@
     return query->results;
 }
 
+gboolean
+ddm_data_query_has_error (DDMDataQuery *query)
+{
+    return query->error_message != NULL;
+}
+
 void
 ddm_data_query_set_single_handler (DDMDataQuery     *query,
                                    DDMSingleHandler  handler,
@@ -218,8 +227,10 @@
     g_free(query->fetch_string);
     g_hash_table_destroy(query->params);
 
+    g_free(query->id_string);
+
     g_slist_free(query->results);
-    g_free(query->id_string);
+    g_free(query->error_message);
     
     g_free(query);
 }
@@ -255,16 +266,16 @@
         if (children != NULL) {
             ddm_data_property_get_value(property, &value);
             
-            g_assert (DDM_DATA_BASE(value.type) == DDM_DATA_RESOURCE);
-            
-            if (DDM_DATA_IS_LIST(value.type)) {
-                GSList *l;
-                
-                for (l = value.u.list; l; l = l->next) {
-                    mark_received_fetches(l->data, children, local);
+            if (DDM_DATA_BASE(value.type) == DDM_DATA_RESOURCE) { /* Could also be NONE */
+                if (DDM_DATA_IS_LIST(value.type)) {
+                    GSList *l;
+                    
+                    for (l = value.u.list; l; l = l->next) {
+                        mark_received_fetches(l->data, children, local);
+                    }
+                } else {
+                    mark_received_fetches(value.u.resource, children, local);
                 }
-            } else {
-                mark_received_fetches(value.u.resource, children, local);
             }
         }
     }
@@ -280,23 +291,6 @@
     DDMWorkItem *item;
     GSList *l;
 
-    /* It's possible that we succeeded in getting a list of results, but
-     * couldn't even find out the class_id for the results (particularly
-     * for getResource, where a simple failed query shows this way)
-     * In this case, treat the whole thing as a failure.
-     */
-    for (l = results; l; l = l->next) {
-        if (ddm_data_resource_get_class_id(l->data) == NULL) {
-            g_debug("%s: resource %s has no class ID, failing query",
-                    query->id_string, ddm_data_resource_get_resource_id(l->data));
-            
-            ddm_data_query_error(query,
-                                 DDM_DATA_ERROR_ITEM_NOT_FOUND,
-                                 "Couldn't get details of result items");
-            return;
-        }
-    }
-            
    if (!local) {
         g_debug("%s: Received response", query->id_string);
         
@@ -323,6 +317,7 @@
                          GSList       *results)
 {
     g_return_if_fail(query != NULL);
+    g_return_if_fail(query->error_message == NULL);
     
     data_query_response_internal(query, results, FALSE);
 }
@@ -332,6 +327,7 @@
                                 GSList       *results)
 {
     g_return_if_fail(query != NULL);
+    g_return_if_fail(query->error_message == NULL);
     
     data_query_response_internal(query, results, TRUE);
 }
@@ -341,6 +337,15 @@
 {
     g_return_if_fail(query != NULL);
 
+    if (query->error_message) {
+        if (query->error_handler)    
+            query->error_handler(query->error, query->error_message, query->error_handler_data);
+    
+        ddm_data_query_free(query);
+
+        return;
+    }
+
     g_debug("%s: Have complete fetch, running response", query->id_string);
     
     switch (query->handler_type) {
@@ -379,14 +384,19 @@
 }
 
 void
-ddm_data_query_error (DDMDataQuery *query,
-                      DDMDataError  error,
-                      const char   *message)
+_ddm_data_query_mark_error(DDMDataQuery *query,
+                           DDMDataError  error,
+                           const char   *message)
 {
     g_return_if_fail(query != NULL);
+    g_return_if_fail(message != NULL);
+    g_return_if_fail(query->results == NULL);
 
     g_debug("%s: Got error response: %s (%d)", query->id_string, message != NULL ? message : "<null>", error);
 
+    query->error = error;
+    query->error_message = g_strdup(message);
+
     /* For a getResource query for a particular resource, we want to mark the
      * fetch as 'received', so that we don't try to fetch it again. This is
      * especially important for keeping our multi-part fetch code from going
@@ -413,11 +423,37 @@
     }
 
     _ddm_data_model_query_answered(query->model, query);
+}
+
+void
+ddm_data_query_error (DDMDataQuery *query,
+                      DDMDataError  error,
+                      const char   *message)
+{
+    g_return_if_fail(query != NULL);
+    g_return_if_fail(message != NULL);
+    g_return_if_fail(query->results == NULL);
+
+    _ddm_data_query_mark_error(query, error, message);
+    _ddm_data_query_run_response(query);
+}
+
+void
+ddm_data_query_error_async (DDMDataQuery *query,
+                            DDMDataError  error,
+                            const char   *message)
+{
+    DDMWorkItem *item;
+
+    g_return_if_fail(query != NULL);
+    g_return_if_fail(message != NULL);
+    g_return_if_fail(query->results == NULL);
+
+    _ddm_data_query_mark_error(query, error, message);
     
-    if (query->error_handler)    
-        query->error_handler(error, message, query->error_handler_data);
-    
-    ddm_data_query_free(query);
+    item = _ddm_work_item_query_response_new(query->model, query);
+    _ddm_data_model_add_work_item(query->model, item);
+    _ddm_work_item_unref(item);
 }
 
 gint64

Modified: dumbhippo/trunk/client/common/ddm/ddm-data-query.h
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-data-query.h	2007-11-08 20:33:40 UTC (rev 6880)
+++ dumbhippo/trunk/client/common/ddm/ddm-data-query.h	2007-11-08 20:39:56 UTC (rev 6881)
@@ -49,15 +49,25 @@
 gboolean      ddm_data_query_is_update        (DDMDataQuery *query);
 GHashTable *  ddm_data_query_get_params       (DDMDataQuery *query);
 GSList *      ddm_data_query_get_results      (DDMDataQuery *query);
+gboolean      ddm_data_query_has_error        (DDMDataQuery *query);
 
 /* Called by the backend when a response is received from upstream */
 
 void ddm_data_query_response (DDMDataQuery *query,
                               GSList       *results);
+
 void ddm_data_query_error    (DDMDataQuery *query,
                               DDMDataError  error,
                               const char   *message);
 
+/* Like ddm_data_query_error(), but calls the callback asynchronously;
+ * this should generally be used when an error occurs while sending
+ * a DDMDataQuery.
+ */
+void ddm_data_query_error_async (DDMDataQuery *query,
+                                 DDMDataError  error,
+                                 const char   *message);
+
 /* Debugging convenience */
 const char *ddm_data_query_get_id_string (DDMDataQuery *query);
 

Modified: dumbhippo/trunk/client/common/ddm/ddm-work-item.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-work-item.c	2007-11-08 20:33:40 UTC (rev 6880)
+++ dumbhippo/trunk/client/common/ddm/ddm-work-item.c	2007-11-08 20:39:56 UTC (rev 6881)
@@ -239,18 +239,18 @@
         if (children != NULL) {
             ddm_data_property_get_value(property, &value);
             
-            g_assert (DDM_DATA_BASE(value.type) == DDM_DATA_RESOURCE);
-            
-            if (DDM_DATA_IS_LIST(value.type)) {
-                GSList *l;
-                
-                for (l = value.u.list; l; l = l->next) {
-                    if (!item_fetch_additional(item, l->data, children))
+            if (DDM_DATA_BASE(value.type) == DDM_DATA_RESOURCE) { /* Could also be NONE */
+                if (DDM_DATA_IS_LIST(value.type)) {
+                    GSList *l;
+                    
+                    for (l = value.u.list; l; l = l->next) {
+                        if (!item_fetch_additional(item, l->data, children))
+                            all_satisfied = FALSE;
+                    }
+                } else {
+                    if (!item_fetch_additional(item, value.u.resource, children))
                         all_satisfied = FALSE;
                 }
-            } else {
-                if (!item_fetch_additional(item, value.u.resource, children))
-                    all_satisfied = FALSE;
             }
         }
     }
@@ -317,13 +317,30 @@
         break;
     case ITEM_QUERY_RESPONSE:
         {
-            GSList *resources = ddm_data_query_get_results(item->u.query_response.query);
-            for (l = resources; l; l = l->next) {
-                DDMDataResource *resource = l->data;
-                
-                if (!item_fetch_additional(item, resource,
-                                           ddm_data_query_get_fetch(item->u.query_response.query)))
-                    all_satisfied = FALSE;
+            DDMDataQuery *query = item->u.query_response.query;
+            
+            if (!ddm_data_query_has_error(query)) {
+                GSList *resources = ddm_data_query_get_results(query);
+                for (l = resources; l; l = l->next) {
+                    DDMDataResource *resource = l->data;
+                    
+                    if (item_fetch_additional(item, resource,
+                                              ddm_data_query_get_fetch(query))) {
+                        if (ddm_data_resource_get_class_id(resource) == NULL) {
+                            /* This means that we've done everything we can and we still know
+                             * nothing about the resource.
+                             */
+                            _ddm_data_query_mark_error(query,
+                                                       DDM_DATA_ERROR_ITEM_NOT_FOUND,
+                                                       "Couldn't get details of result item");
+                            all_satisfied = TRUE;
+                            break;
+
+                        }
+                    } else {
+                        all_satisfied = FALSE;
+                    }
+                }
             }
         }
         break;

Modified: dumbhippo/trunk/client/common/ddm/hippo-dbus-helper.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/hippo-dbus-helper.c	2007-11-08 20:33:40 UTC (rev 6880)
+++ dumbhippo/trunk/client/common/ddm/hippo-dbus-helper.c	2007-11-08 20:39:56 UTC (rev 6881)
@@ -1145,20 +1145,26 @@
     if (service == NULL)
         return; /* we don't care about this */
 
-    if (service->owner &&
-        (new_owner == NULL ||
-         strcmp(service->owner, new_owner) != 0)) {
-        /* Our previously-believed owner is going away */
+    if ((new_owner == NULL ||
+         (service->owner != NULL && strcmp(service->owner, new_owner) != 0))) {
+        /* Our previously-believed owner is going away, or we didn't find
+         * the service on startup. */
         char *owner;
-        
-        g_hash_table_remove(helper->services_by_owner,
-                            service->owner);
-        owner = service->owner;
-        service->owner = NULL;
 
-        g_debug("Service '%s' is now unavailable, old owner was '%s'",
-                service->well_known_name,
-                owner);
+        if (service->owner) {
+            g_hash_table_remove(helper->services_by_owner,
+                                service->owner);
+            owner = service->owner;
+            service->owner = NULL;
+            
+            g_debug("Service '%s' is now unavailable, old owner was '%s'",
+                    service->well_known_name,
+                    owner);
+        } else {
+            g_debug("Service '%s' not available on startup",
+                    service->well_known_name);
+        }
+
         (* service->tracker->unavailable_handler) (connection,
                                                    service->well_known_name,
                                                    owner,
@@ -1241,7 +1247,6 @@
             if (*v_STRING == '\0')
                 v_STRING = NULL;
 
-            /* this will do nothing if going NULL->NULL on startup */
             handle_name_owner_changed(god->connection,
                                       god->well_known_name,
                                       NULL,
@@ -1269,6 +1274,19 @@
                 dbus_message_unref(msg);
             }
         }
+    } else  {
+        /*
+         * Probably org.freedesktop.DBus.Error.NameHasNoOwner, but we might as well treat
+         * all error messages the same.
+         */
+        const char *message = NULL;
+        dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, &message, DBUS_TYPE_INVALID);
+        g_debug("GetNameOwner failed: %s", message);
+        
+        handle_name_owner_changed(god->connection,
+                                  god->well_known_name,
+                                  NULL,
+                                  NULL);
     }
     
     dbus_message_unref(reply);

Modified: dumbhippo/trunk/client/common/ddm/static-file-backend.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/static-file-backend.c	2007-11-08 20:33:40 UTC (rev 6880)
+++ dumbhippo/trunk/client/common/ddm/static-file-backend.c	2007-11-08 20:39:56 UTC (rev 6881)
@@ -77,14 +77,13 @@
         DDMDataProperty *frontend_property;
         DDMQName *property_id;
         DDMDataValue value;
-        DDMDataFetch *children;
         DDMDataCardinality cardinality;
         gboolean default_include;
         DDMDataFetch *default_children;
         char *default_children_string;
         GSList *l;
 
-        ddm_data_fetch_iter_next(&iter, &backend_property, &children);
+        ddm_data_fetch_iter_next(&iter, &backend_property, NULL);
 
         property_id = ddm_data_property_get_qname(backend_property);
         
@@ -158,16 +157,17 @@
         
         if (children != NULL) {
             ddm_data_property_get_value(backend_property, &value);
-            g_assert(DDM_DATA_BASE(value.type) == DDM_DATA_RESOURCE);
             
-            if (DDM_DATA_IS_LIST(value.type)) {
-                for (l = value.u.list; l; l = l->next) {
+            if (DDM_DATA_BASE(value.type) == DDM_DATA_RESOURCE) { /* Could also be NONE */
+                if (DDM_DATA_IS_LIST(value.type)) {
+                    for (l = value.u.list; l; l = l->next) {
+                        model_process_query_recurse(static_file_model, query,
+                                                    l->data, children);
+                    }
+                } else {
                     model_process_query_recurse(static_file_model, query,
-                                                l->data, children);
+                                                value.u.resource, children);
                 }
-            } else {
-                model_process_query_recurse(static_file_model, query,
-                                            value.u.resource, children);
             }
         }
     }

Modified: dumbhippo/trunk/client/common/ddm/test-ddm.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/test-ddm.c	2007-11-08 20:33:40 UTC (rev 6880)
+++ dumbhippo/trunk/client/common/ddm/test-ddm.c	2007-11-08 20:39:56 UTC (rev 6881)
@@ -58,29 +58,19 @@
 }
 
 static void
-on_query_response(GSList            *resources,
-                  gpointer           user_data)
+on_query_response(DDMDataResource *resource,
+                  gpointer         user_data)
 {
-    GSList *l;
-
-    for (l = resources; l != NULL; l = l->next) {
-        DDMDataResource *resource = l->data;
-        GSList *properties;    
+    GSList *properties;    
     
-        g_print("Resource '%s' received in reply to query\n",
-                ddm_data_resource_get_resource_id(resource));
-        
-        properties = ddm_data_resource_get_properties(resource);
-        
-        print_resource_properties(resource, properties);
-
-        g_slist_free(properties);
-        
-        ddm_data_resource_connect(resource,
-                                  NULL, /* NULL = all properties */
-                                  on_resource_changed,
-                                  NULL);
-    }
+    g_print("Resource '%s' received in reply to query\n",
+            ddm_data_resource_get_resource_id(resource));
+    
+    properties = ddm_data_resource_get_properties(resource);
+    
+    print_resource_properties(resource, properties);
+    
+    g_slist_free(properties);
 }
 
 static void
@@ -91,36 +81,42 @@
     g_printerr("Failed to get query reply: '%s'\n", message);
 }
 
+static void
+on_ready(DDMDataModel *model)
+{
+    DDMDataQuery *query;
+    DDMDataResource *self_resource;
+
+    self_resource = ddm_data_model_get_self_resource(model);
+    if (self_resource != NULL) {
+        ddm_data_model_query_resource(model, self_resource,
+                                      "name;photoUrl;lovedAccounts +");
+        ddm_data_resource_connect(self_resource, NULL, on_resource_changed, NULL);
+    }
+    
+    query = ddm_data_model_query_resource(model,
+                                          ddm_data_model_get_global_resource(model),
+                                          "self [ photoUrl ]");
+    if (query == NULL) {
+        g_printerr("Failed to query global resource\n");
+        return;
+    }
+    
+    ddm_data_query_set_single_handler(query, on_query_response, NULL);    
+    ddm_data_query_set_error_handler(query, on_query_error, NULL);
+}
+
 int
 main(int argc, char **argv)
 {
-    DDMDataQuery *global_resource_query;
     GMainLoop *loop;
     
     g_type_init();
     
     ddm_model = ddm_data_model_get_default();
 
-#if 0
-    global_resource_query = ddm_data_model_query_resource(ddm_model,
-                                                          "http://dogfood.mugshot.org:9080/o/user/c4a3fc1f528070";,
-                                                          "topApplications+;contacts+;settings+");
-#else
-    global_resource_query = ddm_data_model_query_resource(ddm_model,
-                                                          "online-desktop:/o/global", "self [ photoUrl ]");
-#endif
-    
-    if (global_resource_query == NULL) {
-        g_printerr("Failed to query global resource\n");
-        return 1;
-    }
-    
-    ddm_data_query_set_multi_handler(global_resource_query,
-                                     on_query_response, NULL);    
-    
-    ddm_data_query_set_error_handler(global_resource_query,
-                                     on_query_error, NULL);
-    
+    g_signal_connect(ddm_model, "ready", G_CALLBACK(on_ready), NULL);
+
     loop = g_main_loop_new(NULL, FALSE);
     g_main_loop_run(loop);
     g_main_loop_unref(loop);

Modified: dumbhippo/trunk/client/common/ddm/test-multipart-fetch.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/test-multipart-fetch.c	2007-11-08 20:33:40 UTC (rev 6880)
+++ dumbhippo/trunk/client/common/ddm/test-multipart-fetch.c	2007-11-08 20:39:56 UTC (rev 6881)
@@ -33,7 +33,7 @@
     DDMDataResource *result = NULL;
     const char *error = NULL;
     
-    query = ddm_data_model_query_resource(model, resource_id, fetch);
+    query = ddm_data_model_query_resource_by_id(model, resource_id, fetch);
     ddm_data_query_set_single_handler(query, on_query_result, &result);
     ddm_data_query_set_error_handler(query, on_query_error, &error);
 

Modified: dumbhippo/trunk/client/common/ddm/test-notification.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/test-notification.c	2007-11-08 20:33:40 UTC (rev 6880)
+++ dumbhippo/trunk/client/common/ddm/test-notification.c	2007-11-08 20:39:56 UTC (rev 6881)
@@ -42,7 +42,7 @@
     DDMDataResource *result = NULL;
     const char *error = NULL;
     
-    query = ddm_data_model_query_resource(model, resource_id, fetch);
+    query = ddm_data_model_query_resource_by_id(model, resource_id, fetch);
     ddm_data_query_set_single_handler(query, on_query_result, &result);
     ddm_data_query_set_error_handler(query, on_query_error, &error);
 

Modified: dumbhippo/trunk/client/common/ddm/test-static-file-backend.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/test-static-file-backend.c	2007-11-08 20:33:40 UTC (rev 6880)
+++ dumbhippo/trunk/client/common/ddm/test-static-file-backend.c	2007-11-08 20:39:56 UTC (rev 6881)
@@ -33,7 +33,7 @@
     DDMDataResource *result = NULL;
     const char *error = NULL;
     
-    query = ddm_data_model_query_resource(model, resource_id, fetch);
+    query = ddm_data_model_query_resource_by_id(model, resource_id, fetch);
     ddm_data_query_set_single_handler(query, on_query_result, &result);
     ddm_data_query_set_error_handler(query, on_query_error, &error);
 

Modified: dumbhippo/trunk/client/common/hippo/hippo-connection.c
===================================================================
--- dumbhippo/trunk/client/common/hippo/hippo-connection.c	2007-11-08 20:33:40 UTC (rev 6880)
+++ dumbhippo/trunk/client/common/hippo/hippo-connection.c	2007-11-08 20:39:56 UTC (rev 6881)
@@ -936,10 +936,14 @@
 hippo_connection_connect_failure(HippoConnection *connection,
                                  const char      *message)
 {
+    /* message can be NULL */
     g_debug("Connection failure message: '%s'", message ? message : "NULL");
 
-    /* message can be NULL */
     hippo_connection_clear(connection);
+    
+    if (connection->state == HIPPO_STATE_REDIRECTING)
+        return;
+
     hippo_connection_start_retry_timeout(connection);
     hippo_connection_state_change(connection, HIPPO_STATE_RETRYING);
 }
@@ -3950,8 +3954,9 @@
     if (child) {
         char *redirect_host = g_strdup(child->value);
         g_debug("Got see-other-host message, redirected to '%s'", redirect_host);
-        
-        hippo_connection_signout(connection);
+
+        hippo_connection_state_change(connection, HIPPO_STATE_REDIRECTING);
+        hippo_connection_disconnect(connection);
         hippo_connection_connect(connection, redirect_host);
         g_free (redirect_host);
         return LM_HANDLER_RESULT_REMOVE_MESSAGE;
@@ -4201,6 +4206,7 @@
         tip = _("%s (please log in to mugshot.org)");
         break;
     case HIPPO_STATE_CONNECTING:
+    case HIPPO_STATE_REDIRECTING:
     case HIPPO_STATE_AUTHENTICATING:
         tip = _("%s (connecting - please wait)");
         break;    
@@ -4252,6 +4258,8 @@
         return "SIGN_IN_WAIT";
     case HIPPO_STATE_CONNECTING:
         return "CONNECTING";
+    case HIPPO_STATE_REDIRECTING:
+        return "REDIRECTING";
     case HIPPO_STATE_RETRYING:
         return "RETRYING";
     case HIPPO_STATE_AUTHENTICATING:

Modified: dumbhippo/trunk/client/common/hippo/hippo-connection.h
===================================================================
--- dumbhippo/trunk/client/common/hippo/hippo-connection.h	2007-11-08 20:33:40 UTC (rev 6880)
+++ dumbhippo/trunk/client/common/hippo/hippo-connection.h	2007-11-08 20:39:56 UTC (rev 6881)
@@ -12,6 +12,7 @@
     HIPPO_STATE_SIGNED_OUT,     // User hasn't asked to connect
     HIPPO_STATE_SIGN_IN_WAIT,   // Waiting for the user to sign in
     HIPPO_STATE_CONNECTING,     // Waiting for connecting to server
+    HIPPO_STATE_REDIRECTING,    // Redirection from load balancer to real server
     HIPPO_STATE_RETRYING,       // Connection to server failed, retrying
     HIPPO_STATE_AUTHENTICATING, // Waiting for authentication
     HIPPO_STATE_AUTH_WAIT,      // Authentication failed, waiting for new creds

Modified: dumbhippo/trunk/client/common/hippo/hippo-data-model-backend.c
===================================================================
--- dumbhippo/trunk/client/common/hippo/hippo-data-model-backend.c	2007-11-08 20:33:40 UTC (rev 6880)
+++ dumbhippo/trunk/client/common/hippo/hippo-data-model-backend.c	2007-11-08 20:39:56 UTC (rev 6881)
@@ -71,50 +71,150 @@
 }
 
 static void
-on_connection_connected_changed(HippoConnection *connection,
-                                gboolean         connected,
-                                HippoModel      *hippo_model)
-{   
+model_set_initial_properties(HippoModel *hippo_model)
+{
+    HippoConnection *connection = hippo_data_cache_get_connection(hippo_model->data_cache);
+    HippoPlatform *platform = hippo_connection_get_platform(connection);
     DDMDataResource *global_resource;
+    DDMDataResource *self_resource;
     const char *self_id;
-    DDMQName *self_id_prop;
     DDMDataValue value;
+    char *web_server;
+    char *web_base_url;
 
+    global_resource = ddm_data_model_ensure_local_resource(hippo_model->ddm_model,
+                                                           DDM_GLOBAL_RESOURCE, DDM_GLOBAL_RESOURCE_CLASS);
+    ddm_data_model_set_global_resource(hippo_model->ddm_model, global_resource);
+                                       
+    self_id = hippo_connection_get_self_resource_id(connection);
+    if (self_id) {
+        self_resource = ddm_data_model_ensure_resource(hippo_model->ddm_model,
+                                                       self_id, "http://mugshot.org/p/o/user";);
+        value.type = DDM_DATA_RESOURCE;
+        value.u.resource = self_resource;
+    } else {
+        self_resource = NULL;
+        value.type = DDM_DATA_NONE;
+    }
+    
+    ddm_data_model_set_self_resource(hippo_model->ddm_model, self_resource);
+
+    ddm_data_resource_update_property(global_resource,
+                                      ddm_qname_get(DDM_GLOBAL_RESOURCE_CLASS, "self"),
+                                      self_id ? DDM_DATA_UPDATE_REPLACE : DDM_DATA_UPDATE_DELETE,
+                                      DDM_DATA_CARDINALITY_01,
+                                      FALSE, NULL,
+                                      &value);
+    
+    /* DESKTOP hardcoded here since this is the data model API, nothing to do with stacker */
+    web_server = hippo_platform_get_web_server(platform, HIPPO_SERVER_DESKTOP);
+    /* Note, no trailing '/', don't add one here because relative urls are given
+     * starting with '/' and so you want to be able to do baseurl + relativeurl
+     */
+    web_base_url = g_strdup_printf("http://%s";, web_server);
+    
+    value.type = DDM_DATA_STRING;
+    value.u.string = web_base_url;
+
+    ddm_data_resource_update_property(global_resource,
+                                      ddm_qname_get(DDM_GLOBAL_RESOURCE_CLASS, "webBaseUrl"),
+                                      DDM_DATA_UPDATE_REPLACE,
+                                      DDM_DATA_CARDINALITY_1,
+                                      FALSE, NULL,
+                                      &value);
+
+    g_free(web_server);
+    g_free(web_base_url);
+
+    value.type = DDM_DATA_BOOLEAN;
+    value.u.boolean = hippo_connection_get_connected(connection);
+
+    ddm_data_resource_update_property(global_resource,
+                                      ddm_qname_get(DDM_GLOBAL_RESOURCE_CLASS, "online"),
+                                      DDM_DATA_UPDATE_REPLACE,
+                                      DDM_DATA_CARDINALITY_1,
+                                      FALSE, NULL,
+                                      &value);
+}
+
+static void
+model_on_connected(HippoModel *hippo_model)
+{
     /* We first "reset" - deleting all non-local resources from the model, and all property
      * values that reference non-local properties; then we add back the "self" property, which
      * references the local resource, *then* we signal that we are reconnected, so that the
      * self property is already there.
      */
     ddm_data_model_reset(hippo_model->ddm_model);
+
+    model_set_initial_properties(hippo_model);
     
+    ddm_data_model_signal_ready(hippo_model->ddm_model);
+}
+
+static void
+model_on_disconnected(HippoModel *hippo_model)
+{
+    DDMDataResource *global_resource;
+    DDMDataValue value;
+
     global_resource = ddm_data_model_ensure_local_resource(hippo_model->ddm_model,
                                                            DDM_GLOBAL_RESOURCE, DDM_GLOBAL_RESOURCE_CLASS);
-    if (connected) {
-        self_id = hippo_connection_get_self_resource_id(connection);
-    } else {
-        self_id = NULL;
-    }
 
-    self_id_prop = ddm_qname_get(DDM_GLOBAL_RESOURCE_CLASS, "self");
-    if (self_id) {
-        value.type = DDM_DATA_RESOURCE;
-        value.u.resource = ddm_data_model_ensure_resource(hippo_model->ddm_model,
-                                                          self_id, "http://mugshot.org/p/o/user";);
-    } else {
-        value.type = DDM_DATA_NONE;
-    }
+    value.type = DDM_DATA_BOOLEAN;
+    value.u.string = FALSE;
 
     ddm_data_resource_update_property(global_resource,
-                                      self_id_prop,
-                                      self_id ? DDM_DATA_UPDATE_REPLACE : DDM_DATA_UPDATE_DELETE,
-                                      DDM_DATA_CARDINALITY_01,
+                                      ddm_qname_get(DDM_GLOBAL_RESOURCE_CLASS, "online"),
+                                      DDM_DATA_UPDATE_REPLACE,
+                                      DDM_DATA_CARDINALITY_1,
                                       FALSE, NULL,
                                       &value);
+}
     
-    ddm_data_model_set_connected(hippo_model->ddm_model, connected);
+static void
+on_connection_connected_changed(HippoConnection *connection,
+                                gboolean         connected,
+                                HippoModel      *hippo_model)
+{
+    if (connected)
+        model_on_connected(hippo_model);
+    else
+        model_on_disconnected(hippo_model);
 }
 
 static void
+on_connection_state_changed(HippoConnection *connection,
+                            HippoModel      *hippo_model)
+{
+    /* When we start up, we want to wait to signal ready until either:
+     *
+     * - We manage to connect
+     * - We fail to connect and are either retrying or waiting for user input
+     *
+     */
+    
+    switch (hippo_connection_get_state(connection)) {
+    case HIPPO_STATE_SIGNED_OUT:
+    case HIPPO_STATE_SIGN_IN_WAIT:
+    case HIPPO_STATE_RETRYING:
+    case HIPPO_STATE_AUTH_WAIT:
+        if (!ddm_data_model_is_ready(hippo_model->ddm_model)) {
+            model_set_initial_properties(hippo_model);
+            ddm_data_model_signal_ready(hippo_model->ddm_model);
+        }
+        break;
+        
+    case HIPPO_STATE_CONNECTING:
+    case HIPPO_STATE_REDIRECTING:
+    case HIPPO_STATE_AUTHENTICATING:
+    case HIPPO_STATE_AWAITING_CLIENT_INFO:
+    case HIPPO_STATE_AUTHENTICATED:
+        break;
+    }
+}
+
+static void
 hippo_add_model    (DDMDataModel *ddm_model,
                     void         *backend_data)
 {
@@ -141,6 +241,9 @@
     g_signal_connect(connection, "has-auth-changed",
                      G_CALLBACK(on_connection_has_auth_changed), hippo_model);
     
+    g_signal_connect(connection, "state-changed",
+                     G_CALLBACK(on_connection_state_changed), hippo_model);
+    
     open_disk_cache(hippo_model);
 }
 

Modified: dumbhippo/trunk/client/linux/src/hippo-dbus-model-client.c
===================================================================
--- dumbhippo/trunk/client/linux/src/hippo-dbus-model-client.c	2007-11-08 20:33:40 UTC (rev 6880)
+++ dumbhippo/trunk/client/linux/src/hippo-dbus-model-client.c	2007-11-08 20:39:56 UTC (rev 6881)
@@ -56,7 +56,7 @@
     DataClientConnection *connection = g_new0(DataClientConnection, 1);
 
     connection->client = client;
-    connection->resource = resource;
+    connection->resource = ddm_data_resource_ref(resource);
     connection->fetch = NULL;
 
     return connection;
@@ -81,6 +81,8 @@
 data_client_connection_destroy(DataClientConnection *connection)
 {
     ddm_data_resource_set_client_fetch(connection->resource, DDM_CLIENT(connection->client), NULL);
+    ddm_data_resource_unref(connection->resource);
+
     g_free(connection);
 }
 

Modified: dumbhippo/trunk/client/linux/src/hippo-dbus-model.c
===================================================================
--- dumbhippo/trunk/client/linux/src/hippo-dbus-model.c	2007-11-08 20:33:40 UTC (rev 6880)
+++ dumbhippo/trunk/client/linux/src/hippo-dbus-model.c	2007-11-08 20:39:56 UTC (rev 6881)
@@ -30,9 +30,8 @@
     GHashTable *clients;
 };
 
-static void        on_connected_changed    (DDMDataModel    *ddm_model,
-                                            gboolean         connected,
-                                            void            *data);
+static void        on_ready    (DDMDataModel    *ddm_model,
+                                void            *data);
 
 static guint
 data_client_id_hash(const DataClientId *id)
@@ -318,53 +317,24 @@
 }
 
 static dbus_bool_t
-handle_get_connected(void            *object,
-                     const char      *prop_name,
-                     DBusMessageIter *append_iter,
-                     DBusError       *error)
+handle_get_ready(void            *object,
+                 const char      *prop_name,
+                 DBusMessageIter *append_iter,
+                 DBusError       *error)
 {
     DDMDataModel *ddm_model;
-    dbus_bool_t connected;
+    dbus_bool_t ready;
     
     ddm_model = hippo_app_get_data_model(hippo_get_app());
 
-    connected = ddm_data_model_get_connected(ddm_model);
+    ready = ddm_data_model_is_ready(ddm_model);
 
-    dbus_message_iter_append_basic(append_iter, DBUS_TYPE_BOOLEAN, &connected);
+    dbus_message_iter_append_basic(append_iter, DBUS_TYPE_BOOLEAN, &ready);
 
     return TRUE;
 }
 
-static char *
-get_self_id()
-{
-    HippoDataCache *cache = hippo_app_get_data_cache(hippo_get_app());
-    HippoConnection *hippo_connection = hippo_data_cache_get_connection(cache);
-    const char *self_resource_id = hippo_connection_get_self_resource_id(hippo_connection);
-
-    if (self_resource_id == NULL) {
-        return g_strdup("");
-    } else {
-        return g_strdup(self_resource_id);
-    }
-}
-
 static dbus_bool_t
-handle_get_self_id(void            *object,
-                   const char      *prop_name,
-                   DBusMessageIter *append_iter,
-                   DBusError       *error)
-{
-    char *resource_id = get_self_id();
-    
-    dbus_message_iter_append_basic(append_iter, DBUS_TYPE_STRING, &resource_id);
-
-    g_free(resource_id);
-
-    return TRUE;
-}
-
-static dbus_bool_t
 handle_get_server(void            *object,
                   const char      *prop_name,
                   DBusMessageIter *append_iter,
@@ -386,34 +356,6 @@
     return TRUE;
 }
 
-static dbus_bool_t
-handle_get_web_base_url(void            *object,
-                        const char      *prop_name,
-                        DBusMessageIter *append_iter,
-                        DBusError       *error)
-{
-    char *server;
-    char *url;
-    HippoDataCache *cache = hippo_app_get_data_cache(hippo_get_app());
-    HippoConnection *hippo_connection = hippo_data_cache_get_connection(cache);
-    HippoPlatform *platform = hippo_connection_get_platform(hippo_connection);
-
-    /* DESKTOP hardcoded here since this is the data model API, nothing to do with stacker */
-    server = hippo_platform_get_web_server(platform, HIPPO_SERVER_DESKTOP);
-
-    /* Note, no trailing '/', don't add one here because relative urls are given
-     * starting with '/' and so you want to be able to do baseurl + relativeurl
-     */
-    url = g_strdup_printf("http://%s";, server);
-    
-    dbus_message_iter_append_basic(append_iter, DBUS_TYPE_STRING, &url);
-    
-    g_free(server);
-    g_free(url);
-    
-    return TRUE;
-}
-
 static const HippoDBusMember model_members[] = {
     /* Query: Send a query to the server
      *
@@ -459,42 +401,22 @@
      */
     { HIPPO_DBUS_MEMBER_METHOD, "Forget", "os", "", handle_forget },
 
-    /* ConnectedChanged: connected status changed (this signal will be replaced
-     * eventually by moving self id and the online flag into global resource)
+    /* Ready: Start fetching application data from the data model
      * 
      * Parameters:
-     *  boolean connected = true/false
-     *  string selfId
      * 
      */
-    { HIPPO_DBUS_MEMBER_SIGNAL, "ConnectedChanged", "", "bs", NULL },
+    { HIPPO_DBUS_MEMBER_SIGNAL, "Ready", "", "", NULL },
     
     { 0, NULL }
 };
 
 static const HippoDBusProperty model_properties[] = {
-    { "Connected",  "b", handle_get_connected, NULL },
+    { "Ready",  "b", handle_get_ready, NULL },
 
-
-    /* FIXME This is probably broken to have here, since it's just a special
-     * case of something that should be in the data model anyway, right?
-     */
-    { "SelfId",     "s", handle_get_self_id, NULL },
-
     /* this should return the server we are encoding in the dbus bus name, like foo.bar.org:8080 */
     { "Server",     "s", handle_get_server, NULL },
 
-    /* right now this will be the server with http:// in front, but
-     * in theory could have https or a path on the host or
-     * something. The url should NOT have a trailing '/', though
-     * the one returned by the old "org.mugshot.Mugshot" API does.
-     */
-
-    /* All URLs in the data model have already been made absolute
-     * on receipt from the server, so this is just useful if you want to construct
-     * an URL like /account. FIXME: Such URL's perhaps should also be in the data model
-     */
-    { "WebBaseUrl", "s", handle_get_web_base_url, NULL },
     { NULL }
 };
 
@@ -512,8 +434,8 @@
 
     ddm_model = hippo_app_get_data_model(hippo_get_app());
     
-    g_signal_connect(G_OBJECT(ddm_model), "connected-changed", G_CALLBACK(on_connected_changed),
-                     connection);
+    g_signal_connect(G_OBJECT(ddm_model), "ready",
+                     G_CALLBACK(on_ready), connection);
 }
 
 static gboolean
@@ -542,65 +464,22 @@
 }
 
 static void
-on_connected_changed(DDMDataModel *ddm_model,
-                     gboolean      connected,
-                     void         *data)
+on_ready(DDMDataModel *ddm_model,
+         void         *data)
 {
     DBusConnection *connection = data;
     
-    if (connected) {
-        DataClientMap *map = data_client_map_get(ddm_model);
-        char *resource_id = get_self_id();
+    DataClientMap *map = data_client_map_get(ddm_model);
 
-        /* Once a client receives ConnectedChanged, it must forget its current
-         * state and start over. It would be a little more efficent to just remove
-         * all connections and leave the D-BUS disconnection-watches in place,
-         * that might be important if we had dozens of clients.
-         */
-        g_hash_table_remove_all(map->clients);
+    /* Once a client receives Ready, it must forget its current
+     * state and start over. It would be a little more efficent to just remove
+     * all connections and leave the D-BUS disconnection-watches in place,
+     * that might be important if we had dozens of clients.
+     */
+    g_hash_table_remove_all(map->clients);
         
-        hippo_dbus_helper_emit_signal(connection,
-                                      HIPPO_DBUS_MODEL_PATH, HIPPO_DBUS_MODEL_INTERFACE,
-                                      "ConnectedChanged",
-                                      DBUS_TYPE_BOOLEAN, &connected,
-                                      DBUS_TYPE_STRING, &resource_id,
-                                      DBUS_TYPE_INVALID);
-
-        g_free(resource_id);
-    } else {
-        const char *empty_string;
-
-        empty_string = "";
-
-        /* Including an empty string for the self-id might make you think that being disconnected
-         * means that there is no longer a self-id. That's, however, wrong. The self_id is
-         * *independent* of the connected state and can be used to fetch things out of the
-         * offline cache.
-         *
-         * In fact, I think the whole existence of a ConnectedChanged signal is possibly a confusing
-         * thing. There are two different events:
-         *
-         * Connected:
-         * - The connected boolean is true
-         * - All prior notification's you've registered have been removed
-         * - All data you might be caching is invalid, dump it
-         * - The selfId might have changed
-         * - Start fetching your data from scratch with the assumption that everything has changed
-         *
-         * Disconnected:
-         * - The connected boolean is false
-         *
-         * So while the two events *do* signal changes in the Connected boolean, they otherwise require
-         * entirely different handling on the part of the recipient.
-         *
-         * This also means that even if the connected boolean moved in the global resource, you'd
-         * still want the "Connected" signal.
-         */
-        hippo_dbus_helper_emit_signal(connection,
-                                      HIPPO_DBUS_MODEL_PATH, HIPPO_DBUS_MODEL_INTERFACE,
-                                      "ConnectedChanged",
-                                      DBUS_TYPE_BOOLEAN, &connected,
-                                      DBUS_TYPE_STRING, &empty_string,
-                                      DBUS_TYPE_INVALID);
-    }
+    hippo_dbus_helper_emit_signal(connection,
+                                  HIPPO_DBUS_MODEL_PATH, HIPPO_DBUS_MODEL_INTERFACE,
+                                  "Ready",
+                                  DBUS_TYPE_INVALID);
 }

Modified: dumbhippo/trunk/client/linux/src/hippo-platform-impl.c
===================================================================
--- dumbhippo/trunk/client/linux/src/hippo-platform-impl.c	2007-11-08 20:33:40 UTC (rev 6880)
+++ dumbhippo/trunk/client/linux/src/hippo-platform-impl.c	2007-11-08 20:39:56 UTC (rev 6881)
@@ -791,6 +791,7 @@
         msg = _("Mugshot is not connected - please log in to mugshot.org");
         break;
     case HIPPO_STATE_CONNECTING:
+    case HIPPO_STATE_REDIRECTING:
     case HIPPO_STATE_AUTHENTICATING:
         msg = _("Mugshot is trying to connect to mugshot.org");
         break;    



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]