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.