#----------------------------------------------------------------- # MOSES::MOBY::ServiceException # Author: Edward Kawas , # Martin Senger # For copyright and disclaimer see below. # # $Id: ServiceException.pm,v 1.4 2008/04/29 19:45:01 kawas Exp $ #----------------------------------------------------------------- package MOSES::MOBY::ServiceException; use base qw( MOSES::MOBY::Base Exporter ); use MOSES::MOBY::Tags; use strict; use vars qw( @EXPORT ); # add versioning to this module use vars qw /$VERSION/; $VERSION = sprintf "%d.%02d", q$Revision: 1.4 $ =~ /: (\d+)\.(\d+)/; my %severityNames = (); my %codeNames = (); use constant OK => 700; use constant INFO => 3; use constant ERROR => 1; use constant WARNING => 2; use constant UNKNOWN_NAME => 200; use constant INPUTS_INVALID => 201; use constant INPUT_NOT_ACCEPTED => 202; use constant INPUT_REQUIRED_PARAMETER => 221; use constant INPUT_INCORRECT_PARAMETER => 222; use constant INPUT_INCORRECT_SIMPLE => 223; use constant INPUT_INCORRECT_PARAMETERS => 224; use constant INPUT_INCORRECT_COLLECTION => 225; use constant INPUT_EMPTY_OBJECT => 226; use constant INPUT_INCORRECT_NAMESPACE => 227; use constant NOT_RUNNABLE => 300; use constant NOT_RUNNING => 301; use constant NOT_TERMINATED => 302; use constant NO_METADATA_AVAILABLE => 400; use constant PROTOCOLS_UNACCEPTED => 500; use constant INTERNAL_PROCESSING_ERROR => 600; use constant COMMUNICATION_FAILURE => 601; use constant UNKNOWN_STATE => 602; use constant NOT_IMPLEMENTED => 603; BEGIN { @EXPORT = qw( ERROR WARNING INFO OK UNKNOWN_NAME INPUTS_INVALID INPUT_NOT_ACCEPTED INPUT_REQUIRED_PARAMETER INPUT_INCORRECT_PARAMETER INPUT_INCORRECT_SIMPLE INPUT_INCORRECT_PARAMETERS INPUT_INCORRECT_COLLECTION INPUT_EMPTY_OBJECT INPUT_INCORRECT_NAMESPACE NOT_RUNNABLE NOT_TERMINATED NO_METADATA_AVAILABLE PROTOCOLS_UNACCEPTED INTERNAL_PROCESSING_ERROR COMMUNICATION_FAILURE UNKNOWN_STATE NOT_IMPLEMENTED ); # initialize severity names $severityNames{ ERROR() } = 'error'; $severityNames{ WARNING() } = 'warning'; $severityNames{ INFO() } = 'information'; # initialize code names $codeNames{OK} = 'OK'; $codeNames{UNKNOWN_NAME} = 'UNKNOWN_NAME'; $codeNames{INPUTS_INVALID} = 'INPUTS_INVALID'; $codeNames{INPUT_NOT_ACCEPTED} = 'INPUT_NOT_ACCEPTED'; $codeNames{INPUT_REQUIRED_PARAMETER} = 'INPUT_REQUIRED_PARAMETER'; $codeNames{INPUT_INCORRECT_PARAMETER} = 'INPUT_INCORRECT_PARAMETER'; $codeNames{INPUT_INCORRECT_SIMPLE} = 'INPUT_INCORRECT_SIMPLE'; $codeNames{INPUT_REQUIRED_PARAMETERS} = 'INPUT_REQUIRED_PARAMETERS'; $codeNames{INPUT_INCORRECT_COLLECTION} = 'INPUT_INCORRECT_COLLECTION'; $codeNames{INPUT_EMPTY_OBJECT} = 'INPUT_EMPTY_OBJECT'; $codeNames{INPUT_INCORRECT_NAMESPACE} = 'INPUT_INCORRECT_NAMESPACE'; $codeNames{NOT_RUNNABLE} = 'NOT_RUNNABLE'; $codeNames{NOT_RUNNING} = 'NOT_RUNNING'; $codeNames{NOT_TERMINATED} = 'NOT_TERMINATED'; $codeNames{NO_METADATA_AVAILABLE} = 'NO_METADATA_AVAILABLE'; $codeNames{PROTOCOLS_UNACCEPTED} = 'PROTOCOLS_UNACCEPTED'; $codeNames{INTERNAL_PROCESSING_ERROR} = 'INTERNAL_PROCESSING_ERROR'; $codeNames{COMMUNICATION_FAILURE} = 'COMMUNICATION_FAILURE'; $codeNames{UNKNOWN_STATE} = 'UNKNOWN_STATE'; $codeNames{NOT_IMPLEMENTED} = 'NOT_IMPLEMENTED'; } #----------------------------------------------------------------- # A list of allowed attribute names. See MOSES::MOBY::Base for details. #----------------------------------------------------------------- { my %_allowed = ( severity => { type => MOSES::MOBY::Base->INTEGER }, code => { type => MOSES::MOBY::Base->INTEGER }, message => undef, jobId => undef, dataName => undef, ); sub _accessible { my ( $self, $attr ) = @_; exists $_allowed{$attr} or $self->SUPER::_accessible($attr); } sub _attr_prop { my ( $self, $attr_name, $prop_name ) = @_; my $attr = $_allowed{$attr_name}; return ref($attr) ? $attr->{$prop_name} : $attr if $attr; return $self->SUPER::_attr_prop( $attr_name, $prop_name ); } } #----------------------------------------------------------------- # init #----------------------------------------------------------------- sub init { my ($self) = shift; $self->SUPER::init(); $self->severity (INFO); $self->code (OK); } #----------------------------------------------------------------- # getErrorCodeAsString #----------------------------------------------------------------- sub getErrorCodeAsString { my $self = shift; return $codeNames{ $self->code } || ''; } #----------------------------------------------------------------- # getSeverityAsString #----------------------------------------------------------------- sub getSeverityAsString { my $self = shift; return $severityNames{ $self->severity } || ''; } #----------------------------------------------------------------- # toXML #----------------------------------------------------------------- sub toXML { my $self = shift; $self->increaseXMLCounter; my $root = $self->createXMLElement(MOBYEXCEPTION); $self->setXMLAttribute( $root, REFQUERYID, $self->jobId ); $self->setXMLAttribute( $root, REFELEMENT, $self->dataName ); # error code element if ( $self->code ) { my $elemCode = $self->createXMLElement(EXCEPTIONCODE); $elemCode->appendText( $self->code ); $root->appendChild($elemCode); } # message element if ( $self->message ) { my $elemMessage = $self->createXMLElement(EXCEPTIONMESSAGE); $elemMessage->appendText( $self->message ); $root->appendChild($elemMessage); } return $self->closeXML($root); } #----------------------------------------------------------------- # extractExceptions # # Extract and return all exceptions from the given serviceNotes XML # element. #----------------------------------------------------------------- sub extractExceptions { my ( $self, $element ) = @_; return [] unless $element; my @result = (); foreach my $exElem ( $element->getChildrenByTagName(MOBYEXCEPTION) ) { my $ex = $self->_extractException($exElem); push( @result, $ex ) if $ex; } foreach my $exElem ( $element->getChildrenByTagNameNS( MOBY_XML_NS, MOBYEXCEPTION ) ) { my $ex = $self->_extractException($exElem); push( @result, $ex ) if $ex; } return \@result; } #----------------------------------------------------------------- # _extractException # Create and return a MOSES::MOBY::ServiceException from the given # XML $element (must be a 'mobyException' element) #----------------------------------------------------------------- sub _extractException { my ( $self, $element ) = @_; my $result = new MOSES::MOBY::ServiceException; $result->severity( getXMLAttribute( $element, SEVERITY ) ); # note: should only be one child in a properly formatted exception foreach my $code ( $element->getChildrenByTagName(EXCEPTIONCODE) ) { $result->code( $code->textContent ); } my $message; # note: should only be one child in a properly formatted exception foreach my $msg ( $element->getChildrenByTagName(EXCEPTIONMESSAGE) ) { $result->message( $msg->textContent ); } return $result; } #----------------------------------------------------------------- # error # # Create an instance of ServiceException that represents an error. It # has either a single argument representing an error message, or a # hash ref with recognized keys 'code' and 'msg'. # #----------------------------------------------------------------- sub error { my $self = shift; # make an instance, if called as a class method unless (ref $self) { no strict 'refs'; $self = $self->new; } my ($msg, $code); if ( @_ > 0 ) { if ( ref( $_[0] ) eq 'HASH' ) { $msg = $_[0]->{msg}; $code = $_[0]->{code}; } else { $msg = $_[0]; } } else { $code = INTERNAL_PROCESSING_ERROR; } $self->severity (ERROR); $self->message ($msg || ''); $self->code ($code || INTERNAL_PROCESSING_ERROR); return $self; } #----------------------------------------------------------------- # warning #----------------------------------------------------------------- sub warning { my ($self, $msg) = @_; # make an instance, if called as a class method unless (ref $self) { no strict 'refs'; $self = $self->new; } $self->severity (WARNING); $self->message ($msg || ''); return $self; } #----------------------------------------------------------------- # info #----------------------------------------------------------------- sub info { my ($self, $msg) = @_; # make an instance, if called as a class method unless (ref $self) { no strict 'refs'; $self = $self->new; } $self->severity (INFO); $self->message ($msg || ''); return $self; } 1; __END__ =head1 NAME MOSES::MOBY::ServiceException - =head1 SYNOPSIS use MOSES::MOBY::ServiceException; # initialize %params as you see fit, for example: my %params = ( code => INPUTS_INVALID(), severity => ERROR(), jobId => 'a1', dataName => 'myBasicGFFSequenceFeature', message => 'there was something wrong with GFF' ); my $serviceException = MOSES::MOBY::ServiceException->new(%params); my $articleName = $serviceException->dataName(); $serviceException->dataName($articleName); my $code = $serviceException->code(); $serviceException->code($code); my $formattedText = $serviceException->formatString(2); my $errorString = $serviceException->getErrorCodeAsString(); my $severityString = $serviceException->getSeverityAsString(); my $jobId = $serviceException->jobId(); $serviceException->jobId(id => $jobId); my $message = $serviceException->message(); $serviceException->message($message); my $severityCode = $serviceException->severity(); $serviceException->severity($severityCode); my $string = $serviceException->toString(); my $domElement = $serviceException->toXML(); #instantiate ServiceException objects flexibly: # for an error (set up params as appropriate) %params = (code => OK(), msg => "ERROR ERROR ERROR"); $serviceException = MOSES::MOBY::ServiceException::error(%params); # for info (set up params as appropriate) %params = (msg => "INFO INFO INFO"); $serviceException = MOSES::MOBY::ServiceException::info(%params); # for a warning (set up params as appropriate) %params = (msg => "WARN WARN WARN"); $serviceException = MOSES::MOBY::ServiceException::warning(%params); =cut =head1 DESCRIPTION This module encapsulates a Moby service exception raised by service providers when something wrong has to be reported to a client. These exceptions are carried in the service notes part of a MobyPackage. Also included in this module are constances for known error codes and for exception severity levels. =head1 ACCESSIBLE ATTRIBUTES Details are in L. Here just a list of them: =over =item B An integer representing a severity code. =item B An integer representing an error code. =item B A texual message explaining this exception. =item B An identifier of a job that caused this exception. =item B An article name containing the offending data input. =back =head1 SUBROUTINES =head2 info Create an instance of ServiceException that represents an C. An argument is a message text. =head2 warning Create an instance of ServiceException that represents an C. An argument is a message text. =head2 error Create an instance of ServiceException that represents an error. It has either a single argument representing an error message, or a hash with recognized keys C and C. exception>. An argument is a message text. Function : Create an instance of ServiceException that represents an error. Returns : An instance of ServiceException Args : An optional Hash argument with any or all of the following: code: an error code =head2 extractExceptions Extract and return all exceptions from the given serviceNotes XML element. This is a convenient method that can be used when dealing with an XML response from a service without parsing the whole response to a Moby::Package. An argument is an C containing the serviceNotes (a place where are stored exceptions). Returned value is a reference to an array of Cs. Return a reference to an empty array if there are no exceptions. =head2 getSeverityAsString Return the current severity level as text. Or an empty string if the severity code is unknown. =head2 getErrorCodeAsString Return a stringified form of the error code associated with this exception. Or an empty string if the error code is unknown. =head1 CONSTANT SUBROUTINES The following subroutines represents all error codes and similar constants, as defined by BioMoby API. =head2 ERROR Returns A severity code that corresponds to a fatal error No arguments =cut =head2 WARNING Returns a severity code that corresponds to an informative diagnostic message No arguments =cut =head2 INFO Returns a severity code that corresponds to a message not related to any error No arguments =cut =head2 OK Returns an error code: No error. Used together with severity code INFO indicating that actually no error occured and the service was executed normally. No arguments =cut =head2 UNKNOWN_NAME Returns an error code: Setting input data under a non-existing name, or asking for a result using an unknown name No arguments =cut =head2 INPUTS_INVALID Returns an error code: Input data are invalid; they do not match with their definitions, or with their dependency conditions No arguments =cut =head2 INPUT_NOT_ACCEPTED Returns an error code: Used when a client tries to send input data to a job created in a previous call but the server does not any more accept input data. No arguments =cut =head2 INPUT_REQUIRED_PARAMETER Returns an error code: Service requires a parameter but none was given No arguments =cut =head2 INPUT_INCORRECT_PARAMETER Returns an error code: Given parameter is incorrect No arguments =cut =head2 INPUT_INCORRECT_SIMPLE Returns an error code: Given input of type Simple is incorrect No arguments =cut =head2 INPUT_INCORRECT_PARAMETERS Returns an error code: Service requires two or more data inputs. No arguments =cut =head2 INPUT_INCORRECT_COLLECTION Returns an error code: Given input of type Collection is incorrect. No arguments =cut =head2 INPUT_EMPTY_OBJECT Returns an error code: Given an empty input data. No arguments =cut =head2 INPUT_INCORRECT_NAMESPACE Returns an error code: Incorrect Namespace in the input object. No arguments =cut =head2 NOT_RUNNABLE Returns an error code: The same job (analysis) has already been executed, or the data that had been set previously do not exist or are not accessible anymore. No arguments =cut =head2 NOT_RUNNING Returns an error code: A job (analysis) has not yet been started. Note that this exception is not raised when the job has been already finished. No arguments =cut =head2 NOT_TERMINATED Returns an error code: For some reasons, a job (analysis) is not interruptible, but an attempt to do so was done. No arguments =cut =head2 NO_METADATA_AVAILABLE Returns an error code: There are no metadata available for the executed service/analysis. No arguments =cut =head2 PROTOCOLS_UNACCEPTED Returns an error code: Used when a service does not agree on using any of the proposed notification protocols No arguments =cut =head2 INTERNAL_PROCESSING_ERROR Returns an error code: A placeholder for all other errors not defined explicitly in the Biomoby API. No arguments =cut =head2 COMMUNICATION_FAILURE Returns an error code: A generic network failure. No arguments =cut =head2 UNKNOWN_STATE Returns an error code: Used when a service call expects to find an existing state but failed. No arguments =cut =head2 NOT_IMPLEMENTED Returns an error code: A requested method is not implemented. No arguments =head1 AUTHORS Edward Kawas (edward.kawas [at] gmail [dot] com) Martin Senger (martin.senger [at] gmail [dot] com) =cut