blob: 087e89dee6d84c650025e83b71627175b3f1d0f1 [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
use strict;
use warnings;
package LucyX::Remote::SearchClient;
BEGIN { our @ISA = qw( Lucy::Search::Searcher ) }
our $VERSION = '0.004001';
$VERSION = eval $VERSION;
use Carp;
use Storable qw( nfreeze thaw );
# Inside-out member vars.
our %peer_address;
our %sock;
use IO::Socket::INET;
sub new {
my ( $either, %args ) = @_;
my $peer_address = delete $args{peer_address};
my $self = $either->SUPER::new(%args);
$peer_address{$$self} = $peer_address;
# Establish a connection.
my $sock = $sock{$$self} = IO::Socket::INET->new(
PeerAddr => $peer_address,
Proto => 'tcp',
);
confess("No socket: $!") unless $sock;
$sock->autoflush(1);
my %handshake_args = ( _action => 'handshake' );
my $response = $self->_rpc( \%handshake_args );
confess("Failed to connect") unless $response;
return $self;
}
sub DESTROY {
my $self = shift;
$self->close if defined $sock{$$self};
delete $peer_address{$$self};
delete $sock{$$self};
$self->SUPER::DESTROY;
}
=for comment
Make a remote procedure call. For every call that does not close/terminate
the socket connection, expect a response back that's been serialized using
Storable.
=cut
sub _rpc {
my ( $self, $args ) = @_;
my $sock = $sock{$$self};
my $serialized = nfreeze($args);
my $packed_len = pack( 'N', length($serialized) );
print $sock "$packed_len$serialized" or confess $!;
# disabled
#my $check_val = $sock->syswrite("$packed_len$serialized");
#confess $! if $check_val != length($serialized) + 4;
my $check_val;
# Bail out if we're either closing or shutting down the server remotely.
return if $args->{_action} eq 'done';
return if $args->{_action} eq 'terminate';
# Decode response.
$check_val = $sock->read( $packed_len, 4 );
confess("Failed to read 4 bytes: $!")
unless $check_val == 4;
my $arg_len = unpack( 'N', $packed_len );
$check_val = $sock->read( $serialized, $arg_len );
confess("Failed to read $arg_len bytes")
unless $check_val == $arg_len;
my $response = thaw($serialized);
if ( exists $response->{retval} ) {
return $response->{retval};
}
return;
}
sub top_docs {
my $self = shift;
my %args = ( @_, _action => 'top_docs' );
return $self->_rpc( \%args );
}
sub terminate {
my $self = shift;
my %args = ( _action => 'terminate' );
return $self->_rpc( \%args );
}
sub fetch_doc {
my ( $self, $doc_id ) = @_;
my %args = ( doc_id => $doc_id, _action => 'fetch_doc' );
return $self->_rpc( \%args );
}
sub fetch_doc_vec {
my ( $self, $doc_id ) = @_;
my %args = ( doc_id => $doc_id, _action => 'fetch_doc_vec' );
return $self->_rpc( \%args );
}
sub doc_max {
my $self = shift;
my %args = ( _action => 'doc_max' );
return $self->_rpc( { _action => 'doc_max' } );
}
sub doc_freq {
my $self = shift;
my %args = ( @_, _action => 'doc_freq' );
return $self->_rpc( \%args );
}
sub close {
my $self = shift;
$self->_rpc( { _action => 'done' } );
my $sock = $sock{$$self};
close $sock or confess("Error when closing socket: $!");
delete $sock{$$self};
}
1;
__END__
=head1 NAME
LucyX::Remote::SearchClient - Connect to a remote SearchServer.
=head1 SYNOPSIS
my $client = LucyX::Remote::SearchClient->new(
peer_address => 'searchserver1:7890',
);
my $hits = $client->hits( query => $query );
=head1 DESCRIPTION
SearchClient is a subclass of L<Lucy::Search::Searcher> which can be
used to search an index on a remote machine made accessible via
L<SearchServer|LucyX::Remote::SearchServer>.
=head1 METHODS
=head2 new
Constructor. Takes hash-style params.
=over
=item *
B<peer_address> - The name/IP and the port number which the client should
attempt to connect to.
=back
=cut