## @file
# (Enter your file info here)
#
# $Id: Client.pm 462 2008-05-09 15:34:28Z damjan $

## @class RWDE::Gearman::Client
# (Enter RWDE::Gearman::Client info here)
package RWDE::Gearman::Client;

use strict;

use Error qw(:try);
use Gearman::Client;
use Storable qw( thaw nfreeze );

use RWDE::Configuration;
use RWDE::Exceptions;

use vars qw($VERSION);
$VERSION = sprintf "%d", q$Revision: 560 $ =~ /(\d+)/;

## @cmethod object new()
# Construct a new Gearman clients
# @return initialized object
sub new {
  my ($proto, $params) = @_;

  my $class = ref($proto) || $proto;

  my $gear = Gearman::Client->new();

  my $self = { gear => $gear };

  bless $self, $class;

  $gear->job_servers(@{ RWDE::Configuration->GearHost });

  $self->initialize();

  return $self;
}

## @method object get_gear()
# (Enter get_gear info here)
# @return (Enter explanation for return value----here)
sub get_gear {
  my ($self, $params) = @_;

  throw RWDE::DevelException({ info => 'No worker present' })
    unless defined $self->{gear};

  return $self->{gear};
}

## @method void initialize()
# Use this method to define initial behavior of the class. It should be overriden in subclasses
sub initialize {
  my ($self, $params) = @_;

  return;
}

## @cmethod object Do_task()
# Call the method do_task on the instance
# @return The value/reference produced by the RPC
sub Do_task {
  my ($self, $params) = @_;

  my $client = $self->new;

  return $client->do_task($params);
}

## @method object do_task($method)
# (Enter do_task info here)
# @param method  (Enter explanation for param here)
# @return The value/reference produced by the RPC
sub do_task {
  my ($self, $params) = @_;

  my $client = $self->get_gear();

  my $result_ref;

  try {
    local $SIG{ALRM} = sub { $self->alarm() };

    #waiting for longer than 5 seconds raises an exception
    alarm 5;

    #issue job
    $result_ref = $client->do_task($$params{method}, nfreeze($params));
    alarm 0;

    throw RWDE::DevelException({ info => 'Undefined error trying to execute ' })
      unless defined $result_ref;
  }

  catch Error with {
    my $ex = shift;

    if ($ex =~ m/Can't call method "syswrite" on an undefined value/) {
      throw RWDE::DevelException({ info => "Couldn't connect to Gearman server/worker pair\n" });
    }
    else {
      $ex->throw();
    }
  };

  my $result = thaw $$result_ref;
  $result = $$result;

  #temporarily support multi-element returns as the default case
  #special case the single element returns
  if ((ref $result eq 'ARRAY') && scalar @{$result} == 1) {
    $result = @{$result}[0];
  }

  #check for error
  if ((ref $result) =~ m/Exception/i || (ref $result) =~ m/Error::Simple/i) {

    #result is an exception :/
    $result->throw();
  }

  return $result;
}

## @method void alarm()
# Handle the timeout fromt the server by throwing an exception (devel)
sub alarm {
  my ($self, $params) = @_;

  throw RWDE::DevelException({ info => 'Haven\'t received response within allotted time' });

  return ();
}

1;