It was a sort of steep learning curve, but I am now partial to the XML::Compile* libraries. Especially since I was interacting with a .NET SOAP service that had WSDL files available.
Update:
I've also been figuring out the best way to throw exceptions from this library. XML::Compile internally catches and logs errors, but wasn't throwing errors. The framework I've settled on is: sub wsdl {
my $self = shift;
return $self->{WSDL} if $self->{WSDL};
$self->{WSDL} = XML::Compile::WSDL11->new($wsdl_file);
return $self->{WSDL};
}
sub cc {
my ( $self, $op, $full_response ) = @_;
return $self->{$op} if $self->{$op};
my $f = $self->wsdl()->compileClient(
operation => $op,
port => $port,
@dbug,
);
$self->{$op} = sub {
my $r = eval { $f->( @_ ) };
unless ($r) {
my $err = $@;
confess "Error calling SOAP method $op: $err";
}
my $fault = $r->{Fault};
confess "$fault->{faultcode}: $fault->{faultstring}" if $fault
+;
return $full_response ? $r : $r->{parameters};
};
}
Then to call a SOAP method, it's:$self->cc($method_name)->(%args);
Advantages? One place (per service) to maintain all SOAP calls. One place to insert certain auth parameters or put certain behavior. E.g., in one service I need to insert wsa and wsse headers. In another, I can not make two simultaneous SOAP calls (with the same account), so I wrap the call with lock file code, and in the same service I get a certain sort of common error so I wrap the whole SOAP call with some retry logic.
And I need the $full_response parameter in one service because I have to use data in the header of the response from the first call (basically a 'Login' sort of call) in all subsequent calls. I don't need the header after that so I just return the 'parameters' part of the response.
Update: I've replaced: my $r = eval { $f->( @_ ) };
unless ($r) {
my $err = $@;
confess "Error calling SOAP method $op: $err";
}
with: my $r = eval {
my $tmp = $f->( @_ );
$eval_err = $@;
$tmp;
};
my $err = $@;
close $fh;
unless ($r) {
no warnings 'uninitialized';
# Retry these errors
if ( $eval_err =~ /answer is not xml/ ) {
if ( $retry-- > 0 ) {
Print("[$$] Recieved text/html response. Will retr
+y $op request.");
sleep 2;
next;
}
}
confess "Error calling SOAP method $op: [$err][$eval_e
+rr]";
}
There are non-fatal errors that cause undef to be returned from the SOAP call, and this catches them. There are evals inside XML::Compile, so catching $@ at that level catches the error message, and I can retry based on that error message. |