File Coverage

blib/lib/FedoraCommons/APIA.pm
Criterion Covered Total %
statement 18 211 8.5
branch 0 92 0.0
condition n/a
subroutine 6 21 28.5
pod n/a
total 24 324 7.4


line stmt bran cond sub pod time code
1             # ========================================================================= #
2             #
3             # FedoraCommons::APIA - interface to Fedora's SOAP based API-A
4             #
5             # ========================================================================= #
6             #
7             # Copyright (c) 2011, Cornell University www.cornell.edu (enhancements)
8             # Copyright (c) 2010, Cornell University www.cornell.edu (enhancements)
9             # Copyright (c) 2007, The Pennsylvania State University, www.psu.edu
10             #
11             # This library is free software; you can redistribute it and/or modify
12             # it under the terms of the GNU General Public License as published by
13             # the Free Software Foundation; either version 2, or (at your option)
14             # any later version.
15             #
16             # See pod documentation for further license and copyright information.
17             #
18             # History: APIA interface was developed at PSU in 2006/2007. This code
19             # was built on top of an existing module which used SOAP (Technical
20             # Knowledge Center of Denmark[2006]).
21             #
22             # Cornell University began using the module for scripts
23             # to import legacy digital collections into a new cloud-based
24             # Archival Repository (2009). During this work the module has been
25             # enhanced with several new methods implemented.
26             #
27             # The module made interacting with a Fedora Commons repository
28             # easy thus the decision to share the module on CPAN.
29             #
30             #
31             # Manifest of APIA API methods and APIA Module methods:
32             # POD
33             # Fedora 3.0 APIA API Methods Supported Status Documented
34             # --------------------------- --------- ------ ----------
35             #
36             # * Repository Access
37             # o describeRepository No
38             # * Object Access
39             # o findObjects Supported OK Yes
40             # o resumeFindObjects Supported OK Yes
41             # o getObjectHistory No
42             # o getObjectProfile No
43             # * Datastream Access
44             # o getDatastreamDissemination Supported OK Yes
45             # o listDatastreams Supported OK Yes (PSU)
46             # * Dissemination Access
47             # o getDissemination No
48             # o listMethods No
49             #
50             # Local Additions:
51             #
52             # * Datastream Access
53             # o datastreamExists Supported OK Yes
54             #
55             #
56             # *PSU - Implemented by Penn State University
57             #
58             # ========================================================================= #
59             #
60             # $Id: APIA.pm,v 1.3 2007/06/25 15:22:25 dlf2 Exp $
61             #
62             # ========================================================================= #
63             #
64              
65             package FedoraCommons::APIA;
66              
67 1     1   30564 use 5.008005;
  1         4  
  1         44  
68 1     1   6 use strict;
  1         2  
  1         48  
69 1     1   6 use warnings;
  1         7  
  1         76  
70              
71             require SOAP::Lite;
72 1     1   1324 use Time::HiRes qw(time);
  1         2477  
  1         6  
73 1     1   208 use Carp;
  1         1  
  1         103  
74              
75             require Exporter;
76 1     1   5710 use AutoLoader qw(AUTOLOAD);
  1         6512  
  1         8  
77              
78             our @ISA = qw(Exporter);
79              
80             # Items to export into callers namespace by default. Note: do not export
81             # names by default without a very good reason. Use EXPORT_OK instead.
82             # Do not simply export all your public functions/methods/constants.
83              
84             # This allows declaration use FedoraCommons::APIA ':all';
85             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
86             # will save memory.
87             our %EXPORT_TAGS = ( 'all' => [ qw(
88              
89             ) ] );
90              
91             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
92              
93             our @EXPORT = qw(
94              
95             );
96              
97             our $VERSION = '0.5';
98              
99             our $FEDORA_VERSION = "3.2";
100              
101             sub import {
102 0     0     my $pkg = shift;
103 0           while (@_) {
104 0           my $command = shift;
105 0           my $parameter = shift;
106 0 0         if ($command eq 'version') {
107              
108             # Add legal Fedora version numbers as they become available
109 0 0         if ($parameter eq "3.2") {
110 0           $FEDORA_VERSION = $parameter;
111             }
112              
113             }
114             }
115             }
116              
117             my $ERROR_MESSAGE;
118              
119             # Preloaded methods go here.
120              
121             # Autoload methods go after =cut, and are processed by the autosplit program.
122              
123              
124              
125             # ========================================================================= #
126             #
127             # Public Methods
128             #
129             # ========================================================================= #
130              
131              
132              
133             # Constructor
134             #
135             # Args in parameter hash:
136             # host : Fedora host
137             # port : Port
138             # usr : Fedora Admin user
139             # pwd : Fedora Admin password
140             # timeout: Allowed timeout
141             #
142             # Return:
143             # The Fedora::APIA object
144             #
145             sub new {
146 0     0     my $class = shift;
147 0           my %args = @_;
148 0           my $self = {};
149 0           $self->{'protocol'} = "http";
150              
151 0           foreach my $k (keys %args) {
152 0 0         if ($k eq 'usr') {
    0          
    0          
    0          
    0          
    0          
153 0           $self->{$k} = $args{$k};
154             } elsif ( $k eq 'pwd') {
155 0           $self->{$k} = $args{$k};
156             } elsif ( $k eq 'host') {
157 0           $self->{$k} = $args{$k};
158             } elsif ( $k eq 'port') {
159 0           $self->{$k} = $args{$k};
160             } elsif ( $k eq 'timeout') {
161 0           $self->{$k} = $args{$k};
162             } elsif ( $k eq 'protocol') {
163 0           $self->{$k} = $args{$k};
164             }
165             }
166              
167             # Check mandatory parameters
168 0 0         Carp::croak "Initialisation parameter 'host' missing" unless defined($self->{'host'});
169 0 0         Carp::croak "Initialisation parameter 'port' missing" unless defined($self->{'port'});
170 0 0         Carp::croak "Initialisation parameter 'usr' missing" unless defined($self->{'usr'});
171 0 0         Carp::croak "Initialisation parameter 'pwd' missing" unless defined($self->{'pwd'});
172              
173             # Bless object
174 0           bless $self;
175            
176             # Initialise SOAP class
177 0           my $apia=SOAP::Lite
178             -> uri("http://www.fedora.info/definitions/api/")
179             -> proxy($self->_get_proxy());
180 0 0         if (defined($self->{timeout})) {
181 0           $apia->proxy->timeout($self->{timeout});
182             }
183 0           $self->{apia} = $apia;
184              
185 0           return $self;
186             }
187              
188             # Error from most recent operation
189             sub error {
190 0     0     my $self = shift;
191 0           return $self->{ERROR_MESSAGE};
192             }
193              
194             # Elapsed time of most recent operation
195             sub get_time {
196 0     0     my $self = shift;
197 0           return $self->{TIME};
198             }
199              
200             # Statistics
201             sub get_stat {
202 0     0     my $self = shift;
203 0           return $self->{STAT};
204             }
205              
206             sub get_proxy {
207 0     0     my $self = shift;
208 0           return $self->_get_proxy();
209             }
210              
211             # Start statistic gathering over
212             sub start_stat {
213 0     0     my $self = shift;
214 0           $self->{STAT} = {};
215 0           return;
216             }
217              
218             # findObjects
219             #
220             # Args in parameter hash:
221             # maxResults: Max number of results returned
222             # fldsrchProperty: The field (aka property) being searched
223             # fldsrchValue: Operator for comparing a property to a value
224             # fldsrchOperator: The value of the field being searched
225             # searchRes_ref: Reference to scalar to hold search results
226             #
227             # Return:
228             #
229             # 0 = success
230             # 1 = Error
231             # 2 = Error on remote server
232             #
233             sub findObjects{
234 0     0     my $self = shift;
235 0           my %args = @_;
236              
237 0           $self->{ERROR_MESSAGE}=undef;
238 0           $self->{TIME}=undef;
239              
240 0 0         Carp::croak "Parameter 'maxResults' missing" unless defined($args{maxResults});
241 0 0         Carp::croak "Parameter 'fldsrchProperty' missing" unless defined($args{fldsrchProperty});
242 0 0         Carp::croak "Parameter 'fldsrchOperator' missing" unless defined($args{fldsrchOperator});
243 0 0         Carp::croak "Parameter 'fldsrchValue' missing" unless defined($args{fldsrchValue});
244 0 0         Carp::croak "Parameter 'searchRes_ref' missing" unless defined($args{searchRes_ref})
245             ;
246              
247             # List Datastreams
248 0           my $findobjs_result;
249 0           eval {
250 0           my $start=time;
251 0           $findobjs_result = $self->{apia}->findObjects(
252             SOAP::Data->name('resultFields'=> \SOAP::Data->value('pid')),
253             SOAP::Data->name('maxResults')->value($args{maxResults})->type('xsd:nonNegativeInteger'),
254             SOAP::Data->name('query'=> \SOAP::Data->value(
255             SOAP::Data->name("conditions" => \SOAP::Data->value(
256             SOAP::Data->name("condition" => \SOAP::Data->value(
257             SOAP::Data->name("property" => $args{fldsrchProperty}),
258             SOAP::Data->name("operator" => $args{fldsrchOperator})->type('xsd:enumeration'),
259             SOAP::Data->name("value" => $args{fldsrchValue})))))))
260             );
261 0           my $elapse_time = time - $start;
262 0           $self->{TIME} = $elapse_time;
263 0           $self->{STAT}->{'findObjects'}{count}++;
264 0           $self->{STAT}->{'findObjects'}{time} += $elapse_time;
265             };
266 0 0         if ($@) {
267 0           $self->{ERROR_MESSAGE}=$self->_handle_exceptions($@);
268 0           return 1;
269             }
270              
271             # Handle error from Fedora target
272 0 0         if ($findobjs_result->fault) {
273 0           $self->{ERROR_MESSAGE}=
274             $findobjs_result->faultcode."; ".
275             $findobjs_result->faultstring."; ".
276             $findobjs_result->faultdetail;
277 0           return 2;
278             }
279              
280             # Handle success
281 0           ${$args{searchRes_ref}} = $findobjs_result->result();
  0            
282 0           return 0;
283              
284             }
285              
286              
287             # resumeFindObjects
288             #
289             # Args in parameter hash:
290             # sessionToken:
291             # searchRes_ref: Reference to scalar to hold search results
292             #
293             # Return:
294             #
295             # 0 = success
296             # 1 = Error
297             # 2 = Error on remote server
298             #
299             sub resumeFindObjects{
300 0     0     my $self = shift;
301 0           my %args = @_;
302              
303 0           $self->{ERROR_MESSAGE}=undef;
304 0           $self->{TIME}=undef;
305              
306 0 0         Carp::croak "Parameter 'sessionToken' missing" unless defined($args{sessionToken});
307 0 0         Carp::croak "Parameter 'searchRes_ref' missing" unless defined($args{searchRes_ref})
308             ;
309              
310             # List Datastreams
311 0           my $resumefindobjs_result;
312 0           eval {
313 0           my $start=time;
314 0           $resumefindobjs_result = $self->{apia}->resumeFindObjects($args{sessionToken});
315 0           my $elapse_time = time - $start;
316 0           $self->{TIME} = $elapse_time;
317 0           $self->{STAT}->{'resumeFindObjects'}{count}++;
318 0           $self->{STAT}->{'resumeFindObjects'}{time} += $elapse_time;
319             };
320 0 0         if ($@) {
321 0           $self->{ERROR_MESSAGE}=$self->_handle_exceptions($@);
322 0           return 1;
323             }
324              
325             # Handle error from Fedora target
326 0 0         if ($resumefindobjs_result->fault) {
327 0           $self->{ERROR_MESSAGE}=
328             $resumefindobjs_result->faultcode."; ".
329             $resumefindobjs_result->faultstring."; ".
330             $resumefindobjs_result->faultdetail;
331 0           return 2;
332             }
333              
334             # Handle success
335 0           ${$args{searchRes_ref}} = $resumefindobjs_result->result();
  0            
336 0           return 0;
337              
338             }
339              
340              
341             # getDatastreamDissemination
342             #
343             # Args in parameter hash:
344             # pid: Record PID in fedora
345             # dsID: Datastream PID
346             # asOfDateTime
347             # stream_ref Reference to scalar to hold resulting stream
348             #
349             # Return:
350             #
351             # 0 = success
352             # 1 = Error
353             # 2 = Error on remote server
354             #
355             sub getDatastreamDissemination {
356 0     0     my $self = shift;
357 0           my %args = @_;
358              
359 0           $self->{ERROR_MESSAGE}=undef;
360 0           $self->{TIME}=undef;
361              
362 0 0         Carp::croak "Parameter 'pid' missing" unless defined($args{pid});
363 0 0         Carp::croak "Parameter 'dsID' missing" unless defined($args{dsID});
364 0 0         Carp::croak "Parameter 'stream_ref' missing" unless defined($args{stream_ref})
365             ;
366              
367             # Set Defaults
368 0 0         if (!defined($args{asOfDateTime})) {
369 0           $args{asOfDateTime} = "undef";
370             }
371              
372             # Get Datastream Dissemination
373 0           my $gdd_result;
374 0           eval {
375 0           my $start=time;
376 0           $gdd_result = $self->{apia}->getDatastreamDissemination(
377             $args{pid},
378             $args{dsID},
379             # $args{asOfDateTime},
380             undef,
381             );
382 0           my $elapse_time = time - $start;
383 0           $self->{TIME} = $elapse_time;
384 0           $self->{STAT}->{'getDatastreamDissemination'}{count}++;
385 0           $self->{STAT}->{'getDatastreamDissemination'}{time} += $elapse_time;
386             };
387 0 0         if ($@) {
388 0           $self->{ERROR_MESSAGE}=$self->_handle_exceptions($@);
389 0           return 1;
390             }
391              
392             # Handle error from Fedora target
393 0 0         if ($gdd_result->fault) {
394 0           $self->{ERROR_MESSAGE}=
395             $gdd_result->faultcode."; ".
396             $gdd_result->faultstring."; ".
397             $gdd_result->faultdetail;
398 0           return 2;
399             }
400              
401             # Handle success
402             # Decode Stream
403 0           ${$args{stream_ref}} = MIME::Base64::decode_base64($gdd_result->result()->{'stream'});
  0            
404            
405 0           return 0;
406              
407             }
408              
409              
410             # listDatastreams
411             #
412             # Args in parameter hash:
413             # pid: Record PID in fedora
414             # asOfDateTime:
415             # datastream_ref: Reference to scalar to hold result
416             # list: Reference to list. When provides will create list of
417             # datastream ids.
418             #
419             # Return:
420             #
421             # 0 = success
422             # 1 = Error
423             # 2 = Error on remote server
424             #
425             sub listDatastreams{
426 0     0     my $self = shift;
427 0           my %args = @_;
428              
429 0           $self->{ERROR_MESSAGE}=undef;
430 0           $self->{TIME}=undef;
431              
432 0 0         Carp::croak "Parameter 'pid' missing" unless defined($args{pid});
433 0 0         Carp::croak "Parameter 'datastream_ref' missing" unless defined($args{datastream_ref});
434              
435             # Set Defaults
436 0 0         if (!defined($args{asOfDateTime})) {
437 0           $args{asOfDateTime} = "undef";
438             }
439              
440             # List Datastreams
441 0           my $lds_result;
442 0           eval {
443 0           my $start=time;
444 0           $lds_result = $self->{apia}->listDatastreams(
445             $args{pid},
446             # $args{asOfDateTime},
447             undef
448             );
449 0           my $elapse_time = time - $start;
450 0           $self->{TIME} = $elapse_time;
451 0           $self->{STAT}->{'listDatastreams'}{count}++;
452 0           $self->{STAT}->{'listDatastreams'}{time} += $elapse_time;
453             };
454 0 0         if ($@) {
455 0           $self->{ERROR_MESSAGE}=$self->_handle_exceptions($@);
456 0           return 1;
457             }
458              
459             # Handle error from Fedora target
460 0 0         if ($lds_result->fault) {
461 0           $self->{ERROR_MESSAGE}=
462             $lds_result->faultcode."; ".
463             $lds_result->faultstring."; ".
464             $lds_result->faultdetail;
465 0           return 2;
466             }
467              
468             # Does the user want a list of datastream identifiers.
469 0 0         if ($args{list}) {
470              
471 0           my $datastreams;
472 0           $datastreams = $lds_result;
473              
474 0           foreach my $ds ($datastreams->valueof('//datastreamDef')) {
475 0           push(@{$args{list}},$ds->{ID});
  0            
476             }
477             }
478              
479             # Handle success
480 0           ${$args{datastream_ref}} = $lds_result;
  0            
481 0           return 0;
482              
483             }
484              
485             # datastreamExists (Boolean)
486             #
487             # Args in parameter hash:
488             # pid: Record PID in fedora
489             # dsID: Datastream identifier. Will check
490             # existence of datastream.
491             #
492             # Return:
493             #
494             # 0 = False, datastream does not exist
495             # 1 = True, datastream exists
496             #
497             sub datastreamExists {
498 0     0     my $self = shift;
499 0           my %args = @_;
500              
501 0 0         Carp::croak "Parameter 'pid' missing" unless defined($args{pid});
502 0 0         Carp::croak "Parameter 'dsID' missing" unless defined($args{dsID});
503              
504              
505              
506 0           my $pid = $args{'pid'};
507 0           my $dsID = $args{'dsID'};
508 0           my $datastreams;
509              
510 0 0         if ($self->listDatastreams(pid=>$pid,
511             datastream_ref =>\$datastreams) == 0) {
512 0           my @dslist = ();
513              
514 0           foreach my $ds ($datastreams->valueof('//datastreamDef')) {
515 0           push(@dslist,$ds->{ID});
516             }
517 0 0         if (grep $dsID eq $_, @dslist) {
518 0           return 1;
519             } else {
520 0           return 0;
521             }
522             }
523 0           return 0;
524             }
525              
526              
527              
528             # getObjectProfile
529             #
530             # Args in parameter hash:
531             # pid: PID of the object
532             # asOfDateTime:
533             # profile_ref: reference to return metadata profile
534             #
535             # Return:
536             #
537             # 0 = success
538             # 1 = Error
539             # 2 = Error on remote server
540             #
541             sub getObjectProfile {
542 0     0     my $self = shift;
543 0           my %args = @_;
544              
545 0           $self->{ERROR_MESSAGE}=undef;
546 0           $self->{TIME}=undef;
547              
548 0 0         Carp::croak "Parameter 'pid' missing" unless defined($args{pid});
549 0 0         Carp::croak "Parameter 'profile_ref' missing" unless defined($args{profile_ref});
550              
551             # Set Defaults
552 0 0         if (!defined($args{asOfDateTime})) {
553 0           $args{asOfDateTime} = "undef";
554             }
555 0           my $gds_result;
556 0           eval {
557 0           my $start=time;
558 0           $gds_result = $self->{apia}->getObjectProfile (
559             $args{pid},
560             );
561 0           my $elapse_time = time - $start;
562 0           $self->{TIME} = $elapse_time;
563 0           $self->{STAT}->{'getDatastream'}{count}++;
564 0           $self->{STAT}->{'getDatastream'}{time} += $elapse_time;
565             };
566 0 0         if ($@) {
567 0           $self->{ERROR_MESSAGE}=$self->_handle_exceptions($@);
568 0           return 1;
569             }
570              
571             # Handle error from Fedora target
572 0 0         if ($gds_result->fault) {
573 0           $self->{ERROR_MESSAGE}=
574             $gds_result->faultcode."; ".
575             $gds_result->faultstring."; ".
576             $gds_result->faultdetail;
577 0           return 2;
578             }
579              
580 0           my $data = $gds_result->result();
581              
582             # Handle success
583 0           ${$args{profile_ref}} = $gds_result->result();
  0            
584 0           return 0;
585              
586             } # getObjectProfile
587              
588             ###
589              
590             # ========================================================================= #
591             #
592             # Private Methods
593             #
594             # ========================================================================= #
595              
596              
597              
598             # Map die exceptions from SOAP::Lite calls to Fedora::APIA error messages
599             sub _handle_exceptions {
600 0     0     my ($self, $exception_text) = @_;
601 0 0         if ($exception_text =~ m{^401 Unauthorized}) { return "401 Unauthorized"; }
  0            
602 0           return $exception_text;
603             }
604              
605             # Method for constructing proxy URL
606             sub _get_proxy {
607 0     0     my ($self) = @_;
608 0           return "$self->{'protocol'}://".
609             $self->{usr}.":".$self->{pwd}.
610             "\@".$self->{host}.":".$self->{port}.
611             "/fedora/services/access";
612             }
613              
614             1;
615             __END__