1package Net::OAI::ListRecords; 2 3use strict; 4use warnings; 5use base qw( XML::SAX::Base Net::OAI::Base ); 6use Carp qw( croak ); 7use Net::OAI::Record; 8use Net::OAI::Record::Header; 9use File::Temp qw( tempfile ); 10use Storable qw( store_fd fd_retrieve ); 11use IO::File; 12 13=head1 NAME 14 15Net::OAI::ListRecords - Results of the ListRecords OAI-PMH verb. 16 17=head1 SYNOPSIS 18 19=head1 DESCRIPTION 20 21=head1 METHODS 22 23Like all responses to OAI verbs, ListRecords is based on L<Net::OAI::Base> 24and inherits its methods. 25 26 27=head2 new() 28 29You probably don't want to be using this method yourself, since 30Net::OAI::Harvester::listRecords() calls it for you. 31 32=cut 33 34sub new { 35 my ( $class, %opts ) = @_; 36 37 my $package; 38 if ( $package = $opts{ recordHandler } ) { 39 $opts{ metadataHandler } and croak( "you may pass either a recordHandler or a metadataHandler to getRecord()" ); 40 delete $opts { metadataHandler }; 41 } elsif ( $package = $opts{ metadataHandler } ) { 42 delete $opts{ recordHandler }; 43 } else { 44 delete $opts{ recordHandler }; 45 $package = $opts{ metadataHandler } = 'Net::OAI::Record::OAI_DC'; 46 } 47 Net::OAI::Harvester::_verifyHandler( $package ); 48 49 my $self = bless \%opts, ref( $class ) || $class; 50 my ( $fh, $tempfile ) = tempfile(UNLINK => 1); 51 binmode( $fh, ':utf8' ); 52 $self->{ recordsFileHandle } = $fh; 53 $self->{ recordsFilename } = $tempfile; 54 55 ## so we can store code refs 56 $Storable::Deparse = 1; 57 $Storable::Eval = 1; 58 59 $self->{ _prefixmap } = {}; 60 return( $self ); 61} 62 63=head2 next() 64 65Returns the L<Net::OAI::Record> object for the next OAI record in the 66response, C<undef> if none remain. resumptionToken handling is performed 67automagically if the original request was listAllIdentifiers(). 68 69=cut 70 71sub next { 72 my $self = shift; 73 74 ## if we haven't opened our object store do it now 75 if ( ! $self->{ recordsFileHandle } ) { 76 $self->{ recordsFileHandle } = IO::File->new( $self->{ recordsFilename } ) 77 or croak "unable to open temp file: ".$self->{ recordsFilename }; 78 ## we assume utf8 encoding (perhaps wrongly) 79 binmode( $self->{ recordsFileHandle }, ':utf8' ); 80 } 81 82 ## no more data to read back from our object store then return undef 83 if ( $self->{ recordsFileHandle }->eof() ) { 84 $self->{ recordsFileHandle }->close() or croak "Could not close() ".$self->{ recordsFilename } 85 .". File system full?"; 86 return( $self->handleResumptionToken( 'listRecords' ) ); 87 } 88 89 ## get an object back from the store, thaw and return it 90 my $record = fd_retrieve( $self->{ recordsFileHandle } ); 91 return( $record ); 92} 93 94=head2 metadataHandler() 95=head2 recordHandler() 96 97Returns the name of the package being used to represent the individual metadata 98records. If unspecified it defaults to L<Net::OAI::Record::OAI_DC> which 99should be ok. 100 101=cut 102 103sub metadataHandler { 104 my $self = shift; 105 return( $self->{ metadataHandler } ); 106} 107 108sub recordHandler { 109 my $self = shift; 110 return( $self->{ recordHandler } ); 111} 112 113## SAX Handlers 114 115sub start_prefix_mapping { 116 my ($self, $mapping) = @_; 117 if ( $self->get_handler() ) { 118 return $self->SUPER::start_prefix_mapping( $mapping )}; 119 $self->{ _prefixmap }->{$mapping->{ Prefix }} = $mapping; 120} 121 122sub end_prefix_mapping { 123 my ($self, $mapping) = @_; 124 if ( $self->get_handler() ) { 125 return $self->SUPER::end_prefix_mapping( $mapping )}; 126 delete $self->{ _prefixmap }->{$mapping->{ Prefix }}; 127} 128 129sub start_element { 130 my ( $self, $element ) = @_; 131 return $self->SUPER::start_element( $element ) unless $element->{NamespaceURI} eq Net::OAI::Harvester::XMLNS_OAI; 132 133 ## if we are at the start of a new record then we need an empty 134 ## metadata object to fill up 135 if ( $element->{ LocalName } eq 'record' ) { 136 ## we store existing downstream handler so we can replace 137 ## it after we are done retrieving the metadata record 138 $self->{ OLD_Handler } = $self->get_handler(); 139 my $header = $self->{ recordHandler } 140 ? Net::OAI::Record::Header->new( 141 Handler => (ref($self->{ recordHandler }) ? $self->{ recordHandler } : $self->{ recordHandler }->new()), 142 fwdAll => 1, 143 ) 144 : Net::OAI::Record::Header->new( 145 Handler => (ref($self->{ metadataHandler }) ? $self->{ metadataHandler } : $self->{ metadataHandler }->new()), 146 ($Net::OAI::Harvester::OLDmetadataHandler ? (fwdAll => 1) : ()), 147 ); 148 $self->set_handler( $header ); 149 foreach my $mapping ( values %{$self->{_prefixmap}} ) { 150 $self->SUPER::start_prefix_mapping($mapping)}; 151 } 152 elsif ( $element->{ LocalName } eq 'ListRecords' ) { 153 } 154 return $self->SUPER::start_element( $element ); 155} 156 157sub end_element { 158 my ( $self, $element ) = @_; 159 160 $self->SUPER::end_element( $element ); 161 return unless $element->{NamespaceURI} eq Net::OAI::Harvester::XMLNS_OAI; 162 163 ## if we've got to the end of the record we need to stash 164 ## away the object in our object store on disk 165 if ( $element->{ LocalName } eq 'record' ) { 166 167 ## we need to swap out the existing metadata handler and freeze 168 ## it on disk 169 my $header = $self->get_handler(); 170 my $data = $header->get_handler(); 171 $header->set_handler( undef ); ## remove reference to $record 172 173 ## set handler to what is was before we started processing 174 ## the record 175 $self->set_handler( $self->{ OLD_Handler } ); 176 my $record; 177 if ( $self->{ recordHandler } ) { 178 $record = Net::OAI::Record->new(header => $header, recorddata => $data) 179 } else { 180 $record = Net::OAI::Record->new(header => $header, metadata => $data) 181 }; 182 183 ## commit the object to disk 184 Net::OAI::Harvester::debug( "committing record to object store" ); 185 store_fd( $record, $self->{ recordsFileHandle } ); 186 } 187 188 ## otherwise if we got to the end of our list we can close 189 ## our object stash on disk 190 elsif ( $element->{ LocalName } eq 'ListRecords' ) { 191 $self->{ recordsFileHandle }->close(); 192 $self->{ recordsFileHandle } = undef; 193 } 194 195} 196 197sub _fatal { 198 print STDERR "fatal: ", shift, "\n"; 199 exit(1); 200} 201 2021; 203 204