Edward Kawas > SADI-1.10 > SADI

Download:
SADI-1.10.tar.gz

Dependencies

Annotate this POD

CPAN RT

Open  0
View/Report Bugs
Module Version: 1.10   Source  

NAME ^

SADI - Perl extension for the automatic generation of SADI web services

Upgrading From Versions prior to Version 1.08 to Version 1.08

This new version of SADI contains some modifications to the entry CGI scripts for each service. In order to stay up to date with the SADI methodology, you should re-generate the cgi scripts for all of your services.

The easiest way to update your services is to do the following (assuming for a second that your home directory is /home/ubuntu/ and that you are using a *NIX machine):

To update your entry scripts, do something like:

For synchronous services
 # C<for i in `find /home/ubuntu/Perl-SADI/cgi -type f -print`; do sadi-generate-services.pl $i; done> 
For asynchronous services
 # C<for i in `find /home/ubuntu/Perl-SADI/cgi -type f -print`; do sadi-generate-services.pl -A $i; done> 

Of course, you will have to remove the scripts located /home/ubuntu/Perl-SADI/cgi.

If you are on windows, try the following (assuming that your home directory is C:\Users\ubuntu\):

For synchronous services
 # C<for /R %i in ("C:\Users\ubuntu\Perl-SADI\cgi\*") do sadi-generate-services %i> 
For asynchronous services
 # C<for /R %i in ("C:\Users\ubuntu\Perl-SADI\cgi\*") do sadi-generate-services -A %i> 

Additionally, for those of you that pre-generate your service base files (not default operation), you will need to re-generate your service bases. For each service that is affected, do

 # C<sadi-generate-service -B your_service_name> 

for asynchronous services and a

 # C<sadi-generate-service -b your_service_name> for synchronous ones.

If you have any problems whatsoever, please contact me or any other SADI developer for help!

Upgrading From Versions prior to Version 1.04 to Version 1.04

This new version of SADI uses an updated OWL2Perl module that adds many new items to generated OWL classes, as well as, makes them more memory and cpu efficient. In order to use these features to the fullest, you will need to regenerate all of your OWL2Perl generated OWL classes again, using sadi-generate-datatypes.

Upgrading From Versions prior to 1.03 to Version 1.03

This new version of SADI uses an updated OWL2Perl module that adds property restrictions to generated OWL classes. In order to use these features to the fullest, you will need to regenerate all of your OWL2Perl generated OWL classes again, using sadi-generate-datatypes.

Upgrading From Version 0.99.4

Version 0.99.5 contains some neat new features and bug fixes! One of the added features is the ability to add unit test for your service.

All that is needed to upgrade to 0.99.5 is for you to re-run the sadi-install.pl script.

Upgrading From Version 0.99.2

For those of you upgrading a previous SADISeS installation and using OWL2Perl modules, you will need to regenerate your datatypes.

After careful consideration, it was determined that we could clean up the package names for generated owl entities.

The easiest way to update your services is to do the following (assuming for a second that your home directory is /home/ubuntu/ and that you are using a *NIX machine):

To update your implementation files, do something like:

for i in `find /home/ubuntu/Perl-SADI/services/Service -type f -print`; do perl -pi -e 's|::owl::|::|g' $i; done

If you are on windows, try the following (assuming that your home directory is C:\Users\ubuntu\):

for /R %i in ("C:\Users\ubuntu\Perl-SADI\cgi\*") do perl -pi -e 's|::owl::|::|g' %i

Additionally, for those of you that pre-generate your asynchronous service base file (not default operation), you will need to re-generate your service basis. For each service that is affected, do a sadi-generate-service -B your_service_name.

Upgrading From Version 0.99.1

For those of you upgrading a previous SADISeS installation, you will need to decide if you want to remove the CGI entry scripts for your services and re-generate them.

The reason for this is that the mime type header for SADI services has been changed to "application/rdf+xml", as per the W3C RDF spec. While this change makes your services more correct, it doesnt change how your services behave.

The easiest way to update your services is to do the following (assuming for a second that your home directory is /home/ubuntu/ and that you are using a *NIX machine):

To update your entry scripts, do something like:

for i in `find /home/ubuntu/Perl-SADI/cgi -type f -print`; do perl -pi -e 's|text/xml|application/rdf\+xml|g' $i; done

If you are on windows, try the following (assuming that your home directory is C:\Users\ubuntu\):

for /R %i in ("C:\Users\ubuntu\Perl-SADI\cgi\*") do perl -pi -e 's|text/xml|application/rdf\+xml|g' %i

SYNOPSIS ^

       # to get started, run the install script
       sadi-install.pl

       # for those of you using MS Windows (commands are issued without the ".pl")
       sadi-install

       # generate a service definition file, for example HelloSadiWorld
       sadi-generate-services.pl -D HelloSadiWorld

       # now you have to go and edit the definition file
       # in Perl-SADI/definitions/HelloSadiWorld
       # to point it to the various ontologies that your service uses
       # once you have done that, then...

       # generate a service implementation, based on this edited
       # definitions file.  In this example, you would simply type:

       sadi-generate-services.pl HelloSadiWorld

       # now add your business logic to the module that was created in
       # Perl-SADI/services/Service/HelloSadiWorld.pm

       # finally, make a symbolic link from Perl-SADI/cgi/HelloSadiWorld to
       # your cgi-bin/ directory to deploy your service.

       # assuming that you deployed it, test the service (-g gets the service interface
       # while -e allows you to send input data to the service)
       sadi-testing-service.pl -g http://localhost/cgi-bin/HelloSadiWorld

       # read the POD for more details!

DESCRIPTION ^

This is the documentation for Perl SADISeS (SADI Services Support). If you are reading this from the perldoc utility, you may notice that some words are missing or that some phrases are incomplete. In order to view this documentation in the manner intended, please view the html version of this documentation that was installed duing make install or the version on CPAN.

First of all, it is assumed that you are familiar with SADI. If this assumption is false, please go to the SADI homepage (http://sadiframework.org).

Hopefully, you have chosen to install this package so that you can create SADI web services. SADI is a novel Web Services framework that dynamically generates RDF graphs by accessing Web Services. Web Service discovery is achieved by registering the type of relationship (i.e. the predicate) that the Web Service creates between its input data and its output data.

Package Installation

Installation of this helpful perl package is straightforward!

On *nix machines, install as follows:

perl Makefile.PL
make test
make install

On Window machines, install as follows:

perl Makefile.PL
nmake test
nmake install

SADI Installation

Assuming that you have already installed this package, the very first thing that you should do is run the script sadi-install.pl.

This script will do the following:

Once the installation process is complete, you can create your first service!

SADI Services Support in Perl

Perl SADISeS, is a project aiming to help SADI service providers develop their code in Perl.

The basic design principles are the same: to provide libraries that allow a full object-oriented approach to SADI services(data types and service instances) and shielding service developers from all the nitty gritty required to develop SADI services.

New Features

Some of the new features included in this release are:

Overview

Perl SADI is a generator of Perl code - but this code does not need to be always stored in files, it can be generated on-the-fly every time a service is called. Also, there are up to four things to be generated: objects representing SADI data types, classes establishing bases for services, empty (but working) service implementation modules, and cgi-bin scripts, the entry points to our SADI services.

However, before going into the gory details, let's install Perl SADI, create and call the first service.

Quick Start - Five Steps to the First Service

1. Download the SADI module, and install it.
2. Run the installation script for SADISeS

From the command prompt, enter:

sadi-install

or,

sadi-install.pl

This works, because part of the installation for the SADISeS module entails the installation of scripts that make SADISeS tasks more simple.

3. Generate a service definition and a service implementation.

You can pick any name for your service:

sadi-generate-services -D HelloSadiWorld

or,

sadi-generate-services.pl -D HelloSadiWorld

This will create a definition for your SADI service (in the <your-home-directory>/Perl-SADI/definitions/ directory).

Next create the implementation for the SADI service by doing:

sadi-generate-services HelloSadiWorld

or,

sadi-generate-services.pl HelloSadiWorld

It creates a Perl module Service::HelloSadiWorld (in <your-home-directory>/Perl-SADI/services/), representing your first SADI Web Service . The service is empty - but is already able to accept SADI input requests and recognize its own data type, and it produces fake output data (of the correct type).

4. Make your service available from your Web Server (this is called deploying).

The only thing you need to do is to tell your Web Server where there is a starting cgi-bin script. The script was already created during the service generation phase in <your-home-directory>/Perl-SADI/cgi directory. Make a symbolic link from a cgi-bin directory of your Web Server (e.g on some Linux distributions, using Apache Web server, the cgi-bin directory is /usr/lib/cgi-bin).

For example:

cd /usr/lib/cgi-bin
sudo ln -s /home/kawas/Perl-SADI/cgi/HelloSadiWorld .

on windows, you can mimic the above

If you cannot create Symbolic links, or apache is not allowed to follow them, try this workaround

5. Last but not least: call your service.

There is a testing client that sends only empty data (so it can be used by any service):

sadi-testing-service.pl -e http://localhost/cgi-bin/HelloSadiWorld

Of course, you can also send real data, from a local file (for example, from data/my-input.xml):

sadi-testing-service.pl -e http://localhost/cgi-bin/HelloSadiWorld \
     data/my-input.xml

The output (the same with any input data) is not really that exciting:

HTTP/1.1 200 OK
Connection: close
Date: Tue, 25 Aug 2009 20:45:08 GMT
Server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.3 with Suhosin-Patch
Vary: Accept-Encoding
Content-Type: text/xml; charset=ISO-8859-1
Client-Date: Tue, 25 Aug 2009 20:45:09 GMT
Client-Peer: 127.0.0.1:80
Client-Response-Num: 1
Client-Transfer-Encoding: chunked

<rdf:RDF
xmlns:a="http://sadiframework.org/ontologies/predicates.owl#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
>
<rdf:Description rdf:about="http://someontology.org/datatypes#Output1">
<a:somePredicate0>0</a:somePredicate0>
<a:somePredicate1>1</a:somePredicate1>
<a:somePredicate2>2</a:somePredicate2>
</rdf:Description>
</rdf:RDF>

You immediately notice that the returned value does not have an expected "hello" greeting. Well, a fake output is just fake output. You would need to add your own business logic into your service to do something meaningful (such as saying warmly "Hello, SADI world").

Also, the service provider can see few log entries:

2009/08/26 08:35:28 (254) INFO> [5554] (eval 34):38 - *** REQUEST START ***
REMOTE_ADDR: 127.0.1.1, HTTP_USER_AGENT: libwww-perl/5.829, CONTENT_LENGTH: 228, CONTENT_TYPE: application/x-www-form-urlencoded
2009/08/26 08:35:28 (278) INFO> [5554] (eval 34):84 - *** RESPONSE READY ***

The number in square brackets is a process ID - it helps to find which response belongs to which request when a site gets more to many requests in the same time. And in parenthesis, there is the number of milliseconds since the program has started. More about the log format in logging.

Motivation

SADISeS in Perl was created to allow people to enter the SADI world with little to no barriers. Web services are complicated enough without SADI! Why should SADI add to the complications?

Bits and Pieces

Requirements

The other modules needed are (all available from the CPAN):

Installation

The installation script is (as well as the other Perl SADI scripts) installed at module install time and is available from any command prompt. You should run it the first time, and you can run it anytime later again. The files that are already created are not overwritten - unless you want it to be (using the -F option).

sadi-install.pl or sadi-install on the MS Windows platform

This is an example of a typical conversation and output of the first installation:



ubuntu@ubuntu:~/Perl-SADI$ sadi-install.pl
Welcome, SADIiers. Preparing stage for Perl SADI ...
------------------------------------------------------
OK. Module Carp is installed.
OK. Module CGI is installed.
OK. Module File::Spec is installed.
OK. Module Config::Simple is installed.
OK. Module File::HomeDir is installed.
OK. Module File::ShareDir is installed.
OK. Module Log::Log4perl is installed.
OK. Module HTTP::Date is installed.
OK. Module Template is installed.
OK. Module Params::Util is installed.
OK. Module Class::Inspector is installed.
OK. Module Unicode::String is installed.
OK. Module IO::String is installed.
OK. Module RDF::Core is installed.
OK. Module IO::Prompt is installed.


Installing in /home/ubuntu/Perl-SADI


Created install directory '/home/ubuntu/Perl-SADI'.
Created install directory '/home/ubuntu/Perl-SADI/cgi'.
Created sample-resources directory '/home/ubuntu/Perl-SADI/sample-resources'.
Created service defintions directory '/home/ubuntu/Perl-SADI/definitions'.
Created service defintions directory '/home/ubuntu/Perl-SADI/unittest'.
Created service defintions directory '/home/ubuntu/Perl-SADI/xml'.


Log properties file created: '/home/ubuntu/Perl-SADI/log4perl.properties'.


Configuration file created: '/home/ubuntu/Perl-SADI/sadi-services.cfg'.


Done.


All these things can be done manually, at any time. Installation script just makes it easier for the first comers. Here is what the installation does:

    * It checks if all needed third-party Perl modules are available. 
        Since you got this far, they most likely are! It does not help with 
        installing them, however. Perl has a CPAN mechanism in place to do 
        that. The required modules are listed in requirements. Installation
        stops if some module is not available.

        * It creates a directory called 'Perl-SADI' in your user directory.
        Perl SADI will stop working if you move this directory because it 
        contains vital configuration information inside it.
        
        * It creates sub directories in the 'Perl-SADI' directory for places
        to store your service definitions, generated code, example service
        input, etc.

    * It creates two empty log files Perl-SADI/services.log and
        Perl-SADI/parser.log - unless they already exist. In any case,
        it changes their permissions to allow everybody to write to them.
        This helps later, when the same log files are written to by a Web
        Server. The purpose of these two files is described in logging.

    * It creates a Perl-SADI/log4perl.properties file from a distributed
        template, and updates their locations to reflect your local
        installation. Again, more about this file in logging.

    * Finally, it creates a configuration file Perl-SADI/sadi-services.cfg 
        (unless it already exists). See more about how to further configure Perl 
        SADI in configuration.

If you wish to install from scratch (the same way it was done the first time), start it by using a force option:

sadi-install.pl -F

In this mode, it overwrites the files sadi-services.cfg, services.log, parser.log and log4perl.properties.

There is a little extra functionality going on behind the scenes: If the configuration file sadi-services.cfg exists when you start the installation script, its values are used instead of default ones. It may be useful in cases when you plan to put all Perl SADI directories somewhere else (typically and for example, if your Web Server does not support symbolic links that can point to the current directories). In such cases, edit your sadi-services.cfg, put the new locations inside it, and run sadi-install.pl again.

What Perl SADI Really Does

Perl Moses generates Perl code. Actually, up to four pieces of the code:

Perl Datatypes Representing OWL classes for SADI services

SADISeS allows for easier implementation of SADI services by allowing you to use automatically generated PERL modules representing OWL classes in your service implementation.

The Generated perl modules that represent OWL classes have their own constructors, contain getter and setters for their datatype properties, and even have range checking for object properties. While the generator isn't perfect, it will do the job in most cases; making your service provision much more simple.

Perl Modules Representing Bases of Service Implementations

Each Perl SADI service implementation can benefit by inheriting some basic functionality from its base. These bases contain the code specific for the given service.

The service base takes care about:

    * Logging request/response.
    * Allowing to run a service locally, outside of the HTTP environment (good for early testing).
    * Catching and reporting exceptions if the input is wrong or incomplete.

You can see its code by running (for example):

sadi-generate-services.pl -sb HelloSadiWorld

Again, the services bases can be generated and loaded on-the-fly, or pre-generated in the files.

Perl Modules Representing Empty Service Implementations

This is your playground! What is generated is only an empty service implementation - and you are supposed to add the meat - whatever your service is expected to do.

Well, it is not that empty, after all.

First, because it inherits from its base, it already knows how to do all the features listed in the paragraph above:

#-----------------------------------------------------------------
# Service name: HelloSadiWorld
# Authority:    helloworld.com
# Created:      26-Aug-2009 07:52:01 PDT
# Contact:      myaddress@organization.org
# Description:
#               the usual hello world service
#-----------------------------------------------------------------


package Service::HelloSadiWorld;


use FindBin qw( $Bin );
use lib $Bin;


#-----------------------------------------------------------------
# This is a mandatory section - but you can still choose one of
# the two options (keep one and commented out the other):
#-----------------------------------------------------------------
use SADI::Base;
# --- (1) this option loads dynamically everything
BEGIN {
    use SADI::Generators::GenServices;
    new SADI::Generators::GenServices->load(
         service_names => ['HelloSadiWorld']);
}


# --- (2) this option uses pre-generated module
#  You can generate the module by calling a script:
#    moses-generate-services -b helloworld.com HelloSadiWorld
#  then comment out the whole option above, and uncomment
#  the following line (and make sure that Perl can find it):
#use com::helloworld::HelloSadiWorldBase;


# (this to stay here with any of the options above)
use vars qw( @ISA );
@ISA = qw( com::helloworld::HelloSadiWorldBase );
use strict;

Second, it has the code that reads the input, using methods specific for this service. It does not do anything with the input, but the code shows you what methods you can use and how:

my @inputs = @$values;
# iterate over each input
foreach my $input (@inputs) {
     # NOTE: this fills in the log file
     $LOG->info ("Input data ("
        . $input->getURI ? $input->getURI : "no_uri"
        . ")"
        . $input->getLocalValue ? ":\n" . $input->getLocalValue : ""
        ."") if defined $input;
     # do something with $input ... (sorry, can't help with that)


}

And finally, it produces a fake output (not related to the input at all). Which is good because you can call the service immediately, without writing a single line of code, and because you see what methods can be used to create the real output:

# fill in the output nodes - this is what you need to do!
foreach my $output (0..2) {
	# for example ...
    $core->addOutputData(
        node => $core->Signature->OutputClass,
        value => "$output",
    	predicate => "http://sadiframework.org/ontologies/predicates.owl#somePredicate$output"
	);
}

The service implementations are definitely not generated on-the-fly. They must be pre-generated into a file (because you have to edit them, don't you?).

Again, the sadi-generate-services.pl script will do it. More in scripts.

Perl CGI scripts representing entry points to SADI services

The files generated into the Perl-SADI/cgi directory are the gateway for the world to use your SADI services. These scripts are the ones that you will be deploying to the apache cgi-bin directory and usually do not need changing!

Scripts

Scripts

The scripts are small programs that generate pieces and that let you test things.

They share some basic features:

    * They are automatically installed with the perl module.

    * They can be started from anywhere.

    * They all are Perl programs, expecting Perl executable in /usr/bin/perl. If your perl is elsewhere, start them as:

        perl -w <script-name>

    * They all recognize an option -h, giving a short help. They also have options -v (verbose) and -d (debug) for setting the level of logging.

Here they are in the alphabetic order:

sadi-config-status.pl

This script does not do much but gives you overview of your configuration and installation. You can run it to find how Perl SADI will behave when used. For example:

Perl-SADI VERSION: 0.96


Configuration
-------------
Default configuration file: sadi-services.cfg
Environment variable SADI_CFG_DIR is not set
Successfully read configuration files:
        sadi-services.cfg
All configuration parameters:
        generators.impl.definitions => /home/ubuntu/Perl-SADI/definitions
        generators.impl.home => /home/ubuntu/Perl-SADI
        generators.impl.outdir => /home/ubuntu/Perl-SADI/services
        generators.impl.package.prefix => Service
        generators.outdir => /home/ubuntu/Perl-SADI/generated
        log.config => /home/ubuntu/Perl-SADI/log4perl.properties
All imported names (equivalent to parameters above):
        $SADICFG::GENERATORS_IMPL_DEFINITIONS
        $SADICFG::GENERATORS_IMPL_HOME
        $SADICFG::GENERATORS_IMPL_OUTDIR
        $SADICFG::GENERATORS_IMPL_PACKAGE_PREFIX
        $SADICFG::GENERATORS_OUTDIR
        $SADICFG::LOG_CONFIG
        $SADICFG::LOG_FILE
        $SADICFG::LOG_LEVEL
        $SADICFG::LOG_PATTERN
Logging
-------
Logger name (use it in the configuration file): services
Available appenders (log destinations):
        Log: /home/ubuntu/Perl-SADI/services.log
        Screen: stderr
Logging level FATAL: true
Logging level ERROR: true
Logging level WARN:  true
Logging level INFO:  true
Logging level DEBUG: false


Logging configuration file
        Name: /home/ubuntu/Perl-SADI/log4perl.properties
        Contents:
log4perl.logger.services = INFO, Screen, Log
#log4perl.logger.services = DEBUG, Screen, Log


log4perl.appender.Screen = Log::Log4perl::Appender::Screen
log4perl.appender.Screen.stderr = 1
log4perl.appender.Screen.Threshold = FATAL
log4perl.appender.Screen.layout = Log::Log4perl::Layout::PatternLayout
log4perl.appender.Screen.layout.ConversionPattern = %d (%r) %p> [%x] %F{1}:%L - %m%n


log4perl.appender.Log = Log::Log4perl::Appender::File
log4perl.appender.Log.filename = /home/ubuntu/Perl-SADI/services.log
log4perl.appender.Log.mode = append
log4perl.appender.Log.layout = Log::Log4perl::Layout::PatternLayout
log4perl.appender.Log.layout.ConversionPattern = %d (%r) %p> [%x] %F{1}:%L - %m%n


# for parser debugging
log4perl.logger.parser = INFO, ParserLog
#log4perl.logger.parser = DEBUG, ParserLog


log4perl.appender.ParserLog = Log::Log4perl::Appender::File
log4perl.appender.ParserLog.filename = /home/ubuntu/Perl-SADI/parser.log
log4perl.appender.ParserLog.mode = append
log4perl.appender.ParserLog.layout = Log::Log4perl::Layout::PatternLayout
log4perl.appender.ParserLog.layout.ConversionPattern = %d (%r) %p> [%x] %F{1}:%L - %m%n


Testing log messages (some may go only to a logfile):
2009/08/26 08:38:37 (50) FATAL> [[undef]] sadi-config-status.pl:106 - Missing Dunkin' Donuts

sadi-generate-datatypes.pl

This script is really important for those of you wishing to use PERL modules representing your OWL classes.

Basically, this script reads your OWL file and processes all of the OWL classes and properties specified in that file. Once the processing is complete, PERL modules representing those classes and properties are either written to disk or displayed on screen.

One obvious question is "where are these perl objects generated"?

You can always determine this after generation by looking in the log file - the message has the INFO level which means it is almost always logged. But, if you want to know in advance here are the rules:

If there is a generators.outdir parameter in the configuration file, it is used. It defines the directory where OWL classes and properties are created.
Otherwise, program is trying to find an existing directory named 'generated' anywhere in the @INC (a set of directories used by Perl to locate its modules).
If it fails, it creates a new directory 'generated' in the "current" directory.

You can use option -s to get the generated result directly on the screen (in that case no file is created).

To generate OWL classes and properties from a remote url, you would use the -u option. This option sets the base URI for the OWL file to be the URL to the OWL file (if one didn't already exist).

If the file you would like to generate classes and properties from resides on your disk, then you would use not specify any option. If the local file doesn't specify a base URI for the ontology, you can do this with the -b flag. When the script executes, it will prompt you for a URI.

More often than not, your ontology will contain import statements. If you wish to follow them, you need to set the -i flag. This flag tells the script to process import statements, so make sure that you have an internet connection if the referenced files are located somewhere remote.

Like the other SADI scripts included in this distribution, you can access the script help page by running the script with the -h flag.

For more information on the perl OWL modules that were generated, read the perldoc for the class or property of interest!

sadi-generate-services.pl

This is the most important script. You may use only the sadi-install.pl and this one - and you will get all what you need. It generates services - all pieces belonging to services (except data types).

Usually, you generate code for one or only several services. Before you can generate an implementation for your service, you need to generate and edit a definition file for it.

sadi-generate-services.pl -D HelloSadiWorld will generate a definition file for the service and will place it into Perl-SADI/definitions. Once created, all you need to do is modify the various values as you see fit.

An example of a definition file is listed below

# make sure to escape the chars: # #,= with a \, for instance \# # new lines in a property value are not supported yet! # dont change the service name! # if you find that you need to modify # the service name, regenerate a new file # and remove this one! ServiceName = HelloSadiWorld # modify the values below as you see fit. ServiceType = http://someontology.org/services/sometype InputClass = http://someontology.org/datatypes\#Input1 OutputClass = http://someontology.org/datatypes\#Output1 Description = An implementation of the 'HelloSadiWorld' service UniqueIdentifier = urn:lsid:myservices:HelloSadiWorld Authority = authority.for.HelloSadiWorld Authoritative = 1 Provider = myaddress@organization.org ServiceURI = http://localhost/cgi-bin/HelloSadiWorld URL = http://localhost/cgi-bin/HelloSadiWorld SignatureURL = http://localhost/cgi-bin/HelloSadiWorld

Follow up the generation of the Service definition with a call to sadi-generate-services.pl to generate your skeleton files!

sadi-generate-services.pl HelloSadiWorld

There are several configurables options to influence the result:

generators.impl.outdir dictates where the code is to be generated.

generators.impl.package.prefix tells what package name should be used (the package name always ends with the service name as was used during definition generation). Default is Service.

With options, you can generated other Perl SADI pieces:



   Option -D generate a definition file for the service that you can fill in.
   This is the first thing that you need to generate before generating a service implementation!

Option -b generates service bases, Option -S generates both service bases and service implementations. This also influences how the service base will be used at run-time: if it is already generated (with the -S option) there is no need to do it again in the run-time - therefore, the service implementation is generated slightly differently - with an option "use the base, rather than load the base" enabled.

Option -A generates both a service implementation as well as an asynchronous module. This is not supported yet!

You can use option -s to get the generated result directly on the screen (in that case no file is created).

sadi-install.pl

This script is used for installation.

sadi-testing-service.pl

A script for the testing of your SADI web service. It does not give you the comfort that you can get from other SADI clients - but it is well suited for immediate testing.

It calls a SADI service in one of the two modes (actually the two modes are completely separated to the point that this script could be two scripts):

In both modes, the script can send an input RDF/XML file to the service - but if the input file is not given, an empty input is created and sent to the service. Which is not particularly useful, but still it can help with some preliminary testing.

When calling the service locally, you may use the following options/parameters:

    * A mandatory package name - a full package name of the called service.

    * Option -l location can be used to specify a directory where is the called service stored. Default is Perl-SADI/services.

    * Options -v and -d make also sense in this mode (but not in the other one).

    * An optional input file name.

sadi-testing-service.pl -d Service::HelloSadiWorld

The output of this call was already shown in this documentation. Therefore, just look what debug messages were logged (notice the -d option used):

    2009/08/26 09:17:32 (218) INFO> [6283] (eval 45):38 - *** REQUEST START ***
    2009/08/26 09:17:32 (218) DEBUG> [6283] (eval 45):46 - Input raw data:
    <?xml version="1.0"?>
    <rdf:RDF
      xmlns:b="http://www.w3.org/2000/01/rdf-schema#"
      xmlns:a="http://protege.stanford.edu/plugins/owl/dc/protege-dc.owl#"
      xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
    </rdf:RDF>
    2009/08/26 09:17:32 (227) INFO> [6283] (eval 45):85 - *** RESPONSE READY ***

The full mode has the following options/parameters:

One of:

    * An optional input file name. This method only makes sense with the -e option. As the -g option is used to get the service interface.

    sadi-testing-service.pl \
           -e http://localhost/cgi-bin/HelloSADIWorld

Configuration

Configuration means to avoid hard-coding local-specific things (such as file paths) into the code itself but hard-coding them in a separate file, a file that is not shared with other (CVS) users.

Perl SADI stores configuration in a file named moby-services.cfg. The file name is hard-coded (and cannot be changed without changing the SADI::Config module), but its location can be set using an environment variable SADI_CFG_DIR. Perl SADI looks for its configuration place in the following places, in this order:

   1. In the "current" directory (which is not that well defined when used from a Web Server).
   2. In the directory given by SADI_CFG_DIR environment variable.
   3. In the directory <your-user-dir>/Perl-SADI/.
   4. In one of the @INC directories (directories where Perl looks for its modules). 

Therefore, the best place is to keep the configuration file together where the installation script puts it anyway.

The Perl SADI internally uses Config::Simple CPAN module, but wraps it into its own SADI::Config. This allows expansion later, or even changing the underlying configuration system. The Config::Simple is simple (thus the name, and thus we selected it) but has few drawbacks that may be worth to work on later.

The file format is as defined by the Config::Simple. It can be actually of several formats. The most common is the one distributed in the sadi-services.cfg.template. This is an example of a configuration file:

[generators]
outdir = /home/ubuntu/Perl-SADI/generated
impl.outdir = /home/ubuntu/Perl-SADI/services
impl.package.prefix = Service
impl.definitions    = /home/ubuntu/Perl-SADI/definitions
impl.home = /home/ubuntu/Perl-SADI


[log]
config = /home/ubuntu/Perl-SADI/log4perl.properties
#file = /home/ubuntu/Perl-SADI/services.log
#level = info
#pattern = "%d (%r) %p> [%x] %F{1}:%L - %m%n"

The names of the configuration parameters are created by concatenating the "section" name (the one in the square brackets) and the name itself. For example, the logger configuration is specified by the parameter log.config. Parameters that are outside of any section has just their name, or they can be referred to as from the default section. For example, these two names are equivalent: default.cachedir and cachedir.

Blank lines are ignored, comments lines start with a hash (#), and boolean properties must have a value ('true' or 'false').

Obviously, important is to know what can be configured, and how. This document on various places already mentioned several configuration options. Here is their list (for more explanations about their purpose you may visit an appropriate section of this document):

generators.outdir - Directory where to generate data types and service bases. The default value for data types is 'generated', for service bases is 'services'.

generators.impl.outdir - Directory where to generate service implementations. Default is 'Perl-SADI/services'.

generators.impl.package.prefix - A beginning of the package name of the generated service implementations. Default is 'Service'. For example, a service Mabuhay will be represented by a Perl module Service::Mabuhay.

generators.impl.definitions - Directory where SADI web service definition files are kept.

generators.impl.home - The users Perl-SADI directory. Default is 'Perl-SADI'.

log.config - A full file name with the Log4perl properties. No default. If this parameter is given but the file is not existing or not readable, Perl SADI complains on STDERR (which may end up in the Web Server error.log file).

log.file - A full file name of a log file (where the log messages will be written to). No default. If the value is 'stderr' (case-insensitive) the messages will go to the STDERR. It is not clear what happens when it is used together with the above log.config.

log.level - A log level. Default is ERROR.

log.pattern - A format of the log messages. Default is '%d (%r) %p> [%x] %F{1}:%L - %m%n'.

The parameters just described are used by Perl SADI modules - but the configuration system is here also for your own services. You can invent any not-yet-taken name, and add your own parameter. In order not to clash with the future Perl Moses parameters, it is recommended to prefix your configuration properties with the service name. For example, the HelloSadiWorld service needs to read a file with "hellos" in many languages, so it defines:

[HelloSadiWorld]
resource.file = /home/ubuntu/Perl-SADI/samples-resources/HelloSadiWorld.file

How to use configuration in your service implementation?

All configuration parameters are imported to a Perl namespace SADICFG. The imported names are changed to all-uppercase and dots are replaces by underscores. You can see this change if you run the config-status.cfg:

$SADICFG::GENERATORS_IMPL_DEFINITIONS
$SADICFG::GENERATORS_IMPL_HOME
$SADICFG::GENERATORS_IMPL_OUTDIR
$SADICFG::GENERATORS_IMPL_PACKAGE_PREFIX
$SADICFG::GENERATORS_OUTDIR
$SADICFG::LOG_CONFIG
$SADICFG::LOG_FILE
$SADICFG::LOG_LEVEL
$SADICFG::LOG_PATTERN
$SADICFG::HELLOSADIWORLD_RESOURCE_FILE

In your program, you can use the imported names. For example, here is how the HelloSadiWorld service opens its resource file:

open HELLO, $SADICFG::HELLOSADIWORLD_RESOURCE_FILE
   or $self->throw ('HelloSadiWorld resource file not found.');

You can also change or add parameters during the run-time. For example, to overwrite existing parameters because you want to create everything in a separate place, in a temporary directory, and within the 'Testing' package. Because the generators read from the configuration files:

my $outdir = File::Spec->catfile ($tmpdir, 'generated-services');
SADI::Config->param ('generators.impl.outdir', $outdir);
SADI::Config->param ('generators.impl.package.prefix', 'Testing');
unshift (@INC, $SADICFG::GENERATORS_IMPL_OUTDIR);
my $generator = new SADI::Generators::GenServices;

More about how to communicate pragmatically with the configuration can be (hopefully) find in the Perl Modules Documentation.

Logging

The logging system is based on a splendid Perl module Log::Log4perl, a Perl port of the widely popular log4j logging package. The Log4perl is well documented (here is its POD documentation http://search.cpan.org/~mschilli/Log-Log4perl-1.06/lib/Log/Log4perl.pm).

How does it work in Perl SADI?

The logging is available from the moment when Perl SADI knows about the SADI::Base module. All generated service implementations inherit from this class, so all of them have immediate access to the logging system. By default, the SADI::Base creates a logger in a variable $LOG. Which means that in your service implementation you can log events in five different log levels:

$LOG->debug ("Deep in my mind, I have an idea...");
$LOG->info  ("What a nice day by a keyboard.");
$LOG->warn  ("However, the level of sugar is decreasing!");
$LOG->error ("Tim Hortons'' Donuts");
$LOG->fatal ('...and we are out of their splendid coffee!');

The logger name is "services". (The name is used in the logging configuration file - see below).

You can create your own logger. Which may be good if you wish to have, for example, a different logging level for a particular service, or for a part of it! Here is what you need to do:

use Log::Log4perl qw(get_logger :levels);
my $mylog = get_logger ('my_log_name');

Then use the name "my_log_name" in the configuration to set its own properties. Which brings also us to the logging configuration.

The logging configuration can be done in three ways:

If Perl SADI cannot find a log4perl.properties file, and if there are no logging options in sadi-services.cfg, it assumes some defaults (check them in SADI::Base, in its BEGIN section, if you need-to-know).

The better way is to use log4perl.properties file. The file name can be actually different - it is specified by an option log.config in the sadi-services.cfg configuration file. This is what Perl SADI installation creates there (of course, using your own path):

[log]
config = /home/ubuntu/Perl-SADI/log4perl.properties

The log4perl.properties is created (in the installation time) from the log4perl.properties.template, by putting there your specific paths to log files. The log4perl (or log4j) documentation explains all details - here is just a brief example what is in the file and what it means:

log4perl.logger.services = INFO, Screen, Log
  
log4perl.appender.Screen = Log::Log4perl::Appender::Screen
log4perl.appender.Screen.stderr = 1
log4perl.appender.Screen.Threshold = FATAL
log4perl.appender.Screen.layout = Log::Log4perl::Layout::PatternLayout
log4perl.appender.Screen.layout.ConversionPattern = %d (%r) %p> [%x] %F{1}:%L - %m%n
 
log4perl.appender.Log = Log::Log4perl::Appender::File
log4perl.appender.Log.filename = /home/ubuntu/Perl-SADI/services.log
log4perl.appender.Log.mode = append
log4perl.appender.Log.layout = Log::Log4perl::Layout::PatternLayout
log4perl.appender.Log.layout.ConversionPattern = %d (%r) %p> [%x] %F{1}:%L - %m%n

It says: Log only INFO (and above) levels (so no DEBUG messages are logged) on the screen (meaning on the STDERR) and to a file. But because of the "screen appender" has defined a Threshold FATAL - the screen (STDERR) will get only FATAL messages. There is no threshold in the "file appender" so the file gets all the INFO, WARN, ERROR and FATAL messages. In both cases the format of the messages is defined by the "ConversionPattern".

Note that printing to STDERR means that the message will go to the error.log file of your Web Server.

To change the log level to DEBUG, replace INFO by DEBUG in the first line.

The message format (unless you change the Perl SADI default way) means:

%d                  (%r ) %p   > [%x   ] %F{1}               :%L - %m      %n
2009/08/28 11:38:07 (504) FATAL> [26849] HelloSadiWorld:63 - Go away!
1                   2     3      4       5                    6    7       8

Where:

The last option how to specify logging properties is to set few configuration options in the sadi-service.cfg file. It was already mentioned that there is an option log.config that points to a full log property file. If this option exists, no other logging configuration options are considered. But if you comment it out, you can set the basics in the following options:

[log]
#config = /home/ubuntu/Perl-SADI/log4perl.properties
file = /home/ubuntu/Perl-SADI/services.log
level = info
pattern = "%d (%r) %p> [%x] %F{1}:%L - %m%n"

Where log.file defines a log file, log.level specifies what events will be logged (the one mentioned here and above), and the log.pattern creates the format of the log events.

This is meant for a fast change in the logging system (perhaps during the testing phase).

There are definitely more features in the Log4perl system to be explored:

For example, in the mod_perl mode it would be interesting to use the "Automatic reloading of changed configuration files". In this mode, Log::Log4perl will examine the configuration file every defined number of seconds for changes via the file's last modification time-stamp. If the file has been updated, it will be reloaded and replace the current logging configuration.

Or, one can explore additional log appenders (you will need to install additional Perl modules for that) allowing, for example, to rotate automatically log files when they reach a given size. See the Log4perl documentation for details.

Deploying

Deploying means to make your SADI web service visible via your Web Server.

By contrast to the deployment of the Java-based services, Perl services are invoked by a cgi-bin script directly from a Web Server - there is no other container, such as Tomcat in the Java world. Which makes life a whole bunch easier. Well, only slightly, because soon you will start to think about using mod_perl module, and it may make things complicated.

TO BE DONE The Perl SADI was not tested in the mod_perl environment, at all. But it should be. I wonder if anybody can explore this a bit. In order to deploy a Perl SADI service, you need:




#!/usr/bin/perl -w
# This is a CGI-BIN script, invoked by a web server when an HTTP
# POST comes in, dispatching requests to the appropriate module
# (SADI web service).
#
# It includes some hard-coded paths - they were added during the
# generate service call.
#
# $Id: SADI.pm,v 1.36 2010-03-09 18:04:43 ubuntu Exp $
# Contact: Edward Kawas <edward.kawas@gmail.com>
# ---------------------------------------------------------------


#-----------------------------------------------------------------
# Authority:    helloworld.com
# Service name: HelloSadiWorld
# Generated:    26-Aug-2009 10:15:16 PDT
# Contact: Edward Kawas <edward.kawas@gmail.com>
#-----------------------------------------------------------------


use strict;


# --- during service generation ---
# leave at the top!
use lib '/home/ubuntu/Perl-SADI/generated';
use lib '/home/ubuntu/Perl-SADI/services';
use lib '/home/ubuntu/Perl-SADI';


use CGI;
use CGI::Carp qw(fatalsToBrowser);


use SADI::Service::Instance;
use SADI::RDF::Core;
use SADI::Generators::GenServices;


# limit the max size of a post - change it as you see fit
$CGI::POST_MAX=1024 * 1024 * 10;  # max 10MB posts


# here we require the service module and add it to ISA hierarchy
use base 'Service::HelloSadiWorld';


# get the cgi variable
my $q = new CGI;


# if this is a GET, send the service interface
if ($ENV{REQUEST_METHOD} eq 'GET') {
    # send the signature for this service
        # instantiate a new SADI::RDF::Core object
        my $core = SADI::RDF::Core->new;
        # set the Instance for $core
        $core->Signature(__PACKAGE__->get_service_signature('HelloSadiWorld'));
        print $q->header(-type=>'text/xml');
    print $core->getServiceInterface();


} else {
    # call the service
    # get the data from the 'data' parameter
    my $data = $q->param('data') || $q->param('POSTDATA') || "";
    # call the service
    my $x =  __PACKAGE__->HelloSadiWorld($data);
    # print the results
    print $q->header(-type=>'text/xml');
    print $x;
}


__END__


These 2 things (a Web Server knowing about your cgi-bin scripts and a cgi-bin script knowing about Perl SADI code) are all you need to have your service deployed.

Perl Modules Documentation

After reading so far you may still wonder: Okay, but what should I do in my implementation to gain all the benefits generated for me by Perl SADI? This section will try to answer it - but notice that some particular activities were already explained in corresponding sections about logging and configuration.

How to write a service implementation

First of all, you need to have a service implementation, at least its starting (empty) phase. Generate it, using the sadi-generated-services script. Depending on how you generate it (without any option, or using an -S option) generator enables one of the following options (not that it matters to your business logic code):

#-----------------------------------------------------------------
# This is a mandatory section - but you can still choose one of
# the two options (keep one and commented out the other):
#-----------------------------------------------------------------
use SADI::Base;
# --- (1) this option loads dynamically everything
BEGIN {
    use SADI::Generators::GenServices;
    new SADI::Generators::GenServices->load(
         service_names => ['HelloSadiWorld']);
}


# --- (2) this option uses pre-generated module
#  You can generate the module by calling a script:
#    moses-generate-services -b helloworld.com HelloSadiWorld
#  then comment out the whole option above, and uncomment
#  the following line (and make sure that Perl can find it):
#use com::helloworld::HelloSadiWorldBase;

Secondly, you need to understand when and how your implementation code is called:

Your service implementation has to implement method process_it that is called for every incoming SADI web service request. The SADI::Service::ServiceBase has details about this method (what parameters it gets, how to deal with exceptions, etc.).

In the beginning of the generated process_it method is the code that tells you what methods are available for reading inputs, and at the end of the same method is the code showing how to fill the response. Feel free to remove the code, extend it, fill it, turn it upside-down, whatever. This is, after all, your implementation. And Perl SADI generator is clever enough not to overwrite the code once is generated. (It is not clever enough, however, to notice that it could be overwritten because you have not touched it yet.)

Perhaps the best way how to close this section is to show a full implementation of (so often mentioned) service HelloSadiWorld:

#-----------------------------------------------------------------
# process_it
#    This method is called for every client request.
#    Input data are in $values.
#    You will need to add output via $core->addOutputData()
#        this method adds nodes to the final output.
#        param keys: node, value, predicate
#-----------------------------------------------------------------
sub process_it {
    my ($self, $values, $core) = @_;
    # empty data, then return
    return unless $values;


    my @inputs = @$values;
    # iterate over each input
    foreach my $input (@inputs) {
        # NOTE: this fills in the log file
        $LOG->info ("Input data ("
                . $input->getURI ? $input->getURI : "no_uri"
                . ")"
                . $input->getLocalValue ? ":\n" . $input->getLocalValue : ""
                ."")
                if defined $input;
        # do something with $input ... (sorry, can't help with that)

        }

        # fill in the output nodes - this is what you need to do!
        foreach my $output (0..2) {
            # for example ...
                $core->addOutputData(
                        node => $core->Signature->OutputClass,
                value => "$output",
                    predicate => "http://sadiframework.org/ontologies/predicates.owl#somePredicate$output"
                );
        }
}

When you go through the code above you notice how to do basic things that almost every service has to do. Which are:

Reading input data:

The possible methods were already pre-generated for you so you know what methods to use. But you should always check if the data are really there (the clients can send you rubbish, of course).

The question is what to do if input (or anything else) is not complete or valid. This brings us to...

Reporting exceptions:

    One option is to throw an exception:
 
    open HELLO, $SADICFG::HELLOSADIWORLD_RESOURCE_FILE
         or $self->throw ('HELLO SADI WORLD resource file not found.');

This immediately stops the processing of the input request.

Another, less drastic, option is to record an exception (and, usually, return):

    TO BE DONE - not sure of how to report errors in SADI

Creating output data:

    Again, methods for creating response were pre-generated, so you have hints how to use them.

foreach my $output (0..2) {
    # for example ...
    $core->addOutputData(
        node => $core->Signature->OutputClass,
        value => "$output",
        predicate => "http://sadiframework.org/ontologies/predicates.owl#somePredicate$output"
    );
}

For more information on how to use the $core variable, refer to SADI::RDF::Core!

FAQ

How can I tell apache to execute HelloSadiWorld on Windows without moving the file to cgi-bin?

This can be done using the following steps (Please make sure to back up the file first!):

  1. Open the file httpd.conf and search for following text:
    ScriptAlias /cgi-bin/
  2. Underneath this text, enter something like the following (replace Eddie with your username):
    ScriptAlias /services/ "C:/Documents/Eddie/Perl-SADI/"
  3. Just below this, after the </IfModule> line, add the following text (replace Eddie with your username and the directory with your directory):
          <Directory "C:/Documents/Eddie/Perl-SADI">
             AllowOverride None
             Options +ExecCGI
             Order allow,deny
             Allow from all
          </Directory>
        
  4. Save the file and restart apache.
  5. The very last thing to do is to open up the file HelloSadiWorld and change the header
       #!/usr/bin/perl -w
    to
       #!C:/path/to/perl/bin/perl.exe -w
  6. Now anytime you read about http://localhost/cgi-bin/HelloSadiWorld, replace it with http://localhost/services/HelloSadiWorld

How Can Apache Follow Symbolic links?

Add the following to the end of your httpd.conf file:

    <Directory "/path/to/cgi-bin" >
	    Options +FollowSymLinks
	</Directory>

Make sure to change /path/to/cgi-bin to be the real path to your cgi-bin directory!

Cannot Create Symbolic links

If you cannot create symbolic links, another tested alternative would be to copy your file HelloSadiWorld to the cgi-bin directory of your web server.

Once the file has been copied, change the ownership of the file to the web servers' user/group. Also, make sure that the path (and its parents) to all of the directories in the 'use lib ...' are readable by your web server.

That's all there is to it! Now when you test your services, remember that your file may no longer be called HelloSadiWorld, but something else that you named it!

When I run the install script, IO::Prompt complains ...

This could mean that the package IO::Prompt is not installed properly.

What version do you have?

perl -MIO::Prompt -e'print "$IO::Prompt::VERSION\n";'

We have tested version 0.99.2 on both *nix machines and windows. Please make sure that you have that version. If you do not, please remove the one that you have (the cpan module CPAN Plus is very useful here) and install version 0.99.2! Version 0.99.4 doesnt seem to work too well and produces numerous warnings in our module. Other versions have yet to be tested.

Missing Features

There will be always bugs waiting to be fixed. Please let us know about them.

And there are features (and known) bugs that should or could be implemented (or fixed). Here are those I am aware of (B = bug, N = not yet implemented, F = potential future feature):

    * (N) Parsing the input and output owl classes and creating perl module stubs for them so that you can use them in your code and not have to manipulate RDF by yourself.

    * (N) Documentation of the Perl Modules is unfinished, and links to it are not yet included in this document. This is an important part of the documentation because it expands hints how to write your own service implementation.

    * (N) The generated service implementation could have a better Perl documentation, listing all available methods for inputs and outputs (the methods are already shown in the code, but having them also in the POD would help).

An up-to-date list of the recent changes is in the Changes file included with the distribution.

Acknowledgement

The main perl developers (whom please direct your suggestions and reports to) are Edward Kawas and Mark Wilkinson.

However, there would be no SADISes without CardioSHARE - CardioSHARE is generously supported by the Heart and Stroke Foundation of B.C. and Yukon, the Canadian Institutes of Health Research, and Microsoft Research.

Lots of code was written by Martin Senger for the Perl MoSeS module and used in this module.

EXPORT ^

None by default.

SEE ALSO ^

For information on SADI, please see

http://sadiframework.org/

Please visit the SADI website at http://sadiframework.org!

AUTHORS ^

Edward Kawas

COPYRIGHT ^

Copyright (c) 2009, Mark Wilkinson All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, 
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
    * Neither the name of the University of British Columbia nor the names of 
      its contributors may be used to endorse or promote products derived from 
      this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

syntax highlighting: