W3C home > Mailing lists > Public > public-dap-commits@w3.org > January 2010

2009/dap/docs virtual-ws.html,NONE,1.1

From: Robin Berjon via cvs-syncmail <cvsmail@w3.org>
Date: Wed, 20 Jan 2010 14:29:58 +0000
To: public-dap-commits@w3.org
Message-Id: <E1NXbZG-0003rJ-E7@lionel-hutz.w3.org>
Update of /sources/public/2009/dap/docs
In directory hutz:/tmp/cvs-serv14802/docs

Added Files:
	virtual-ws.html 
Log Message:
Virtual web services discussion, preliminary document

--- NEW FILE: virtual-ws.html ---
<!DOCTYPE html>
<html>
  <head>
    <title>Devices as Virtual Web Services</title>
    <meta http-equiv='Content-Type' content='text/html;charset=utf-8'/>
    <script src='../ReSpec.js/js/respec.js' class='remove'></script>
    <script class='remove'>
      var respecConfig = {
          specStatus:           "base",
          shortName:            "virtual-ws",
          edDraftURI:           "http://dev.w3.org/2009/dap/docs/virtual-ws.html",
          // lcEnd: "2009-08-05",
          noIDLIn:  true,
          editors: [
                {   name:       "Robin Berjon",
                    url:        "http://berjon.com/",
                    company:    "Vodafone",
                    companyURL: "http://vodafone.com/" },
          ],
          inlineCSS:  false,
      };
    </script>
    <script src='../common/config.js' class='remove'></script>
  </head>
  <body>
    <section id='abstract'>
      A discussion of the advantages and issues with exposing local data services as REST services
      instead as of APIs.
    </section>
    
    <section class='informative'>
      <h2>Introduction</h2>
      <p>
        At the beginning of 2010, Mark Miller 
        <a href='http://www.w3.org/mid/4d2fac901001071847k22a908b6yb4ab71fa37f19c33@mail.gmail.com'>started a discussion</a>
        in the DAP WG about whether some of DAP's APIs couldn't be exposed as RESTful web services that would be
        somehow localised.
      </p>
      <p>
        While he didn't list which APIs this approach would apply to, one can presume that it would apply
        better to those APIs on DAP's charter that provide information services (Contacts, Calendar, Tasks, Messaging,
        System Information, Communications Log, Gallery) than to those that may be characterised as
        "closer to the metal" (File System, Application Launcher, Capture) or directly integrated with the
        system (User Interaction).
      </p>
      <p>
        This document intends to analyse the pros and cons of such an approach, taking into account the 
        gearing towards information services described above.
      </p>
    </section>

    <section>
      <h2>Case Study: Geolocation</h2>
      <p>
        There is a large body of experience in designing both Javascript and RESTful APIs, but without concrete
        examples we will likely nevertheless be left counting angels. As a result, to get a feel for the issues
        that may crop up in developing a local-REST approach (henceforth, LREST) and how it compares with straight JS APIs we'll
        start from a related and well-known JS API — Geolocation — and try to adapt it to LREST.
      </p>
      <p>
        The examples we use are stolen from the <a href='http://www.w3.org/TR/geolocation-API/'>Geolocation API</a>
        specification. Note that in order to show of the code only what is fully relevant, we assume that we have 
        access to the common jQuery library in order to perform requests (though naturally any other library will
        do, and the equivalent XMLHttpRequest code can easily be inferred).
      </p>
      <section>
        <h2>Example: a "one-shot" position request</h2>
        <p>
          Original example:
        </p>
        <pre class='example sh_javascript_dom'>
function showMap (position) {
  // Show a map centred at (position.coords.latitude, position.coords.longitude).
}
// One-shot position request.
navigator.geolocation.getCurrentPosition(showMap);
        </pre>
        <p>
          LREST version:
        </p>
        <pre class='example sh_javascript_dom'>
function showMap (position) {
  // Show a map centred at (position.coords.latitude, position.coords.longitude).
}
// One-shot position request.
$.getJSON("service://w3c/geo/get-location", showMap);
        </pre>
        <p>
          The port is fairly straightforward (assuming the availability of a library — it would be longer with 
          bare-metal XMLHttpRequest). Things to note are the fact that we expose a URL (an important detail 
          which will be discussed further) and that for better or for worse we've made it possible to make 
          synchronous requests. The data that <code>showMap</code> gets can be exactly the same in both cases.
        </p>
      </section>
      <section>
        <h2>Example: requesting repeated position updates and handling errors</h2>
        <p>
          Original example:
        </p>
        <pre class='example sh_javascript_dom'>
function scrollMap(position) {
  // Scrolls the map so that it is centred at (position.coords.latitude, position.coords.longitude).
}
function handleError(error) {
  // Update a div element with error.message.
}
// Request repeated updates.
var watchId = navigator.geolocation.watchPosition(scrollMap, handleError);
function buttonClickHandler() {
  // Cancel the updates when the user clicks a button.
  navigator.geolocation.clearWatch(watchId);
}
        </pre>
        <p>
          LREST version (requiring the Comet plugin):
        </p>
        <pre class='example sh_javascript_dom'>
function scrollMap (position) {
  // Scrolls the map so that it is centred at (position.coords.latitude, position.coords.longitude).
}
function handleError (error) {
  // Update a div element with error.message.
}
$.cometd.configure("service://w3c/geo");
var errH = $.cometd.addListener("/meta/unsuccessful", handleError);
var sucH = $.cometd.subscribe("/watch-position", scrollMap);
$.cometd.handshake();
function buttonClickHandler () {
  // Cancel the updates when the user clicks a button.
  $.cometd.removeListener(errH);
  $.cometd.unsubscribe(sucH);
}
        </pre>
        <p>
          This provides for a richer and more problematic example. Handling errors is not what adds real overhead here
          (the first example could easily handle them as well by using <code>$.ajax</code> instead of <code>$.getJSON</code>)
          but rather the fact that HTTP wasn't designed with pub-sub in mind.
        </p>
        <p>
          I therefore chose to use the Bayeux Comet protocol because it is well-known and has existing library support.
          That being said, this approach is hardly optimal, and isn't all that well documented and supported in the
          wild (a Google search shows that it is largely the knowledge of a limited group rather than commonly used).
        </p>
        <p>
          There may be better ways of handling subscriptions (event-source for instance) — feedback is welcome. If subscriptions
          do indeed prove to be too much of a problem at this point in time, the LREST approach may not necessarily be
          disqualified, but might have to be limited to APIs that don't need this functionality (which probably maps to
          Contacts, Calendar, Tasks, Messaging, Gallery but excludes System Information and Communications Log).
        </p>
      </section>
      <section>
        <h2>Example: requesting a potentially cached position</h2>
        <p>
          Original example:
        </p>
        <pre class='example sh_javascript_dom'>
navigator.geolocation.getCurrentPosition(successCallback,
                                         errorCallback,
                                         {maximumAge:600000});
function successCallback(position) {
  // By using the 'maximumAge' option above, the position
  // object is guaranteed to be at most 10 minutes old.
}
function errorCallback(error) {
  // Update a div element with error.message.
}
        </pre>
        <p>
          LREST version:
        </p>
        <pre class='example sh_javascript_dom'>
function successCallback(position) {
  // By using the 'maximumAge' option above, the position
  // object is guaranteed to be at most 10 minutes old.
}
function errorCallback(error) {
  // Update a div element with error.message.
}
$.ajax({
    url:        "service://w3c/geo/get-location",
    dataType:   "json",
    beforeSend: function (xhr) { xhr.setRequestHeader("If-Modified-Since", formatHTTPDate(-600000)); },
    success:    successCallback,
    error:      errorCallback,
});
        </pre>
        <p>
          This example is functional, albeit more convoluted. Weasel alert: the <code>formatHTTPDate()</code>
          method is not described in the code, and while not too difficult to implement it does represent
          extra work that any author would have to do in order to support the caching functionality here.
          As discussed above, we can see that adding error handling is trivial.
        </p>
      </section>
      <section>
        <h2>Example: Forcing the User Agent to return a fresh cached position</h2>
        <p>
          Original example:
        </p>
        <pre class='example sh_javascript_dom'>
navigator.geolocation.getCurrentPosition(successCallback,
                                         errorCallback,
                                         {maximumAge:600000, timeout:0});
function successCallback (position) {
  // By using the 'maximumAge' option above, the position
  // object is guaranteed to be at most 10 minutes old.
  // By using a 'timeout' of 0 milliseconds, if there is
  // no suitable cached position available, the User Agent 
  // will immediately invoke the error callback with code
  // TIMEOUT and will not initiate a new position
  // acquisition process.
}
function errorCallback (error) {
  switch(error.code) {
    case error.TIMEOUT:
      // Quick fallback when no suitable cached position exists.
      doFallback();
      // Acquire a new position object.
      navigator.geolocation.getCurrentPosition(successCallback, errorCallback);
      break;
    case ... // treat the other error cases.
  };
}
function doFallback() {
  // No fresh enough cached position available.
  // Fallback to a default position.
}
        </pre>
        <p>
          LREST version:
        </p>
        <pre class='example sh_javascript_dom'>
Unknown
        </pre>
        <p>
          Supporting a timeout is not hard, in fact <code>$.ajax</code> does have a timeout parameter
          which could be used for any value other than the one used here: zero. The special semantics of
          timeout zero here taken to mean "give me a value from cache or don't give me anything" have no
          mapping to HTTP.
        </p>
        <p>
          Again, while this must be noted as a limitation — which prevents the implementation of Geolocation
          as a LREST service atop the existing XHR stack — it may not preclude other APIs that have no such
          requirement from being supported.
        </p>
      </section>
    </section>
    
    <section>
      <h2>Discussion</h2>
      <p>
        Several points have been outlined above that require further discussion. On top of this, some additional,
        less concrete items have been brought up on the mailing list and need be addressed.
      </p>
      <section>
        <h2>Which URI to expose</h2>
        <p>
          All the examples used above make use of a mythical <code>service:</code> URI scheme. This should be seen
          as a placeholder rather than as a recommendation for the ideal solution. There are several considerations
          to look at before making a decision.
        </p>
        <p>
          The first option is to mint a <code>service:</code> URI scheme that we could use to access LREST
          services. This would involve defining what the authority is (we could use the domain name of the
          defining body), followed by an identifying path for the given API, and possibly a query string
          for further identification (for instance a JSONQuery).
        </p>
        <p>
          There are several downsides to this approach, one of which is the number of hoops that one has to
          jump through in order to mint a new scheme (and we'd get a lot of flak if we didn't register it —
          not that we'd necessarily get any less flak for doing the "right thing"). The main problem is that
          we are defining this endpoint as acting like an HTTP endpoint, accessed through HTTP APIs, with
          HTTP return codes and HTTP semantics, HTTP headers... you get the idea: it quacks like HTTP, in fact
          it is HTTP. If it's HTTP, then why mint a new scheme instead of using HTTP?
        </p>
        <p>
          And that's the second option: just using HTTP. In fact, one of the arguments in favour of LREST is
          that websites could offer their own endpoints with the same API as the local one; for instance, Facebook
          could expose the Contacts API in the very same way that the LREST contacts are exposed. But that opens
          up the question of how one would point to an actual LREST service, knowing that it cannot be mixed up
          with something on the network (if only for security reasons). None of the options I can think of
          (highjacking localhost, using a magic URI that couldn't otherwise exist or depends on a specific TLD,
          using a magic port) is good, in fact they all seem fairly horrible.
        </p>
        <p>
          If using HTTP URIs doesn't work and defining a new scheme is fraught with peril and inelegance, are
          we damned if we do and done in damnation? Not necessarily, for there is no issue that cannot be
          addressed by being stuffed far, far away under the carpet. We could therefore use completely opaque
          identifiers:
        </p>
        <pre class='example sh_javascript_dom'>
$.getJSON(navigator.geolocation.getPosition, showMap);
$.getJSON(navigator.services.contacts.get, listContacts);
// etc.
        </pre>
        <p>
          Since of course those opaque strings could be exposed, we would still need something defined but it
          could be as simple as a <code>uuid:</code> URI, or a given namespace ID inside of a URN with an
          arbitrary, implementation-dependent value. Similar discussions are ongoing in WebApps concerning
          the File Reader API.
        </p>
      </section>
      <section>
        <h2>Access Control</h2>
        <p>
          One issue being discussed and not addressed in this document is that of access control. Indeed,
          most APIs that we are concerned with should not be exposed without user consent.
        </p>
        <p>
          In effect, it is unlikely that solutions that have been listed so far would function for LREST.
          Both CORS and UMP are concerned with access to remote services being expressed by said remote
          service (as opposed to interactively by a user). While at the implementation level they could be
          used (in a rather naïve implementation that would actually use a local web server) to authorise
          the request they don't address the problem of getting the user's consent in the first place.
          Likewise, OAuth could be used to provide the script with a form of token that it would use to
          access the API, but that doesn't solve consent either (and potentially leads to rather clumsy UI).
        </p>
        <p>
          I would expect all the APIs concerned by a potential LREST model to have similar access control
          mechanisms, and such mechanisms to be used irrespective of whether they are exposed through LREST
          or through vanilla JS APIs (e.g. infobar decision).
        </p>
      </section>
      <section>
        <h2>Defining the Protocols</h2>
        <p>
          Issues outlined in this section only need to be addressed if we do select a LREST approach,
          they are listed mostly for purposes of discussion.
        </p>
        <dl>
          <dt>Defining JSON</dt>
          <dd>
            <p>
              An LREST approach does not prevent us from being formal. In fact, specifications will need
              to define what the JSON objects that are exchanged over LREST look like (note that I am
              assuming JSON to the protocol's syntax rather than XML, as it is much easier to process
              from JS and fits the requirements).
            </p>
            <p>
              Two formalisms could be used: WebIDL or JSON Schema. Neither has been finalised. JSON Schema
              is more powerful but WebIDL is better known and more supported in tooling. We would also need to 
              define how unknown properties are expected to be treated and how implementers are supposed to make
              extensions. Common serialisations for some frequently needed types (such as Dates) may be needed.
            </p>
          </dd>
          <dt>Querying JSON</dt>
          <dd>
            <p>
              Some API calls require filtering, for instance to find a given contact by its full name.
              This could be defined using JSON Query, with the caveat that it is not currently well specified.
            </p>
          </dd>
          <dt>Subscription Protocols</dt>
          <dd>
            <p>
              These could be out of scope (as described above), or a comet protocol could be used.
            </p>
          </dd>
          <dt>Object Creation</dt>
          <dd>
            <p>
              One aspect that isn't covered in the Geolocation examples is creation of a new object for
              storage. This could be done rather naturally using POST and PUT requests.
            </p>
          </dd>
        </dl>
      </section>
      <section>
        <h2>Performance Concerns</h2>
        <p>
          The level of indirection involved in using LREST raises performance and integrability concerns. 
          While translation between native representation and Javascript objects has a non-negligible cost,
          it will be lower than that involved in resolving a URI (even if opaque) to a service, and producing
          the JSON representation (which will then have to be parsed...) for the same object.
        </p>
        <p>
          This issue could be partly alleviated by having a <code>responseJSON</code> field available on 
          XMLHttpRequest objects under certain conditions, and only serialising if <code>responseText</code>
          is requested. Protocol overhead should not be a major issue as most of it can be emulated away (i.e.
          skipped) though one would have to look at each specific header supported by XMLHttpRequest to
          make sure that it would not cause particular issues (notably semantic clashes when ignored).
        </p>
        <p>
          Nevertheless integration into existing runtimes needs to be investigated, notably the feasibility
          for approaches such as PhoneGap to intercept LREST requests and to process them with sufficient
          efficiency on mobile devices.
        </p>
        <p>
          Ideally, if the debate continues we would want to benefit from closer evaluation of the feasibility
          and a rough idea of the performance impact.
        </p>
      </section>
      <section>
        <h2>REST as an API Modelling Approach</h2>
        <p>
          John Kemp of the TAG has suggested that the WG merely use REST as a model from which to create
          conventional JS APIs rather than going all the way to defining a full LREST approach. This
          idea has issues similar to those in Steve Lewontin's interesting suggestion to model at least some of the APIs
          (probably the same set as selected here) as databases in that it doesn't cleanly account for
          subscriptions, but it does have the advantage of a more readily defined querying system
          (given the pains of agreeing on some form of SQL).
        </p>
        <p>
          This may indeed be an option, though how it will be exposed in specifications or how it is
          to be evaluated against proposed API designs would still need to be decided.
        </p>
      </section>
    </section>
  </body>
</html>
Received on Wednesday, 20 January 2010 14:30:00 GMT

This archive was generated by hypermail 2.2.0+W3C-0.50 : Wednesday, 20 January 2010 14:30:00 GMT