Tue, 24 Jul 2007

A quick intro to the Mugshot data model [14:21]

One thing I've been working on over the last few months is creating a flexible way for software running within the online desktop to retrieve information from the server. I've been calling this the "data model"; it's not a single piece of software, but rather a set of concepts, protocols, and software components that all fit together.

I posted an extended design document for the data model to the Mugshot mailing list before starting work on it. This post isn't meant to be a substitute for that, or for a reference manual, but just to give a little taste of what the data model is about.

The basic concepts in the data model are resources and property. A resource represents some thing: a user, an application, an addressbook entry. It is identified by an URI like http://mugshot.org/o/user/61m76k3hGbRRFS (the resource for my user account on mugshot.org). A resource has properties, which are just values identified by names; a Mugshot user resource has properties called name, photoUrl, contacts, and so forth. (Yes, the name "resource" is borrowed from RDF.)

You get information about resources by making queries. A query is a bit like a HTTP GET; you send a query name and a list of parameters to the server, and get a list of resources as a result. Just getting a list of resource URIs isn't that interesting, so you can also specify a "fetch string" with the query which says what property values to retrieve for each resource. If the property values point to additional resources, you can recursively specify what properties to retrieve from those resources. The most basic query is the 'getResource' query, which just gets property values from a single resource specified by its ID. For example, if you wanted to get my name and "home URL" from the mugshot.org server, and then also get the name and homeUrl for everybody in my contact list (my network), you could use the query:

 http://mugshot.org/p/system/getResource
   resourceId: http://mugshot.org/o/user/61m76k3hGbRRFS
   fetch: name;homeUrl;contacts [name;homeUrl]

The result from that is a list of resources and their properties; some of which are "direct" (query results), and some "indirect" (resources referenced by other resource properties)

  http://mugshot.org/o/user/hKcbRMYl4vNDqw (indirect)
     name: Colin
     homeUrl: http://mugshot.org/person?who=hKcbRMYl4vNDqw
  http://mugshot.org/o/user/gDmfAh8d7gXVRP (indirect)
     name: Havoc
     homeUrl: http://mugshot.org/person?who=gDmfAh8d7gXVRP          
  http://mugshot.org/o/user/61m76k3hGbRRFS (direct)
     name: Owen
     homeUrl: http://mugshot.org/person?who=61m76k3hGbRRFS
     contacts: http://mugshot.org/o/user/gDmfAh8d7gXVRP
     contacts: http://mugshot.org/o/user/hKcbRMYl4vNDqw

The thing that goes a beyond a HTTP GET is that a query doesn't just fetch property values, it also selects for future notification on those properties. So if Havoc changes his name on the server, I get notified immediately. This is important for the online desktop where there is no reload button and the user will expect information to just update by itself. Since the server tracks what it has sent the client, it can also avoid sending the same data over and over again.

I don't want to go into a lot of detail here about how the protocol works, how you add properties to the data model in the server, or how you access the data model from client code, so I'll just leave things here with a picture and a couple of code snippets. The picture:

Complete server code necessary to add the homeUrl property:

@DMProperty(defaultInclude=true, type=PropertyType.URL)
public String getHomeUrl() {
     return "/person?who=" + user.getId();
}

Client code in python for retrieving data from the model via the online desktop engine:

def on_got_self(resource):
    print resource.name, resource, resource.homeUrl
    for contact in resource.contacts:
        print "    ", contact.name, contact.homeUrl

model = DataModel()
query = model.query_resource(model.self_id, "name;homeUrl;contacts [name;homeUrl]")
query.add_handler(on_got_self)
query.execute()