XRI - Resolver for eXtensible Resource Identifiers


XRI documentation Contained in the XRI distribution.

Index


Code Index:

NAME

Top

XRI - Resolver for eXtensible Resource Identifiers

SYNOPSIS

Top

    use XRI;
    my $XRI = XRI->new($xri);

    $XRI->resolveToAuthorityXML;
    print $XRI->{descriptorXML};

    $XRI->resolveToLocalAccessURI;
    $XRI->resolveToLocalAccessURI(service, type);
    print $XRI->{localAccessURL};

    print $XRI->doGet;

ABSTRACT

Top

Resolve an XRI to a LocalAccess URL or an XRIAuthority Descriptor

DESCRIPTION

Top

The XRI Resolver Library resolves an XRI to a LocalAccess URL or an XRIAuthority Descriptor. It can also be used to actually fetch the data at the LocalAccess URL.

FATAL ERROR STRINGS

Top

These strings are returned via die() so trap them by running library calls within an eval{} block $@ will be set to the error code, if any.

    RelativePathNotXRIAuthority for passed service or type
    URIAuthorityNotXRIAuthority for passed service or type
    UnknownAuthority: $subseg
    NoDescriptorXML for $url
    MalformedXRIDescriptor for $url
    NoLocalAccessFound for XRI $self->{xri}

BUGS

Top

SEE ALSO

Top

XRI OASIS TC: http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=xri

XRI Specification: http://www.oasis-open.org/committees/download.php/5109/xri-syntax-resolution-1.0-cd.pdf

Mailing list: http://idcommons.net/cgi-bin/mailman/listinfo/xrixdi

Wiki: http://xrixdi.idcommons.net/

AUTHOR

Top

Fen Labalme, <fen@idcommons.net>

COPYRIGHT AND LICENSE

Top


XRI documentation Contained in the XRI distribution.

# Copyright (C) 2004 Identity Commons.  All Rights Reserved.
# See LICENSE for licensing details

# Author: Fen Labalme <fen@idcommons.net>

# TODO: Insert logic about choosing an authority from the current descriptor
# TODO: Logic about whether to look at the mapping, etc.

require 5.6.0;

package XRI;
our $VERSION = '0.2.4';

use strict;

use XRI::Parse qw( GCS_CHARS );
use XRI::Descriptor;

use XML::Smart;
use URI::Escape;
use LWP::Simple;

use Log::Agent;
logconfig(-prefix => $0);

# this gets changed during make install
my $ROOTS = '/etc/xriroots.xml';

my $xrins = 'xri:$r.s/XRIDescriptor';

our %globals = ();
our %private = ();

sub new {
    my $self = shift;
    my $xri = shift;
    my $this = { descriptor=>undef,
                 descriptorXML=>undef,
                 localAccessURL=>undef,
                 xri=>$xri };
    bless $this, $self;
}

# Converts XRIs with DNS-based Authorities to HTTP URIs and stores the localaccessurl
#
# FIXME: more escaping needed - also need to check for absoluteness
#
sub convertToHTTP {
    my $self = shift;

    $self->{localAccessURL} = "http:" .
        (( $self->{xri} =~ /^xri:(.*)$/ ) ? $1 : $self->{xri} );
}

sub resolveToAuthorityXML {
    my $self = shift;
    my ($service, $type) = @_;
    my $XRI = new XRI::Parse $self->{xri}; # FIXME: created two XRI objects...
    my ($authRef, $descriptor, @authURIs);
    my $descXML = undef;

    # get the authority and local access parts
    #
    my $authLocal = $XRI->splitAuthLocal;

    # no authority part -> relative-path
    #
    if ( ! defined $XRI->{authority} ) {
        die "RelativePathNotXRIAuthority for passed service or type\n"
            if ($service || $type);
        $self->{localAccessURL} = $authLocal;
        logtrc 'notice', "No XRI Authority - relative-path resolved";
        return;
    }
    # if the first segment is '//' convert to HTTP and return
    #
    if ( $XRI->{authority} eq '//') {
        die "URIAuthorityNotXRIAuthority for passed service or type\n"
            if ($service || $type);
        $self->convertToHTTP;
        logtrc 'notice', "XRI Authority is DNS Based - XRI converted to HTTP";
        return;
    }
    logtrc 'notice', "Extracted root identifier of %s", $XRI->{authority};

    # load the roots (global roots could be "precomputed")
    # FIXME: need mechanism to incrementally add to private roots
    #
    readRoots() if ! scalar %globals;

    ($authRef, $self->{localAccessURL}) = @$authLocal;
    my $subseg = shift @$authRef;

    # get the root authority
    # HACK: we assume there's only one root AuthorityURI
    # HACK: we randomly choose the first XRIAuthority URI
    #
    my $url = isGCS( $subseg ) ? $globals{ $subseg } : $private{ $subseg };
    if (! defined $url) {
        die "UnknownAuthority: $subseg\n";
    }
    while ( $subseg = shift @$authRef ) {       # divider
        $subseg .= shift @$authRef;             # segment
        logtrc 'notice', "Resolving authority subsegment %s", $subseg;

        $url .= '/' unless $url =~ m|/$|;
        $url .= uri_escape($subseg); # FIXME turn spaces into '+', etc...

        logtrc 'notice', "Contacting Naming Authority URL %s", $url;
        $descXML = get $url; # Quick and dirty - should catch exceptions and 404s
        if ( ! defined $descXML ) {
            die "NoDescriptorXML for $url\n"; # FIXME
        }
        logtrc 'notice', "Descriptor for %s is\n%s", $subseg, $descXML;

        $descriptor = XRI::Descriptor->new($descXML);

        if (! defined $descriptor) {
            die "MalformedXRIDescriptor for $url\n";
        }
        @authURIs = @{$descriptor->getXRIAuthorityURIs};

        # is it a bug if there is no XRIAuthority URI in a delegated segment?
        # we'll accept it for now, and expect to use LocalAccess from here...
        #
        last unless scalar @authURIs;   # done if no XRI Authority URU

        $url = $authURIs[0];            # HACK: randomly choose the first URI
    }
    $self->{descriptor} = $descriptor;
    $self->{descriptorXML} = $descXML;
}

# Performs basic Authority Resolution
# Assumes we are not using a DNS-based authority 
# Sets the descriptor to the XRI
#
sub resolveToLocalAccessURI {
    my $self = shift;
    my ($service, $type) = @_;
    $self->resolveToAuthorityXML($service, $type);

    return unless $self->{descriptor};

    my $local = $self->{localAccessURL};
    my @localAccessElem = $self->{descriptor}->getLocalAccess($service, $type);

    if ( scalar @localAccessElem ) {
        
        # HACK: randomly choose the first LocalAccess element and the first URI within it
        #
        my $desLocalAccess = ${$localAccessElem[0]->uris}[0];
        logtrc( 'notice',
                "Constructed local access URL from base local access url %s and local XRI part %s",
                $desLocalAccess, $local );
        $desLocalAccess .= '/' if $local && $desLocalAccess !~ m|\/$|;
        logtrc 'notice', "Local access descriptor is %s", $desLocalAccess;
        $self->{localAccessURL} = $desLocalAccess . XRI::Parse->new($local)->escapeURI;
    }
    else {
        #No local access URL! Probably should raise an excepetion
        $self->{localAccessURL} = undef;
        die "NoLocalAccessFound for XRI $self->{xri}\n";
    }
}

sub doGet {
    my $self = shift;

    $self->resolveToLocalAccessURI
        unless defined $self->{localAccessURL};
    return get( $self->{localAccessURL} );      # returns the document
}

sub getGetURL {
    my $self = shift;

    $self->resolveToLocalAccessURI
        unless defined $self->{localAccessURL};
    return $self->{localAccessURL};
}

sub isGCS {
    my $char = shift;

    return grep { $_ eq $char } @XRI::Parse::GCS_CHARS;
}

# Read XRI roots file
# TODO: implement mechanism to add private roots
#
sub readRoots {
    my $roots = shift;

    $roots = $ROOTS unless defined $roots;

    die "XRIRootsNotFound: Can't find $roots\n" unless -r $roots;

    my $XML = XML::Smart->new($roots);

    $XML = $XML->cut_root();

    foreach my $descriptor ( @{$XML->{XRIDescriptor}} ) { # missing: ('xmlns','eq',$xrins)
        my $resolved = $descriptor->{Resolved};
        my $authority = $descriptor->{XRIAuthority}{URI};
        if ( isGCS( $resolved )) {
            logtrc 'notice', "Resolved Global %s to %s", $resolved, $authority;
            $XRI::globals{$resolved} = $authority;
        }
        else {
            logtrc 'notice', "Resolved Private %s to %s", $resolved, $authority;
            $XRI::private{$resolved} = $authority;
        }
    }
}

1;
__END__