=head1 NAME ResourcePool::Command::Execute - The implementation of the Command design pattern with L. =head1 SYNOPSIS package Command; use ResourcePool::Command; use vars qw(@ISA); push @ISA, qw(ResourcePool::Command); sub new($) { my $proto = shift; my $class = ref($proto) || $proto; my $self = {}; bless($self, $class); return $self; } sub execute($$) { my ($self, $resource) = @_; return "The quick brown fox jumps over the lazy dog"; } package main; my $pool; # imagine this is a ResourcePool # or LoadBalancer instance my $cmd = Command->new(); print $pool->execute($cmd); # prints "The quick brown fox jumps over the lazy dog" =head1 DESCRIPTION The ResourcePool::Command class builds a base for commands like described in the GoF book "Design Patterns". This makes it possible to use L in a very different way, without manually calling the L, L or L methods. This common structure is encapsulated into this class. The functionality can be accessed by using the L methods of L and L like shown above. =head2 S<$pool-Eexecute($command)> Executes the command with a resource from the supplied pool. The execute method will obtain a resource from the pool using it's L method, will then call the L passing the resource as argument. On success the resource will be handed back to the pool using it's L method and the return value of the command's L method will be passed to the caller. On failure the resource will be marked as broken (using the L method) and the procedure will be tried until the L value of the pool has been reached. In that case the error from the last attempt to execute the operation will be reported to the caller. The error conditions are explained in the L in this document. =over 4 =item $command An object derived from L. =back B =head1 ERROR CONDITIONS There is only one way how a command can propagate an error execution environment in order to trigger it's retry mechanism. This is to die() in some way. For example you can just call "die" directly, or use on of the Exception modules which will then die. Each time the L is terminated by using die, the L will catch this and try again until the L has been reached. If the MaxExecTry value was reached, the last error will be propagated to the caller by using die. Therefore you still have to enclose the call to execute() into a eval block or some other exception catching block. There is only one exception to this, this are the so called L<|ResourcePool::Command::NoFailoverException>. This are Exceptions which are functional failures which should not cause a fail over but do still indicate a failure. An example for this is an insert into a database table with a primary key that already exists. In this case it makes no sense to try the operation again, but it does still indicate an error condition which must be propagated to the caller. A command can throw an L<|ResourcePool::Command::NoFailoverException> by calling die() with a L<|ResourcePool::Command::NoFailoverException> or an object derived from it, as argument. The most convenient way is to throw directly a L for this reason it is possible to carry another exception (the root cause, the constraint violation in our example) with the L. The following example illustrates this: # snipplet from the Command sub execute($$) { my ($self, $resource) = @_; eval { # something } if (failover_required) { die $@; } else { die ResourcePool::Command::NoFailoverException($@); } } # snipplet from the caller eval { $pool->execute($cmd); } if ($@->isa('ResourcePool::Command::NoFailoverException')) { print "Logical exception: " . $@->exception() . "\n"; } else { print "Technical exception: " . $@ . "(failover done, but without success)\n"; } =head1 AUTHOR Copyright (C) 2001-2005 by Markus Winand This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.