#!/usr/bin/perl -Tw # # demo.pl - Redland CGI database and query demo Perl program # # Copyright (C) 2000-2004, David Beckett http://www.dajobe.org/ # Copyright (C) 2000-2004, University of Bristol, UK http://www.bristol.ac.uk/ # # This package is Free Software and part of Redland http://librdf.org/ # # It is licensed under the following three licenses as alternatives: # 1. GNU Lesser General Public License (LGPL) V2.1 or any newer version # 2. GNU General Public License (GPL) V2 or any newer version # 3. Apache License, V2.0 or any newer version # # You may not use this file except in compliance with at least one of # the above three licenses. # # See LICENSE.html or LICENSE.txt at the top of this package for the # complete terms and further detail along with the license texts for # the licenses in COPYING.LIB, COPYING and LICENSE-2.0.txt respectively. # # # # CHANGE THIS FOR YOUR CONFIGURATION $::ROOT_DIR='/somewhere'; use strict; # Helps with broken web requests (missing headers) $ENV{'Content-Length'}||=0; # Tainting, dontcha know $ENV{'PATH'}="/bin:/usr/bin:/usr/local/bin"; # Standard perl modules use CGI; use LWP::Simple; use URI::URL; # Configuration my(@commands)=qw(destroy query parse add print), my(%command_labels)=('destroy' =>'Delete database', 'query' =>'Find triples', 'add', =>'Add a triple', 'parse', =>'Parse RDF', 'print', =>'Print database'); my(%command_needs_write)=('destroy' =>1, 'query' =>0, 'add', =>1, 'parse', =>1, 'print', =>0); my(@parsers)=qw(raptor ntriples); my(%parser_labels)=('raptor' => 'RDF/XML', 'ntriples' => 'N-Triples' ); my(%parser_just_file_uris)=('raptor' => 1, 'ntriples' => 1 ); my $default_command='parse'; my $db_dir="$::ROOT_DIR/db"; my $tmp_dir="$::ROOT_DIR/tmp"; my $log_file="$::ROOT_DIR/logs/demo.log"; # Used for deleting databases my @suffixes=qw(po2s so2p sp2o); my $db_format="%s-%s.db"; # args: db suffix # 1: Use stream parsing (print as it goes) or # 0: parse into model (prints resulting model) my $use_parse_stream=1; my $max_stream_size=200; my(%namespaces)=( 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#', 'rss' => 'http://purl.org/rss/1.0/', 'synd' => 'http://purl.org/rss/1.0/modules/syndication/', 'dc' => 'http://purl.org/dc/elements/1.1/', 'owl' => 'http://www.w3.org/2002/07/owl#', 'xsd' => 'http://www.w3.org/2001/XMLSchema#', ); # Redland perl modules use RDF::Redland; ###################################################################### # Subroutines sub log_action ($$$;$) { my($host, $db, $message, $now)=@_; $now ||= time; return unless open (LOG, ">>$log_file"); my($sec,$min,$hour,$mday,$mon,$year)=gmtime $now; my $date=sprintf("%04d-%02d-%02dT%02d:%02d:%02dZ",1900+$year,$mon+1,$mday,$hour,$min,$sec); print LOG "$host $date $db $message\n"; close(LOG); } sub end_page($) { my $q=shift; print <<'EOT';

The source code of this demonstration is available in the Redland distribution as demos/demo.pl or from the Redland website

EOT print qq{
\n\n\n\n\n\n}; } ###################################################################### # Main code my $q = new CGI; # CGI parameter paranoia my $val; my $db; $val=$q->param('db'); if(defined $val && $val =~ /^(\w+)$/) { $db=$1; } else { $db=undef; } my $command; $val=$q->param('command'); if(defined $val && $val =~ /^([a-z]+)$/) { $command=$1; } else { $command=undef; } my $statement_string; $val=($q->param('triple') || $q->param('statement')); if(defined $val && $val =~ /^([ -~]+)$/) { $statement_string=$1; } else { $statement_string=''; } my $uri_string; $val=$q->param('uri'); if(defined $val && $val =~ /^([ -~]+)$/) { $uri_string=$1; } else { $uri_string=undef; } my $parser_string; $val=$q->param('parser'); if(defined $val && $val =~ /^([a-z]+)$/) { $parser_string=$1; } else { $parser_string=undef; } my $rdf_content=undef; $val=$q->param('content'); # WARNING: pass through of all data if(defined $val) { $val=~ s/^\s*//; $val=~ s/\s*$//; if($val =~ /^(.+)$/s) { $rdf_content=$1; } } my $format_namespaces=''; $val=$q->param('format_namespaces'); if(defined $val) { $format_namespaces=($val eq 'yes'); } # End of parameter decoding # Used in logging my $host=$q->remote_host; ###################################################################### # Emit content print $q->header(-type => 'text/html', -charset=>'utf-8'); # Always print header print <<"EOT"; Redland RDF Database Demo

Redland RDF Database Demo

  1. Pick a database name
  2. Add some RDF triples to the database in one of these ways:

    1. Enter a triple by hand in the Triple field and use the Add a triple command.
    2. Enter some RDF/XML or N-Triples in the Content area and use the Parse RDF command.
    3. Give the URI of the RDF/XML or N-Triples on the web to parse in the URI field and use the Parse RDF command.
  3. Print the database - select the Print database command.
  4. Query the database - either click on the triples from the result of the previous command
  5. or enter a triple in the triple field and use the Find triples command to return matching triples
  6. Delete the database when you are done - thanks!

The syntax of triples is as follows. A triple about a resource is written as:

  [subject]--[predicate]-->[object]

and a triple with a string value is written as:

  [subject]--[predicate]-->"object"

When used as a query, replace any of the parts with ? to match anything e.g.: ?--?-->"foo" to return all triples which have the object string foo.


EOT # use q->url() to get URL of this script without any query parameters # since we are using a POST here and don't want them added to the # submission URL. my $action_url="/".$q->url(-relative=>1); print $q->start_form(-method=>'POST', -action => $action_url),"\n"; print "\n\n

Database name (required):\n"; print $q->textfield(-name=>'db', -default=>'', -size=>20, -maxlength=>20); print "\n\n with Command \n"; print $q->popup_menu(-name=>'command', -values=>\@commands, -default=>$default_command, -labels=>\%command_labels); print "\n\n

Triple (for Find and Add Triples commands):\n"; print $q->textfield(-name=>'triple', -default=>'', -size=>80); print "\n\n

RDF content syntax the Parse RDF command\n"; print $q->popup_menu(-name=>'parser', -values=>\@parsers, -default=>$parsers[0], -labels=>\%parser_labels); print "

    \n
  1. URI of RDF content (or URI of content pasted below)
    \n"; print $q->textfield(-name=>'uri', -default=>'', -size=>80, -maxlength=>1024); print "
  2. \n\n
  3. RDF/XML or N-Triples Content
    \n"; print $q->textarea(-name=>'content', -default=>'', -columns=>80, -rows=>10); print "
  4. \n\n
\n\n"; print "

Present RDF, RDFS, RSS, DC properties in namespace:qname form?\n"; print $q->popup_menu(-name=>'format_namespaces', -values=>['yes','no'], -default=>'no'); print "
(This can make the tables a lot narrower and easier to read)

\n\n"; print "\n\n

"; print $q->submit('Go'),"\n"; print "

\n"; print $q->endform,"\n"; # Any parameters? if(!$q->param) { end_page($q); exit 0; } print "
\n"; ###################################################################### # Validate me if(!$db) { print "\n\n

You must enter a database name.

\n"; end_page($q); exit 0; } if($command && !grep($_ eq $command, @commands)) { print "\n\n

No such command '$command'

\n"; $command=undef; } if(!$command) { end_page($q); exit 0; } if($parser_string && !grep($_ eq $parser_string, @parsers)) { print "\n\n

No such parser '$parser_string' - valid list are @parsers

\n"; $parser_string=''; } my $model=undef; my $storage=undef; my $db0="$db_dir/".sprintf($db_format, $db, $suffixes[0]); if(! -r $db0) { my $write=$command_needs_write{$command} ? 'yes' : 'no'; $storage=new RDF::Redland::Storage("hashes", $db, "new='yes',write='$write',hash-type='bdb',dir='$db_dir'"); if($storage) { $model=new RDF::Redland::Model($storage, ""); } if(!$storage && !$model) { log_action($host, $db, "Failed to create database"); print "\n\n

Sorry - failed to create RDF Database $db. This problem has been recorded.

\n"; end_page($q); exit 0; } log_action($host, $db, "Created new database"); } if($command eq 'destroy') { for my $suffix (@suffixes) { unlink "$db_dir/".sprintf($db_format, $db, $suffix); } log_action($host, $db, "Destroyed database"); print "\n\n

Destroyed RDF database $db OK.

\n"; end_page($q); exit 0; } # If model wasn't created/opened above, create and open now if(!$model) { # Remaining commands need an open database my $write=$command_needs_write{$command} ? 'yes' : 'no'; # Unless exists already and not writable $write='no' if -r $db0 && !-w $db0; $storage=new RDF::Redland::Storage("hashes", $db, "new='no',write='$write',hash-type='bdb',dir='$db_dir'"); if(!$storage) { log_action($host, $db, "Failed to open storage"); print "\n\n

Sorry - failed to open RDF Database $db. This problem has been recorded.

\n"; end_page($q); exit 0; } $model=new RDF::Redland::Model($storage, ""); if(!$model) { log_action($host, $db, "Failed to create model"); print "\n\n

Sorry - failed to open RDF Model for RDF Database $db. This problem has been recorded.

\n"; end_page($q); exit 0; } } my $statement=undef; my $stream=undef; if($command ne 'print' && $command ne 'parse') { if ($statement_string !~ m%^ (?: \? | \[[^]]+\]) \s*--\s* (?: \? | \[[^]]+\]) \s*--\>\s* (?: \? | [\["] [^]"]+ [\]"]) $%x) { # " print <<"EOT"; \n\n

Sorry, I do not understand the triple syntax; it should match that described above.

EOT end_page($q); exit 0; } my($subject_string,$predicate_string,$object_string)=(split(/-->?/, $statement_string, 3)); if($subject_string =~ m%^\[(.+)\]$%) { $subject_string=$1; } else { $subject_string='?'; } if($predicate_string =~ m%^\[(.+)\]$%) { $predicate_string=$1; } else { $predicate_string='?'; } my($delim1,$delim2); if($object_string =~ m%^([\["])(.+)([\]"])$%) { ($delim1,$object_string,$delim2)=($1,$2,$3); } else { $object_string='?'; } my $subject=($subject_string ne '?') ? RDF::Redland::Node->new_from_uri_string($subject_string) : undef; my $predicate=($predicate_string ne '?') ? RDF::Redland::Node->new_from_uri_string($predicate_string) : undef; my $object; if($object_string ne '?') { if($delim1 eq '[') { # Assumes delim2 eq ']' $object=RDF::Redland::Node->new_from_uri_string($object_string); } else { $object=RDF::Redland::Node->new_from_literal($object_string, "", 0, 0); } } else { $object=undef; } $statement=RDF::Redland::Statement->new_from_nodes($subject, $predicate, $object); if(!$statement) { print "\n\n

Failed to create RDF statement from nodes

\n"; end_page($q); exit 0; } # Now $statement is a RDF statement object if required } if($command eq 'add') { $model->add_statement($statement); print "\n\n

Added statement '$statement_string' to model

\n"; log_action($host, $db, "Added statement '$statement_string'"); end_page($q); exit 0; } my $temp_file; if($command eq 'print') { $stream=$model->as_stream; log_action($host, $db, "Printing database"); } elsif ($command eq 'query') { $stream=$model->find_statements($statement); log_action($host, $db, "Querying for statement '$statement_string'"); } else { my $uri; my $source_uri; my $uri_from_form=0; $parser_string ||= $parsers[0]; if($uri_string) { eval "\$uri=new URI::URL(q{$uri_string});"; if($@ || !$uri) { print "\n\n

URI \"$uri\" is not a legal URI (according to perl)

\n"; end_page($q); exit 0; } if(!$uri->scheme || $uri->scheme ne 'http') { print "\n\n

Cannot use URI $uri_string - must be a web http URI.

\n"; end_page($q); exit 0; } } if(defined $rdf_content) { # have content on web page, maybe with URI $temp_file="$tmp_dir/redland-demo-$$.rdf"; if(open(OUT, ">$temp_file")) { print OUT $rdf_content; close(OUT); } else { log_action($host, $db, "Failed to create $temp_file - $!"); print "\n\n

Sorry - cannot create temporary file for RDF content. This problem has been recorded.

\n"; end_page($q); exit 0; } $source_uri=new URI::URL("file:$temp_file"); if($uri) { # already validated above $uri=new URI::URL $uri; } else { $uri=new URI::URL("http://somewhere/"); } $uri_from_form=1; } elsif($uri_string) { # else have only URI $source_uri=new URI::URL $uri; # Must fetch and copy to temp file if($parser_just_file_uris{$parser_string}) { $temp_file="$tmp_dir/redland-demo-$$.rdf"; my $rc=getstore($uri_string, $temp_file); if(!is_success($rc)) { print "\n\n

Failed to read URI $uri_string - HTTP error $rc

\n"; end_page($q); exit 0; } $source_uri=new URI::URL("file:$temp_file"); } } else { # no content or URI print "\n\n

Must give a URI or RDF content for parse command

\n"; end_page($q); exit 0; } my $parser=new RDF::Redland::Parser($parser_string); if(!$parser) { log_action($host, $db, "Failed to create RDF parser $parser_string"); print "\n\n

Sorry - failed to create RDF parser $parser_string. This problem has been logged.

\n"; end_page($q); #unlink $temp_file if $temp_file; exit 0; } my $redland_base_uri=new RDF::Redland::URI $uri; my $redland_source_uri=new RDF::Redland::URI $source_uri; my $uri_label=$uri_from_form ? qq{RDF from form} : qq{URI "$uri"}; log_action($host, $db, "Parsing $uri_label with parser $parser_string"); if($use_parse_stream) { $stream=$parser->parse_as_stream($redland_source_uri, $redland_base_uri); if(!$stream || $stream->end) { print "\n\n

$uri_label failed to parse as RDF into model via stream with $parser_string parser

\n"; end_page($q); #unlink $temp_file if $temp_file; exit 0; } else { print "\n\n

$uri_label parsed as RDF into model via stream with $parser_string parser OK

\n"; } } else { $parser->parse_into_model($redland_source_uri, $redland_base_uri, $model); print "\n\n

$uri_label parsed as RDF into model with $parser_string parser OK

\n"; #unlink $temp_file if $temp_file; $stream=$model->as_stream; } } print <<"EOT";
EOT #$RDF::Debug=1; my $count=0; for(;!$stream->end ; $stream->next) { # Must use a new statement object here since $statement may be # used/shared by the model's find_statements method my $statement2=$stream->current; last if !$statement2; my $subject=$statement2->subject->as_string; my $predicate=$statement2->predicate->as_string; my $object=$statement2->object->as_string; my $subject_label=$subject; my $predicate_label=$predicate; my $object_label=$object; if($format_namespaces) { for my $label_ref (\$subject_label, \$predicate_label, \$object_label) { while(my($ns_prefix,$ns_uri)=each %namespaces) { $$label_ref =~ s%^\[${ns_uri}([^/]+)\]$%[${ns_prefix}:$1\]%; } } } $q->param('command', 'query'); $q->delete('content'); # HACK - makes generated URIs just too large $q->param('triple', "$subject--?-->?"); my $subject_query=$q->self_url; $q->param('triple', "?--$predicate-->?"); my $predicate_query=$q->self_url; if($object =~ /^\[(.+)\]$/) { $q->param('triple', "?--?-->[$1]"); } else { $object=qq{"$object"}; $q->param('triple', "?--?-->$object"); } my $object_string=$object_label; my $from_object=''; if($object =~ /^\[(.+)\]$/) { my $object_query=$q->self_url; $q->param('triple', "[$1]--?-->?"); $object_string=qq{$object_label}; $from_object=qq{Follow}; } else { $from_object=' '; } print << "EOT"; EOT $count++; if ($count >= $max_stream_size) { print << "EOT"; EOT last; } if($use_parse_stream) { if($command eq 'parse') { $model->add_statement($statement2); } } } # while if($use_parse_stream) { if($command eq 'parse') { #unlink $temp_file if $temp_file; } } print <<"EOT";
Subject Predicate Object Follow
Object
$subject_label $predicate_label $object_string $from_object
Truncated at $max_stream_size items - sorry, this is just a demonstration.
EOT my $pl=($count != 1) ? 's' : ''; print "\n\n

Found $count triple$pl

\n"; if($format_namespaces) { print "

Where the following namespace prefixes were used:

\n
"; for my $ns_prefix (sort keys %namespaces) { my $ns_uri=$namespaces{$ns_prefix}; print qq{
${ns_prefix}
\n
namespace ${ns_uri}
\n}; } print "\n
\n\n"; } end_page($q); exit 0;