WITW Part II: SOAP RPC
Keeping track of your location with SOAP RPC.
My second interface to Where In The World is a set of methods that you can invoke using SOAP RPC. I'm not sure I really understand all the jargon yet, but I think the salient bits are:
- The target SOAP node
-
That's “
http://norman.walsh.name/2005/02/witw/soap
” for the “where is someone” services and “http://norman.walsh.name/2005/02/witw/ami/soap
” for the “where I am” services that require authentication. (In fact, the latter node will perform all of the services.) - The namespace of the services
-
That's “
http://norman.walsh.name/witw
”. I'd have made that a dated URI, but I'm using Perl’s SOAP::Lite module to do the implementation and there was some weirdness in the way that the namespace name was mapped to the Perl module name. I decided to just punt. - The names of the methods
-
They are “
user
”, “is
”, “nearby
”, “atlatlong
”, and “atlandmark
”, described below.
As you may have noticed, I've decomposed things a little bit differently. I did that to make some of the return types more manageable.
User
The user
method returns
information about a userid
:
user(xs:string userid)
throws UnknownUser;
It returns the userid
, the
name
of the user, a hash
(in the
mbox_sha1sum-style
of FOAF)
of the user's
email
address, and the
uri
associated with the user.
If the specified userid
is unknown,
the service faults.
Is
The is
method returns information about the
location of the specified userid
.
is(xs:string userid)
throws UnknownUser, UnknownLocation;
It returns the userid
, the
date
when the location was specified, the
latitude
and the
longitude
specified by the user.
It faults if the userid
is unknown or if the
user has not specified a location.
Nearby
The nearby
method returns information about
landmarks that are close to the
location of the specified userid
.
nearby(xs:string userid,
xs:string units= "mi",
xs:decimal distance= 50.0)
throws UnknownUser, UnknownLocation, InvalidUnits, InvalidDistance;
The units
and distance
parameters are optional. If specified, units
must be
one of “mi
”, “km
”,
“m
”, or “ft
”. The default
distance is 50 miles (about 80km).
It returns zero or more sequences of landmarks:
the name
of the landmark, its
latitude
, longitude
,
any URI
associated with the landmark, and
its distance
from the user.
It faults if the userid
is unknown, if the
user has not specified a location, if the units are invalid, or if
the distance is invalid.
At Latitude and Longitude
The atlatlong
method allows a user to specify
their location. You must use the authenticated SOAP node
(http://norman.walsh.name/2005/02/witw/ami/soap
) in order
to access this method and the userid
specified
must match the authenticated user.
atlatlong(xs:string userid,
xs:decimal latitude,
xs:decimal longitude)
throws AuthorizationRequired, InvalidUser, UnknownUser, InvalidLatitude, InvalidLongitude;
It returns the userid
if it
succeeds or throws a SOAP fault.
At Landmark
The atlandmark
method allows a
user to specify that they are at a particular landmark. Like the
atlatlong
method, you must use the
authenticated SOAP node
(http://norman.walsh.name/2005/02/witw/ami/soap
) in order
to access this method and the userid
specified
must match the authenticated user.
atlandmark(xs:string userid,
xs:string landmark)
throws AuthorizationRequired, InvalidUser, UnknownUser, UnknownLandmark;
It returns the userid
if it
succeeds or throws a SOAP fault.
Sample Code
On the assumption that any who's read this far won't be scared off by a few lines of code, here's a short Perl script that returns the latitude and longitude of a user.
#!/usr/bin/perl -- # -*- Perl -*-
use strict;
use SOAP::Lite;
my $usage = "$0 userid\n";
my $userid = shift @ARGV || die $usage;
my $soap = SOAP::Lite->new();
$soap->uri("http://norman.walsh.name/witw");
$soap->proxy("http://norman.walsh.name/2005/02/witw/soap");
my $result = $soap->user($userid);
if ($result->fault()) {
print STDERR "SOAP fault: ", $result->faultcode(), "\n";
print STDERR "\t", $result->faultstring(), "\n";
exit 1;
}
my ($name, $email, $uri) = $result->paramsout();
$result = $soap->is($userid);
if ($result->fault()) {
print STDERR "SOAP fault: ", $result->faultcode(), "\n";
print STDERR "\t", $result->faultstring(), "\n";
exit 1;
}
my ($date, $lat, $long) = $result->paramsout();
$lat = sprintf("%2.2f%s", abs($lat), $lat > 0 ? "N" : "S");
$long = sprintf("%2.2f%s", abs($long), $long > 0 ? "E" : "W");
print "$name was last seen at $lat, $long.\n";
print "That was on $date.\n";
This morning, if you pass it ndw
,
it tells you I'm at home:
$
perl witw.soap.pl ndw
Norman Walsh was last seen at 42.34N, 72.45W.
That was on 2005-02-17T21:35:42Z.
To satisfy my own curiosity, I wrote a Perl script to get the same information using the brute force API:
#!/usr/bin/perl -- # -*- Perl -*-
use strict;
use XML::XPath;
my $usage = "$0 userid\n";
my $userid = shift @ARGV || die $usage;
my $uri = "http://norman.walsh.name/2005/02/witw/is/$userid";
# hack!
my $data = "";
open (F, "wget -q -O - $uri |");
while (<F>) {
$data .= $_;
}
close (F);
my $xpath = new XML::XPath($data);
$xpath->set_namespace("is", 'http://nwalsh.com/xmlns/witw-is#');
if ($xpath->exists("//is:unknown-user")) {
print STDERR "Unknown user: $userid\n";
exit 1;
}
my $name = $xpath->findvalue('//is:user/is:name');
if ($xpath->exists("//is:unknown-location")) {
print STDERR "The whereabouts of $name are unknown.\n";
exit 1;
}
my $lat = $xpath->findvalue('//is:location/@lat');
my $long = $xpath->findvalue('//is:location/@long');
my $date = $xpath->findvalue('//is:location/@date');
# XML::XPath is doing some bizarro Perl overloading.
# Skip the pretty printing.
print "$name was last seen at $lat, $long.\n";
print "That was on $date.\n";
The two styles of API are clearly quite different, but I'll reserve judgement until I've done a few more experiments.