#
# $Id: QueueManager.pm,v 33.3 2009/07/20 19:38:58 biersma Exp $
#
# (c) 1999-2009 Morgan Stanley & Co. Incorporated
# See ..../src/LICENSE for terms of distribution.
#
package MQSeries::QueueManager;
use 5.008;
use strict;
use Carp;
#
# We're going to use this to validate signal names
#
use Config;
use MQSeries qw(:functions);
use MQSeries::Properties;
use Params::Validate qw(validate);
#
# Well, now that we're using the same constants for the Inquire/Set
# interface, they no longer are really part of the Command/PCF
# hierarchy. We may or may not address this namespace asymmetry in a
# future release.
#
use MQSeries::Command::PCF;
our $VERSION = '1.30';
sub new {
my $proto = shift;
my $class = ref($proto) || $proto;
my %args = validate(@_, { 'QueueManager' => 0,
'Carp' => 0,
'CompCode' => 0,
'Reason' => 0,
'GetConvert' => 0,
'PutConvert' => 0,
'RetryCount' => 0,
'RetrySleep' => 0,
'RetryReasons' => 0,
'ConnectTimeout' => 0,
'ConnectTimeoutSignal' => 0,
'ClientConn' => 0,
'SSLConfig' => 0,
'SecurityParms' => 0,
'AutoCommit' => 0,
'AutoConnect' => 0,
});
my $self =
{
Carp => \&carp,
RetryCount => 0,
RetrySleep => 60,
RetryReasons => {
map { $_ => 1 }
(
MQSeries::MQRC_CONNECTION_BROKEN,
MQSeries::MQRC_Q_MGR_NOT_AVAILABLE,
MQSeries::MQRC_Q_MGR_QUIESCING,
MQSeries::MQRC_Q_MGR_STOPPING,
)
},
ConnectTimeoutSignal => 'USR1',
ConnectTimeout => 0,
ConnectArgs => {},
AutoCommit => 0,
};
bless ($self, $class);
#
# First thing -- override the Carp routine if given.
#
if ( $args{Carp} ) {
if ( ref $args{Carp} ne "CODE" ) {
carp "Invalid argument: 'Carp' must be a CODE reference";
return;
} else {
$self->{Carp} = $args{Carp};
}
}
#
# Minimally, the QueueManager is a required option.
#
# Uh, well, if you are using the "default" queue manager, we have
# to allow this to be optional.
#
if ( $args{QueueManager} ) {
if ( ref $args{QueueManager} && $args{QueueManager}->isa("MQSeries::QueueManager") ) {
$self->{QueueManager} = $args{QueueManager}->{QueueManager};
} else {
$self->{QueueManager} = $args{QueueManager};
}
} else {
$self->{QueueManager} = "";
}
#
# Sanity check the data conversion CODE snippets.
#
foreach my $key ( qw( PutConvert GetConvert ) ) {
if ( $args{$key} ) {
if ( ref $args{$key} ne "CODE" ) {
$self->{Carp}->("Invalid argument: '$key' must be a CODE reference");
return;
} else {
$self->{$key} = $args{$key};
}
}
}
#
# Sanity check the other optional attributes. Anything else in
# the arguments is ignored. Developer beware. RTFM. Yada yada
# yada.
#
foreach my $connectarg ( qw( RetryCount RetrySleep RetryReasons
ConnectTimeout ConnectTimeoutSignal
ClientConn SSLConfig SecurityParms) ) {
next unless exists $args{$connectarg};
$self->{ConnectArgs}->{$connectarg} = $args{$connectarg};
}
#
# Has AutoCommit behavior been specified? If not, we make a note
# of it.
#
if ( exists $args{AutoCommit} ) {
$self->{AutoCommit} = $args{AutoCommit};
}
#
# By default, we connect during the constructor. This can be
# turned off (for more detailed error handling) by passing
# AutoConnect as 0.
#
# On failure, we don't return an object, so there's nothing to
# call CompCode or Reason on. Scalar references can eb passed to
# get access to the completion code and reason.
#
unless (exists $args{'AutoConnect'} && $args{'AutoConnect'} == 0) {
my $result = $self->Connect();
foreach my $code ( qw( CompCode Reason )) {
if ( ref $args{$code} eq "SCALAR" ) {
${ $args{$code} } = $self->{$code};
}
}
return unless $result;
}
return $self;
}
sub Open {
my $self = shift;
my %args = validate(@_, { 'Options' => 0,
'ObjDesc' => 0,
});
$self->{CompCode} = MQSeries::MQCC_FAILED;
$self->{Reason} = MQSeries::MQRC_UNEXPECTED_ERROR;
#
# If the options are given, we assume you know what you're doing.
#
if ( exists $args{Options} ) {
$self->{Options} = $args{Options};
} else {
$self->{Options} = MQSeries::MQOO_INQUIRE |
MQSeries::MQOO_FAIL_IF_QUIESCING;
}
#
# Same for the ObjDesc
#
if ( exists $args{ObjDesc} ) {
if ( ref $args{ObjDesc} eq "HASH" ) {
$self->{ObjDescPtr} = $args{ObjDesc};
} else {
$self->{Carp}->("Invalid argument: 'ObjDesc' must be a HASH reference");
return;
}
} else {
$self->{ObjDescPtr} =
{
ObjectType => MQSeries::MQOT_Q_MGR,
};
}
#
# Open the Queue
#
$self->{Hobj} = MQOPEN(
$self->{Hconn},
$self->{ObjDescPtr},
$self->{Options},
$self->{CompCode},
$self->{Reason},
);
if ( $self->{CompCode} == MQSeries::MQCC_OK ) {
return 1;
} elsif ( $self->{CompCode} == MQSeries::MQCC_FAILED ) {
$self->{Carp}->(qq/MQOPEN failed (Reason = $self->{Reason})/);
return;
} else {
$self->{Carp}->(qq/MQOPEN failed, unrecognized CompCode: '$self->{CompCode}'/);
return;
}
}
sub Close {
my $self = shift;
return 1 unless $self->{Hobj};
$self->{CompCode} = MQSeries::MQCC_FAILED;
$self->{Reason} = MQSeries::MQRC_UNEXPECTED_ERROR;
MQCLOSE(
$self->{Hconn},
$self->{Hobj},
MQSeries::MQCO_NONE,
$self->{CompCode},
$self->{Reason},
);
if ( $self->{CompCode} == MQSeries::MQCC_OK ) {
delete $self->{Hobj};
return 1;
} elsif ( $self->{Reason} == MQSeries::MQRC_HCONN_ERROR ) {
delete $self->{Hobj};
return 1;
} else {
$self->{Carp}->("MQCLOSE of $self->{ObjDescPtr}->{ObjectName} on " .
qq/$self->{QueueManager} failed (Reason = $self->{Reason})/);
return;
}
}
sub ObjDesc {
my $self = shift;
if ( $_[0] ) {
if ( exists $self->{ObjDescPtr}->{$_[0]} ) {
return $self->{ObjDescPtr}->{$_[0]};
} else {
$self->{Carp}->("No such ObjDescPtr field: $_[0]");
return;
}
} else {
return $self->{ObjDescPtr};
}
}
sub CompCode {
my $self = shift;
return $self->{CompCode};
}
sub PutConvertReason {
my $self = shift;
return $self->{"PutConvertReason"};
}
sub Reason {
my $self = shift;
return $self->{Reason};
}
sub Reasons {
my $self = shift;
return $self->{ObjDescPtr}->{ResponseRecs};
}
sub Inquire {
my $self = shift;
my (@args) = @_;
$self->{CompCode} = MQSeries::MQCC_FAILED;
$self->{Reason} = MQSeries::MQRC_UNEXPECTED_ERROR;
my (@keys) = ();
my $ForwardMap = $MQSeries::Command::PCF::RequestValues{QueueManager};
my $ReverseMap = $MQSeries::Command::PCF::_Responses{MQSeries::MQCMD_INQUIRE_Q_MGR}->[1];
foreach my $key ( @args ) {
unless ( exists $ForwardMap->{$key} ) {
$self->{Carp}->("Unrecognized Queue attribute: '$key'");
return;
}
push(@keys,$ForwardMap->{$key});
}
my (@values) = MQINQ(
$self->{Hconn},
$self->{Hobj},
$self->{CompCode},
$self->{Reason},
@keys,
);
unless ( $self->{CompCode} == MQSeries::MQCC_OK &&
$self->{Reason} == MQSeries::MQRC_NONE ) {
$self->{Carp}->("MQINQ call failed. " .
qq/CompCode => '$self->{CompCode}', / .
qq/Reason => '$self->{Reason}'\n/);
return;
}
# In case the data parsing fails...
$self->{CompCode} = MQSeries::MQCC_FAILED;
$self->{Reason} = MQSeries::MQRC_UNEXPECTED_ERROR;
my (%values) = ();
for ( my $index = 0 ; $index <= $#keys ; $index++ ) {
my ($key,$value) = ($keys[$index],$values[$index]);
my ($newkey,$ValueMap) = @{$ReverseMap->{$key}};
if ( $ValueMap ) {
unless ( exists $ValueMap->{$value} ) {
$self->{Carp}->("Unrecognized value '$value' for key '$newkey'\n");
return;
}
$values{$newkey} = $ValueMap->{$value};
} else {
$values{$newkey} = $value;
}
}
$self->{CompCode} = MQSeries::MQCC_OK;
$self->{Reason} = MQSeries::MQRC_NONE;
return %values;
}
sub Disconnect {
my $self = shift;
return 1 unless $self->{Hconn};
#
# This should protect us from disconnecting from a queue manager
# when this object is destroyed in a forked child process.
#
return 1 unless exists $MQSeries::QueueManager::Pid2Hconn{$$};
#
# This should protect us from disconnecting when a given Hconn has
# been reused by more than one object. The order the objects are
# created or destroyed shouldnot matter.
#
return 1 if $MQSeries::QueueManager::Pid2Hconn{$$}->{$self->{Hconn}}-- > 1;
$self->{CompCode} = MQSeries::MQCC_FAILED;
$self->{Reason} = MQSeries::MQRC_UNEXPECTED_ERROR;
if ( $self->{_Pending} && $self->{AutoCommit} == 0 ) {
$self->Backout() || do {
my $putcnt = $self->{_Pending}->{Put};
my $getcnt = $self->{_Pending}->{Get};
$self->{Carp}->("Unable to backout pending transaction before disconnect\n" .
"Currently $putcnt puts and $getcnt gets pending\n" .
"Reason => " . MQReasonToText($self->Reason()) . "\n");
return;
};
}
MQDISC(
$self->{Hconn},
$self->{CompCode},
$self->{Reason},
);
if ( $self->{CompCode} == MQSeries::MQCC_OK ) {
delete $self->{Hconn};
return 1;
} else {
$self->{Carp}->(qq/MQDISC of $self->{QueueManager} failed (Reason = $self->{Reason})/);
return;
}
}
sub DESTROY {
my $self = shift;
$self->Close();
$self->Disconnect();
}
sub Backout {
my $self = shift;
$self->{CompCode} = MQSeries::MQCC_FAILED;
$self->{Reason} = MQSeries::MQRC_UNEXPECTED_ERROR;
MQBACK(
$self->{Hconn},
$self->{CompCode},
$self->{Reason},
);
if ( $self->{CompCode} == MQSeries::MQCC_OK ) {
delete $self->{_Pending};
return 1;
} else {
$self->{Carp}->(qq/MQBACK of $self->{QueueManager} failed (Reason = $self->{Reason})/);
return;
}
}
sub Commit {
my $self = shift;
$self->{CompCode} = MQSeries::MQCC_FAILED;
$self->{Reason} = MQSeries::MQRC_UNEXPECTED_ERROR;
MQCMIT(
$self->{Hconn},
$self->{CompCode},
$self->{Reason},
);
if ( $self->{CompCode} == MQSeries::MQCC_OK ) {
delete $self->{_Pending};
return 1;
} else {
$self->{Carp}->(qq/MQCMIT of $self->{QueueManager} failed (Reason = $self->{Reason})/);
return;
}
}
sub Pending {
my $self = shift;
return $self->{_Pending};
}
sub Put1 {
my $self = shift;
my %args = validate(@_, { 'Message' => 1,
'ObjDesc' => 0, # one of Queue or ObjDesc
'Queue' => 0, # one of Queue or ObjDesc
'Sync' => 0,
'PutMsgOpts' => 0,
'PutMsgRecs' => 0,
'PutConvert' => 0,
'Properties' => 0, # MQ v7
});
my $ObjDesc = {};
my $PutMsgOpts = { Options => MQSeries::MQPMO_FAIL_IF_QUIESCING, };
my $retrycount = 0;
my $buffer = undef;
unless (ref $args{Message} and $args{Message}->isa("MQSeries::Message")) {
$self->{Carp}->("Invalid argument: 'Message' must be an MQSeries::Message object");
return;
}
$self->{CompCode} = MQSeries::MQCC_FAILED;
$self->{Reason} = MQSeries::MQRC_UNEXPECTED_ERROR;
unless ($args{"ObjDesc"} or $args{Queue}) {
$self->{Carp}->("Invalid argument: either 'ObjDesc' or " .
"'Queue' must be specified");
return;
}
if ($args{"ObjDesc"}) {
unless ( ref $args{"ObjDesc"} eq 'HASH' ) {
$self->{Carp}->("Invalid ObjDesc argument; must be a HASH reference");
return;
}
$ObjDesc = $args{"ObjDesc"};
} else {
if (ref $args{Queue} eq "ARRAY") {
$ObjDesc->{ObjectRecs} = $args{Queue};
} else {
$ObjDesc->{ObjectName} = $args{Queue};
$ObjDesc->{ObjectQMgrName} = $args{QueueManager};
}
}
if ($args{PutMsgOpts}) {
unless (ref $args{PutMsgOpts} eq 'HASH') {
$self->{Carp}->("Invalid PutMsgOpts argument; must be a HASH reference");
return;
}
$PutMsgOpts = $args{PutMsgOpts};
} else {
if ($args{PutMsgRecs}) {
$PutMsgOpts->{PutMsgRecs} = $args{PutMsgRecs};
}
if ($args{Sync}) {
$PutMsgOpts->{Options} |= MQSeries::MQPMO_SYNCPOINT;
} else {
$PutMsgOpts->{Options} |= MQSeries::MQPMO_NO_SYNCPOINT;
}
}
#
# Sanity check the data conversion CODE snippets.
#
$self->{"PutConvertReason"} = 0;
if ($args{PutConvert}) {
if (ref $args{PutConvert} ne "CODE") {
$self->{Carp}->("Invalid argument: 'PutConvert' must be a CODE reference");
return;
} else {
$buffer = $args{PutConvert}->($args{Message}->Data());
unless ( defined $buffer ) {
$self->{"PutConvertReason"} = 1;
$self->{Carp}->("Data conversion hook (PutConvert) failed.");
return;
}
}
} else {
if ($args{Message}->can("PutConvert")) {
$buffer = $args{Message}->PutConvert($args{Message}->Data());
unless (defined $buffer) {
$self->{"PutConvertReason"} = 1;
$self->{Carp}->("Data conversion hook (PutConvert) failed.");
return;
}
} elsif (ref $self->{PutConvert} eq "CODE") {
$buffer = $self->{PutConvert}->($args{Message}->Data());
unless ( defined $buffer ) {
$self->{"PutConvertReason"} = 1;
$self->{Carp}->("Data conversion hook (PutConvert) failed.");
return;
}
} else {
$buffer = $args{Message}->Data();
}
}
#
# If the user specifies a Properties parameter, it must be a hash
# reference or an MQSeries::Propeties object. This ignores any
# existing message-level properties object.
#
my $props_obj; # Scope must extend past MQPUT1
if (defined $args{Properties}) {
my $props = $args{Properties};
if (ref $props eq 'HASH') {
$props_obj = MQSeries::Properties::->
new('QueueManager' => $self);
while (my ($name, $value) = each %$props) {
if (ref $value) { # Assume hash-ref
$props_obj->SetProperty(%$value, 'Name' => $name);
} else { # String
$props_obj->SetProperty('Name' => $name,
'Value' => $value);
}
}
} elsif (ref $props && $props->isa("MQSeries::Properties")) {
$props_obj = $props;
} else {
$self->{Carp}->("Invalid argument 'Properties': must be a hash reference");
return;
}
if (!defined $PutMsgOpts->{Version} ||
$PutMsgOpts->{Version} < MQSeries::MQPMO_VERSION_3) {
$PutMsgOpts->{Version} = MQSeries::MQPMO_VERSION_3;
}
$PutMsgOpts->{OriginalMsgHandle} = $props_obj->{Hmsg};
}
MQPUT1($self->{Hconn},
$ObjDesc,
$args{Message}->MsgDesc(),
$PutMsgOpts,
$buffer,
$self->{CompCode},
$self->{Reason},
);
if ($self->{CompCode} == MQSeries::MQCC_FAILED) {
$self->{Carp}->("MQPUT1 failed (Reason = $self->{Reason})");
return;
} else {
if ($PutMsgOpts->{Options} & MQSeries::MQPMO_SYNCPOINT) {
$self->{_Pending}->{Put}++;
}
return 1;
}
}
sub Connect {
my $self = shift;
my @combined_params = ( %{$self->{ConnectArgs}}, @_ );
my %args = validate(@combined_params,
{ 'RetryCount' => 0,
'RetrySleep' => 0,
'RetryReasons' => 0,
'ConnectTimeout' => 0,
'ConnectTimeoutSignal' => 0,
'ClientConn' => 0,
'SSLConfig' => 0,
'SecurityParms' => 0,
'AutoConnect' => 0,
});
return 1 if $self->{Hconn};
$self->{CompCode} = MQSeries::MQCC_FAILED;
$self->{Reason} = MQSeries::MQRC_UNEXPECTED_ERROR;
my $retrycount = 0;
foreach my $key ( qw( RetryCount RetrySleep ConnectTimeout ) ) {
next unless exists $args{$key};
unless ( $args{$key} =~ /^\d+$/ ) {
$self->{Carp}->("Invalid argument: '$key' must numeric");
return;
}
$self->{$key} = $args{$key};
}
#
# The ConnectTimeout functionality requires:
#
# (a) A working fork()
# (b) Support for signals
# (c) The ability to send signals to other processes (Win32 loses,
# cause you can only send signals to yourself)
#
if ( $self->{ConnectTimeout} && $^O =~ /win32/i ) {
$self->{Carp}->("MQCONN timeout functionality is not yet supported on Win32");
$self->{ConnectTimeout} = 0;
}
if ( $self->{ConnectTimeout} && not defined $Config{d_fork} ) {
$self->{Carp}->("This platform does not support fork()\n" .
"MQCONN timeout functionality is disabled");
$self->{ConnectTimeout} = 0;
}
if ( $self->{ConnectTimeout} && not defined $Config{sig_name} ) {
$self->{Carp}->("Signals are not available in this version of perl\n" .
"MQCONN timeout functionality is disabled");
$self->{ConnectTimeout} = 0;
}
if ( $self->{ConnectTimeout} ) {
$self->{ConnectTimeoutSignal} = $args{ConnectTimeoutSignal} if $args{ConnectTimeoutSignal};
my %signame = map { $_ => 1 } split(/\s+/,$Config{sig_name});
unless ( $signame{$self->{ConnectTimeoutSignal}} ) {
$self->{Carp}->("Signal name '$self->{ConnectTimeoutSignal}' is not supported on this platform\n" .
"MQCONN timeout functionality is disabled");
$self->{ConnectTimeout} = 0;
}
}
if ( $args{RetryReasons} ) {
unless (
ref $args{RetryReasons} eq "ARRAY" ||
ref $args{RetryReasons} eq "HASH"
) {
$self->{Carp}->("Invalid Argument: 'RetryReasons' must be an ARRAY or HASH reference");
return;
}
if ( ref $args{RetryReasons} eq 'HASH' ) {
$self->{RetryReasons} = $args{RetryReasons};
} else {
$self->{RetryReasons} = { map { $_ => 1 } @{$args{RetryReasons}} };
}
}
my $mqconnx_opts = {};
foreach my $opt (qw(ClientConn SSLConfig SecurityParms)) {
$mqconnx_opts->{$opt} = $args{$opt} if ($args{$opt});
}
CONNECT:
{
my $Hconn = "";
my $timedout = 0;
if ( $self->{ConnectTimeout} ) {
my $alarm = "MQSeries::QueueManager MQCONN timeout";
my $child = 0;
FORK:
{
if ( $child = fork ) {
#
# We're in the parent. Set the signal
# handler, and call MQCONN inside an eval. If
# the child kills us, $@ will indicate this.
#
eval {
local $SIG{$self->{ConnectTimeoutSignal}} = sub { die $alarm };
$Hconn = MQCONNX($self->{ProxyQueueManager} ||
$self->{QueueManager},
$mqconnx_opts,
$self->{CompCode},
$self->{Reason},
);
};
kill $self->{ConnectTimeoutSignal}, $child;
waitpid($child,0);
$timedout++ if $@ =~ /$alarm/;
} elsif ( defined $child ) {
#
# We're in the child. Hand around and then
# send a signal to the parent letting it know
# the timeout period was reached.
#
local $SIG{$self->{ConnectTimeoutSignal}} = sub { exit 0; };
my $ppid = getppid();
sleep $self->{ConnectTimeout};
kill $self->{ConnectTimeoutSignal}, $ppid;
exit 0;
} else {
# XXX do we even want retry logic here??? Hmm...
$self->{Carp}->("Unable to fork: $!");
return;
}
}
} else {
$Hconn = MQCONNX($self->{ProxyQueueManager} ||
$self->{QueueManager},
$mqconnx_opts,
$self->{CompCode},
$self->{Reason},
);
}
if ( $self->{Reason} == MQSeries::MQRC_NONE ||
$self->{Reason} == MQSeries::MQRC_ALREADY_CONNECTED ) {
$self->{Hconn} = $Hconn;
$MQSeries::QueueManager::Pid2Hconn{$$}->{$self->{Hconn}}++;
#
# Inquire the command level and some config parameters,
# and cache them.
#
eval {
$self->Open();
my %attr = $self->Inquire(qw(CommandLevel
MaxMsgLength
MaxUncommittedMsgs
Platform));
$self->Close();
$self->{QMgrConfig} = \%attr;
};
if ($@) {
$self->{Carp}->("Could not inquire queue manager attributes");
}
return 1;
}
if ( $timedout ) {
$self->{Carp}->("MQCONN failed (interrupted after $self->{ConnectTimeout} seconds)");
} else {
$self->{Carp}->(qq{MQCONN failed (Reason = $self->{Reason}) (} .
MQReasonToText($self->{Reason}) . ")");
}
if (
$self->{RetryCount} &&
( exists $self->{RetryReasons}->{$self->{Reason}} || $timedout )
) {
if ( $retrycount < $self->{RetryCount} ) {
$retrycount++;
$self->{Carp}->("Retrying MQCONN call in $self->{RetrySleep} seconds...");
sleep $self->{RetrySleep};
redo CONNECT;
} else {
$self->{Carp}->("Maximum retry attempts reached ($self->{RetryCount})");
}
}
return;
}
}
#
# Return status information on previous operations (MQ v7). Right
# now, this returns info on asynchronous puts; IBM may extend this in
# the future.
#
# Returns:
# - Ref to hash with statistics
#
sub StatusInfo {
my $self = shift;
unless ($self->{Hconn}) {
$self->{Carp}->("Must be connected before invoking MQSTAT\n");
return;
}
my $stats = {};
MQSTAT($self->{Hconn},
MQSeries::MQSTAT_TYPE_ASYNC_ERROR,
$stats,
$self->{CompCode},
$self->{Reason});
if ($self->{CompCode} != MQSeries::MQCC_OK) {
$self->{Carp}->("MQSTAT failed (Reason = $self->{Reason}\n");
}
return $stats;
}
1;
__END__
=head1 NAME
MQSeries::QueueManager - OO interface to the MQSeries Queue Manager
=head1 SYNOPSIS
use MQSeries qw(:functions);
use MQSeries::QueueManager;
#
# Simplest, trivial usage
#
my $qmgr = MQSeries::QueueManager->
new(QueueManager => 'some.queue.manager' ) ||
die("Unable to connect to queue manager\n");
#
# The best way to do error checking. Handle the object
# instantiation and connection to the queue manager independently.
#
my $qmgr = MQSeries::QueueManager->new
(
QueueManager => 'some.queue.manager',
AutoConnect => 0,
) || die "Unable to instantiate MQSeries::QueueManager object\n";
$qmgr->Connect() ||
die("Unable to connect to queue manager\n" .
"CompCode => " . $qmgr->CompCode() . "\n" .
"Reason => " . $qmgr->Reason() .
" (", MQReasonToText($qmgr->Reason()) . ")\n");
#
# Advanced usage. Enable the connection timeout, and connection
# retry logic.
#
my $qmgr = MQSeries::QueueManager->new
(
QueueManager => 'some.queue.manager',
AutoConnect => 0,
ConnectTimeout => 120,
RetryCount => 60,
RetrySleep => 10,
) || die "Unable to instantiate MQSeries::QueueManager object\n";
$qmgr->Connect() ||
die("Unable to connect to queue manager\n" .
"CompCode => " . $qmgr->CompCode() . "\n" .
"Reason => " . $qmgr->Reason() .
" (", MQReasonToText($qmgr->Reason()) . ")\n";
#
# Avoid a channel table file or MQSERVER variable and specify
# the client connect options directly.
#
my $qmgr = MQSeries::QueueManager->new
(
QueueManager => 'some.queue.manager',
ClientConn => { 'ChannelName' => 'FOO',
'TransportType' => 'TCP', # Default
'ConnectionName' => "hostname(1414)",
'MaxMsgLength' => 16 * 1024 * 1024,
},
) || die("Unable to connect to queue manager\n");
#
# Put a message under syncpoint, then commit/backout (in most cases,
# a Put is done using an MQSeries::Queue object instead)
#
my $msg = MQSeries::Message->new(Data => $msg_data);
$qmgr->Put1(Message => $msg,
Queue => 'SOME.QUEUE.NAME',
Sync => 1,
);
if (some_other_work_succeeds()) {
$qmgr->Commit();
} else {
$qmgr->Backout();
}
#
# MQ v7: perform multiple asynchronous Put1 operations,
# then check if any of them ran into issues. Asnchronous
# puts can also be performed from a queue objects, and are
# allowed with syncpoint.
#
foreach my $msg_data (@data_to_be_sent) {
my $msg = MQSeries::Message->new(Data => $msg_data);
$qmgr->Put1(Message => $msg,
Queue => 'SOME.QUEUE.NAME',
PutMsgOpts => { Options => (MQSeries::MQPMO_ASYNC_RESPONSE |
MQSeries::MQPMO_FAIL_IF_QUIESCING),
},
);
}
my $status = $qmgr->StatusInfo();
=head1 DESCRIPTION
The MQSeries::QueueManager object is an OO mechanism for connecting to
an MQSeries queue manager, and/or opening and inquiring a queue
manager object.
This module is used together with MQSeries::Queue, MQSeries::Message
and MQSeries::Properties, and the other MQSeries::* modules. These
objects provide a simpler, higher level interface to the MQI.
This module also provides special support for connect timeouts (for
interrupting MQCONNX() calls that may hang forever), as well as connect
retry logic, which will retry failed MQCONNX() calls for a specific
list of reason codes.
See the "Special Considerations" section for a discussion of these
advanced, but powerful, features.
=head1 METHODS
=head2 new
The constructor takes a hash as an argument, with the following keys:
Key Value
=== =====
QueueManager String
Carp CODE reference
AutoConnect Boolean
AutoCommit Boolean
ConnectTimeout Numeric
ConnectTimeoutSignal String
GetConvert CODE reference
PutConvert CODE reference
RetrySleep Numeric
RetryCount Numeric
RetryReasons HASH Reference
CompCode Reference to Scalar Variable
Reason Reference to Scalar Variable
=over 4
=item QueueManager
This is simply the name of the Queue Manager to which to connect.
This is passed directly to the MQCONNX() call as-is.
Normally, this is simply the name of the queue manager to which you
wish to connect, but if the "default" queue manager is to be used,
then this can either be the empty string "", or simply omitted
entirely.
=item Carp
This key specifies a code reference to a routine to replace all of the
carp() calls in the API, allowing the user of the API to trap and
handle all of the error message generated internally, or simply
redirect how they get logged.
For example, one might want everything to be logged via syslog:
sub MyLogger {
my ($message) = @_;
foreach my $line (split(/\n+/, $message)) {
syslog("err", $line);
}
}
Then, one tells the object to use this routine:
my $qmgr = MQSeries::QueueManager->new
(
QueueManager => 'some.queue.manager',
Carp => \&MyLogger,
) || die("Unable to connect to queue manager.\n");
The default, as one might guess, is Carp::carp();
=item AutoConnect
This is an optional parameter that defaults to true. If the value of
this argument is false, then the constructor will not automatically
call the C method, allowing the developer to call it
explicitly, and thus independently error check object instantiation
and the connection to the queue manager. See the section on Error
Handling in Special Considerations.
=item AutoCommit
If the value of this argument is true, then pending transactions will
be committed during object destruction. If it is false, then pending
transactions will be backed out before disconnecting from the queue
manager during object destruction.
See the section on "AutoCommit" in "Special Considerations".
=item ClientConn
For client connections, the connection details must be provided
somehow. By default, the client channel table file (AMQCLCHL.TAB) or
MQSERVER environment variable is used. This optional parameter allows
you to specify the details in your code (possibly read from some sort
of configuration file or directory service).
The C parameter is a hash reference of which the
C, C and C are most
relevant. See the description of the C parameter structure in
the Application Programming Reference guide for details on the other
fields.
=item SSLConfig
For client connections using SSL, the SSL key repository must be
specified. Than can be done usign the C environment
variable, but also using the C data structured specified on the
MQCONNX. The C parameters provides a way to specify the
key repositority, and overrides the C environment
variable. The example below shows how it can be used:
my $qmgr = MQSeries::QueueManager->
new(QueueManager => 'some.queue.manager',
SSLConfig => { 'KeyRepository' => '/var/mqm/ssl/key' },
);
The C option is usually combined with the C
parameter documented above.
=item SecurityParms
In MQ v6 and above, security parameters can be specified. The example
below shows how it can be used:
my $qmgr = MQSeries::QueueManager->
new(QueueManager => 'some.queue.manager',
SecurityParms => { 'AuthenticationType' => MQSeries::MQZAT_INITIAL_CONTEXT,
'CSPUserId' => $userid,
'CSPPassword' => $passwd,
},
);
By default, no authentication takes place, and channel security exits
can be used.
The C parameter is a hash reference with members
C, C and C. See the
description of the C parameter structure in the Application
Programming Reference guide for details on the other fields.
=item ConnectTimeout
If this value is given, it must be a positive integer. This is the
time, in seconds, in which an MQCONNX() must complete before the MQI
call will be interrupted. The default value is zero, which means the
MQCONNX() call will not be interrupted.
There are outage scenarios, in the experience of the author, where the
MQCONNX() call will block indefinitely and never return. This happens
when a queue manager is "hung", and completely unresponsive, in some
cases.
This feature should be used with caution, since it is implemented
using a SIGALRM handler, and the alarm() system call. See the section
on "Connection Timeouts" in "Special Considerations".
Attempts to use this feature on unsupported platforms that do not
support signals will generate a warning, and be silently ignored.
=item ConnectTimeoutSignal
By default, the ConnectTimeout mechanism is implemented using a signal
handler for SIGUSR1, but the signal used to interrupt the MQCONNX()
call can be customized using this attribute.
The signal handler installed by this API is done using local(), so the
effects of the handler will only override the applications handler
during the call to MQCONNX().
The string used for this attribute should be the short hand name of
the signal, for example, to set the signal to SIGUSR2:
my $qmgr = MQSeries::QueueManager->new
(
QueueManager => 'FOO',
ConnectTimeout => 300,
ConnectTimeoutSignal => 'USR2',
) || die;
=item RetryCount
This is an integer value, and specifies the maximum number of times to
retry the connection, before failing. The default is 0.
=item RetrySleep
This is an integer value, and specified the number of seconds to sleep
between retries. The maximum timeout for an outage is the product of
the RetrySleep and RetryCount parameters. The default is 0.
=item PutConvert, GetConvert
These are CODE references to subroutines which are used to convert the
data in a MQSeries::Message object prior to passing it to the MQPUT
MQI call, or convert the data retreived from the queue by the MQGET
MQI call before inserting it into a MQSeries::Message object.
These must be CODE references, or the new() constructor will fail. A
properly written conversion routine will be passed a single scalar
value and return a single scalar value. In the event of an error, the
conversion routine should return 'undef'.
The example shown in the synopsis shows how one might use a pair of
home grown encryption and decryption subroutines to keep data in clear
text in core, but encrypted in the contents of the message on the
queue. This is probably not the most hi-tech way to encrypt MQSeries
data, of course.
The MQSeries::Message::Storable class provides an example of how to
subclass MQSeries::Message to have this type of conversion handled
transparently, in the class definition.
=item CompCode, Reason
When the constructor encounters an error, it returns nothing, and you
can not make method calls off of a non-existent object. Thus, you do
not have access to the CompCode() and Reason() method calls. If you
want to extract these values, you will have to pass a scalar reference
value to the constructor, for example:
my $CompCode = MQSeries::MQCC_FAILED;
my $Reason = MQSeries::MQRC_UNEXPECTED_ERROR;
my $qmgr = MQSeries::QueueManager->new
(
QueueManager => 'some.queue.manager',
CompCode => \$CompCode,
Reason => \$Reason,
) || die "Unable to open queue: CompCode => $CompCode, Reason => $Reason\n";
But, this is ugly (authors opinion, but then, he gets to write the
docs, too). Use the C option instead to separate C
and C.
=item RetryCount
The call to MQCONNX() (implemented via the Connect() method), can be
told to retry the failure for a specific list of reason codes. This
functionality is only enabled if the RetryCount is non-zero. By
default, this value is zero, and thus retries are disabled.
=item RetrySleep
This argument is the amount of time, in seconds, to sleep between
subsequent retry attempts.
=item RetryReasons
This argument is either an ARRAY or HASH reference indicating the
specific reason code for which retries will be attempted. If given as
an ARRAY, the elements are simply the reason codes, and if given as a
HASH, then the keys are the reason codes (and the values ignored).
=back
=head2 Connect
This method takes no arguments, and merely calls MQCONNX() to connect
to the queue manager. The various options are all set via the
MQSeries::QueueManager constructor (see above).
This method is called automatically by the constructor, unless the
C argument is specified and set to false.
Note that this is a new method as of the 1.06 release, and is provided
to enable more fine grained error checking. See the ERROR HANDLING
section.
=head2 Disconnect
This methodtakes no arguments, and merely calls MQDISC() to disconnect
from the queue manager.
It is important to note that normally, this method need not be called,
since it is implicitly called via the object destructor. If the
Disconnect() call errors need to be handled, then it can be done
explicitly. See the ERROR HANDLING section.
=head2 Backout
This method takes no arguments, and merely calls MQBACK. It returns
true on success, and false on failure.
=head2 Commit
This method takes no arguments, and merely calls MQCMIT. It returns
true on success, and false on failure.
=head2 Put1
This method wraps the MQPUT1 call. The arguments are a hash, with the
following key/value pairs (required keys are marked with a '*'):
Key Value
=== =====
Message* MQSeries::Message object
Queue String, or ARRAY reference (distribution list)
QueueManager String
ObjDesc HASH reference
PutMsgOpts HASH Reference
PutMsgRecs ARRAY Reference
Sync Boolean
PutConvert CODE reference
Properties HASH Reference or MQSeries::Properties object
The return value is true or false, depending on the success of the
underlying MQPUT1() call. If the operation fails, then the Reason()
and CompCode() methods will return the appropriate error codes, if the
error was an MQSeries error.
If a PutConvert() method failed before the actual MQPUT1() function
was called, then the Reason() code will be MQRC_UNEXPECTED_ERROR, and
the PutConvertReason() will be true. All of the PutConvert() methods
supplied with the various MQSeries::Message subclasses in this
distribution will generate some form of error via carp (or the Carp
attribute of the objects, if overridden).
=over 4
=item Message
This argument is the message to be placed onto the queue. The value
must be an MQSeries::Message object.
=item Queue
This is the queue, or list of queue if using a distribution list, to
which to put the message. If it is a single queue, then this value is
a string, naming the queue. If it is a distribution list, then this
value is an ARRAY reference, listing the target queues. There are
three ways to specify the list.
The list may be a simple array of strings:
$qmgr->Put1(
Message => $message,
Queue => [qw( QUEUE1 QUEUE2 QUEUE3 )],
)
or, it can be an array of arrays, each one specifying the queue and
queue manager name of the target queue:
$qmgr->Put1(
Message => $message,
Queue => [
[qw( QUEUE1 QM1 )],
[qw( QUEUE2 QM2 )],
[qw( QUEUE3 QM3 )],
],
)
or finally, it can be an array of hash references, each naming the
queue and queue manager:
$qmgr->Put1(
Message => $message,
Queue => [
{
ObjectName => 'QUEUE1',
ObjectQMgrName => 'QM1',
},
{
ObjectName => 'QUEUE2',
ObjectQMgrName => 'QM2',
},
{
ObjectName => 'QUEUE3',
ObjectQMgrName => 'QM3',
},
],
)
In the latter two cases, the queue manager names are optional. Which
method to use is largely a choice of style.
=item QueueManager
Note that this key is B relevant when not using distribution
lists. This identifies the queue manager of the target queue, to
which the message is being written. This is an optional key.
=item ObjDesc
The entire ObjDesc structure passed to the underlying MQPUT1() call
can be specified via this key. In this case, the Queue and/or
QueueManager are simply ignored. Use of this key would be considered
somewhat non-conventional, as the OO API is attempting to hide the
complexity of these underlying data structures.
However, this allows the developer access to the entire ObjDesc, if
necessary.
=item PutMsgOpts
This argument forces the developer to specify the complete PutMsgOpts
structure, and will override the use of convenience flags, such as
Sync. Similar to the use of ObjDesc, this is non-conventional, but
provided to allow access to the complete API, if necessary.
=item PutMsgRecs
This argument is relevant only when using distribution lists.
The value is an ARRAY reference, specifying the put message records
for the individual queues in the distribution list. Normally, these
are specified as part of the PutMsgOpts, but this API attempts to hide
the complexity of the PutMsgOpts structure from the user.
When using distribution lists, PutMsgRecs are often necessary to
control how the MsgId, CorrelId, and three other specific fields in
the MsgDesc are handled.
For details, see the MQPUT() and MQPUT1() documentation in
MQSeries(3).
=item Sync
This is a flag to indicate that the Syncpoint option is to be used,
and the message(s) not committed to the queue until an MQBACK or
MQCOMM call is made. These are both wrapped with the Backout() and
Commit() methods respectively.
The value is simply interpreted as true or false.
=item PutConvert
See the new() constuctor documentation for the verbose details. This
can be specified for just the Put1() method in the event that a
converted message format needs to be put to a queue on a
MQSeries::QueueManager object for which default conversion routines
have not been installed.
If you have a QueueManager for which all of the Queue use the same
message formats, then you can simply specify the PutConvert and
GetConvert CODE references once, when the MQSeries::QueueManager
object is instantiated. Alternately, you may be specifying the
conversion routined for only a few specific queues. In the latter
case, it is entirely possible that you will need to specify PutConvert
when performing an MQPUT1 MQI call via the Put1() method.
=item Properties
This parameter is only supported if the module has been compiled with
the MQ v7 libraries. It allows properties to be specified with the
message. This can be used for selectors or for publish/subscribe.
The Properties parameter can be a hash reference or an
MQSeries::Properties object. If it is a hash reference, it can be
specified in two ways: as key/value pairs, or with property options.
Specifying the proeprties as key/value pairs is straightforward:
Properties => { 'perl.MQSeries.label' => 'important',
'perl.MQSeries.customer' => 'BigCompany',
}
In this case, the property values are specified to MQ as strings,
which is usually the correct thing to do. However, property options
can be specified if so desired:
Properties => { 'perl.MQSeries.label' => 'important',
'perl.MQSeries.price' => { Type => MQSeries::MQTYPE_FLOAT64,
Value => '8.99',
},
'perl.MQSeries.count' => { Type => MQSeries::MQTYPE_INT32,
Value => 12,
},
}
In addition to Name and Value, you can also specify Encoding and CCSID.
=back
=head2 StatusInfo
This method returns status information and is only supported if the
module has been compiled for MQ v7. It returns information on
previous asynchronous put operations, which can be queue manager Put1
operations or queue Put operations.
The StatusInfo method takes no parameters and returns a hash reference
matching the MQSTS data structure. (See the Application Programming
Reference for details, or run Data::Dumper on the return value.)
=head2 CompCode
This method returns the MQI Completion Code for the most recent MQI
call attempted.
=head2 Reason
This method returns the MQI Reason Code for the most recent MQI
call attempted.
=head2 PutConvertReason
This method returns a true of false value, indicating if a PutConvert
method failed or not. Similar to the MQRC reason codes, false
indicates success, and true indicates some form of error. If there
was no PutConvert method called, this will always return false.
=head2 Reasons
This method call returns an array reference, and each member of the
array is a Response Record returned as a possible side effect of
calling a Put1() method to put a message to a distribution list.
The individual records are hash references, with two keys: CompCode
and Reason. Each provides the specific CompCode and Reason associated
with the put of the message to each individual queue in the
distribution list, respectively.
=head2 Open
This method takes two optional (but typically not necessary)
arguments, and calls MQOPEN() on the Queue Manager, in order to enable
the Inquire method. The arguments are a has, with the following
keys:
Key Value
=== =====
Options MQOPEN 'Options' Values
ObjDesc HASH reference (MQOD structure)
The Options default to MQOO_INQUIRE|MQOO_FAIL_IS_QUIESCING, which is
usually correct. Note that you can not call MQSET() on a queue
manager, so MQOO_SET is meaningless, as are most of the other options.
Advanced users can set this as they see fit.
The ObjDesc argument is also not terribly interesting, as you most of
the values have reasonable defaults for a queue manager. Again, the
API supports advanced users, so you can set this as you see fit. The
keys of the ObjDesc hash are the fields in the MQOD structure.
This method returns a true of false values depending on its success or
failure. Investigate the CompCode() and Reason() for
MQSeries-specific error codes.
=head2 Close
This method takes no arguments, and merely calls MQCLOSE() to close
the actual queue manager object. This is meaningful only if the queue
manager has been Open()ed for use by Inquire().
It is important to note that normally, this method need not be called,
since it is implicitly called via the object destructor, if necessary.
If the Close() call errors need to be handled, then it can be done
explicitly. See the ERROR HANDLING section.
=head2 Inquire
This method is an interface to the MQINQ() API call, however, it takes
more convenient, human-readable strings in place of the C macros for
the selectors, as well as supports more readable strings for some of
the data values as well.
For example, to query the Platform and DeadLetterQName of a queue
manager:
my %qmgrattr = $qmgr->Inquire( qw(Platform DeadLetterQName) );
The argument to this method is a list of "selectors", or QueueManager
attributes, to be queried. The following table shows the complete set
of possible keys, and their underlying C macro.
Note that this list is all-inclusive, and that many of these are not
supported on some of the MQSeries releases or platforms. Consult the IBM
documentation for such details.
Key Macro
=== =====
AccountingConnOverride MQIA_ACCOUNTING_CONN_OVERRIDE,
AccountingInterval MQIA_ACCOUNTING_INTERVAL,
ActivityRecording MQIA_ACTIVITY_RECORDING,
AlterationDate MQCA_ALTERATION_DATE,
AlterationTime MQCA_ALTERATION_TIME,
AdoptNewMCACheck MQIA_ADOPTNEWMCA_CHECK,
AdoptNewMCAType MQIA_ADOPTNEWMCA_TYPE,
AlterationTime MQCA_ALTERATION_TIME,
AuthorityEvent MQIA_AUTHORITY_EVENT,
BridgeEvent MQIA_BRIDGE_EVENT,
ChannelAutoDef MQIA_CHANNEL_AUTO_DEF,
ChannelAutoDefEvent MQIA_CHANNEL_AUTO_DEF_EVENT,
ChannelAutoDefExit MQCA_CHANNEL_AUTO_DEF_EXIT,
ChannelEvent MQIA_CHANNEL_EVENT,
ChannelInitiatorControl MQIA_CHINIT_CONTROL,
ChannelMonitoring MQIA_MONITORING_CHANNEL,
ChannelStatistics MQIA_STATISTICS_CHANNEL,
ChinitAdapters MQIA_CHINIT_ADAPTERS,
ChinitDispatchers MQIA_CHINIT_DISPATCHERS,
ChinitServiceParm MQCA_CHINIT_SERVICE_PARM,
ChinitTraceAutoStart MQIA_CHINIT_TRACE_AUTO_START,
ChinitTraceTableSize MQIA_CHINIT_TRACE_TABLE_SIZE,
ClusterSenderMonitoringDefault MQIA_MONITORING_AUTO_CLUSSDR,
ClusterSenderStatistics MQIA_STATISTICS_AUTO_CLUSSDR,
ClusterWorkLoadData MQCA_CLUSTER_WORKLOAD_DATA,
ClusterWorkLoadExit MQCA_CLUSTER_WORKLOAD_EXIT,
ClusterWorkLoadLength MQIA_CLUSTER_WORKLOAD_LENGTH,
CLWLMRUChannels MQIA_CLWL_MRU_CHANNELS,
CLWLUseQ MQIA_CLWL_USEQ,
CodedCharSetId MQIA_CODED_CHAR_SET_ID,
CommandEvent MQIA_COMMAND_EVENT,
CommandInputQName MQCA_COMMAND_INPUT_Q_NAME,
CommandLevel MQIA_COMMAND_LEVEL,
CommandServerControl MQIA_CMD_SERVER_CONTROL,
ConfigurationEvent MQIA_CONFIGURATION_EVENT,
CPILevel MQIA_CPI_LEVEL,
CreationDate MQCA_CREATION_DATE,
CreationTime MQCA_CREATION_TIME,
DeadLetterQName MQCA_DEAD_LETTER_Q_NAME,
DefXmitQName MQCA_DEF_XMIT_Q_NAME,
DistLists MQIA_DIST_LISTS,
DNSGroup MQCA_DNS_GROUP,
DNSWLM MQIA_DNS_WLM,
ExpiryInterval MQIA_EXPIRY_INTERVAL,
IGQUserId MQCA_IGQ_USER_ID,
IGQPutAuthority MQIA_IGQ_PUT_AUTHORITY,
InhibitEvent MQIA_INHIBIT_EVENT,
IntraGroupQueueing MQIA_INTRA_GROUP_QUEUING,
IPAddressVersion MQIA_IP_ADDRESS_VERSION,
ListenerTimer MQIA_LISTENER_TIMER,
LocalEvent MQIA_LOCAL_EVENT,
LoggerEvent MQIA_LOGGER_EVENT,
LUGroupName MQCA_LU_GROUP_NAME,
LUName MQCA_LU_NAME,
LU62ARMSuffix MQCA_LU62_ARM_SUFFIX,
LU62Channels MQIA_LU62_CHANNELS,
MaxActiveChannels MQIA_ACTIVE_CHANNELS,
MaxChannels MQIA_MAX_CHANNELS,
MaxHandles MQIA_MAX_HANDLES,
MaxMsgLength MQIA_MAX_MSG_LENGTH,
MaxPriority MQIA_MAX_PRIORITY,
MaxPropertiesLength MQIA_MAX_PROPERTIES_LENGTH,
MaxUncommittedMsgs MQIA_MAX_UNCOMMITTED_MSGS,
MQIAccounting MQIA_ACCOUNTING_MQI,
MQIStatistics MQIA_STATISTICS_MQI,
MsgMarkBrowseInterval MQIA_MSG_MARK_BROWSE_INTERVAL,
OutboundPortMax MQIA_OUTBOUND_PORT_MAX,
OutboundPortMin MQIA_OUTBOUND_PORT_MIN,
Parent MQCA_PARENT,
PerformanceEvent MQIA_PERFORMANCE_EVENT,
Platform MQIA_PLATFORM,
PubSubMaxMsgRetryCount MQIA_PUBSUB_MAXMSG_RETRY_COUNT,
PubSubMode MQIA_PUBSUB_MODE,
PubSubNPInputMsg MQIA_PUBSUB_NP_MSG,
PubSubNPResponse MQIA_PUBSUB_NP_RESP,
PubSubSyncPoint MQIA_PUBSUB_SYNC_PT,
QMgrDesc MQCA_Q_MGR_DESC,
QMgrIdentifier MQCA_Q_MGR_IDENTIFIER,
QMgrName MQCA_Q_MGR_NAME,
QSharingGroupName MQCA_QSG_NAME,
QueueAccounting MQIA_ACCOUNTING_Q,
QueueMonitoring MQIA_MONITORING_Q,
QueueStatistics MQIA_STATISTICS_Q,
ReceiveTimeout MQIA_RECEIVE_TIMEOUT,
ReceiveTimeoutMin MQIA_RECEIVE_TIMEOUT_MIN,
ReceiveTimeoutType MQIA_RECEIVE_TIMEOUT_TYPE,
RemoteEvent MQIA_REMOTE_EVENT,
RepositoryName MQCA_REPOSITORY_NAME,
RepositoryNamelist MQCA_REPOSITORY_NAMELIST,
SecurityCase MQIA_SECURITY_CASE,
SharedQQMgrName MQIA_SHARED_Q_Q_MGR_NAME,
SSLCRLNamelist MQCA_SSL_CRL_NAMELIST,
SSLCryptoHardware MQCA_SSL_CRYPTO_HARDWARE,
SSLEvent MQIA_SSL_EVENT,
SSLFipsRequired MQIA_SSL_FIPS_REQUIRED,
SSLKeyRepository MQCA_SSL_KEY_REPOSITORY,
SSLKeyResetCount MQIA_SSL_RESET_COUNT,
SSLTasks MQIA_SSL_TASKS,
StartStopEvent MQIA_START_STOP_EVENT,
StatisticsInterval MQIA_STATISTICS_INTERVAL,
SyncPoint MQIA_SYNCPOINT,
TCPChannels MQIA_TCP_CHANNELS,
TCPKeepAlive MQIA_TCP_KEEP_ALIVE,
TCPStackType MQIA_TCP_STACK_TYPE,
TCPName MQCA_TCP_NAME,
TraceRouteRecording MQIA_TRACE_ROUTE_RECORDING,
TreeLifeTime MQIA_TREE_LIFE_TIME,
TriggerInterval MQIA_TRIGGER_INTERVAL,
The return value of this method is a hash, whose keys are those given
as arguments, and whose values are the queried queue manager
attributes. In almost all cases, the values are left unmolested, but
in the following case, the values are mapped to more readable strings.
=over 4
=item Platform (integer)
Key Macro
=== =====
MVS MQPL_MVS
NSK MQPL_NSK
OS2 MQPL_OS2
OS400 MQPL_OS400
UNIX MQPL_UNIX
Win16 MQPL_WINDOWS
Win32 MQPL_WINDOWS_NT
=back
=head2 ObjDesc
This method can be used to query the ObjDesc data structure. If no
argument is given, then the ObjDesc hash reference is returned. If a
single argument is given, then this is interpreted as a specific key,
and the value of that key in the ObjDesc hash is returned.
NOTE: This method is meaningless unless the queue manager has been
MQOPEN()ed via the Open() method.
=head1 Special Considerations
=head2 AutoCommit
Normally, when you have pending transactions (i.e. MQPUT() and/or
MQGET() calls with syncpoint), they will be automatically committed
when MQDISC() is called. The MQSeries::QueueManager object
destructor, in an attempt to make things easy for the programmer,
automatically calls MQDISC() for you. The result is that transactions
will be automatically committed when the application exits in any way
that allows the object destruction to occur.
This behavior is somewhat counter intuitive, as you would expect
transactions to be backed out unless you explicitly say otherwise
(i.e. call MQCMIT(), or in this context, the Commit() method call).
As of the 1.12 release of the MQSeries Perl API, this behavior is
under the control of the developer. The AutoCommit argument to the
object constructor is a Boolean value that specifies whether
AutoCommit is on or off. If enabled, then a pending transaction will
be committed before disconnecting. If disabled, then the transaction
will be backed out, and only if the backout succeeds will we cleanly
disconnect.
NOTE: The default behavior was backwards compatible in the 1.12
release, meaning that AutoCommit is enabled by default. However, if
you do B specify the AutoCommit behavior explicitly, then the
automatic commit of a pending transaction will generate a warning when
the object is destroyed. This is because we (the MQSeries Perl API
authors) feel that depending on this functionality is dangerous.
ANOTHER NOTE: The default behavior did change with the 1.13 release,
and AutoCommit now defaults to 0, not 1, making the intuitive behavior
the default.
=head2 Connection Timeout Support
There are known outage scenarios wherein the queue manager will be in
a "hung" state, where it is entirely unresponsive, but still up and
running. Attempts to connect to such a queue manager can block
indefinetely, with the MQCONNX() call never returning, until the queue
manager is shutdown and restarted. Normally, applications can not
trap this error, since they will be stuck in the MQCONNX() call,
forever.
By setting the ConnectTimeout argument to the MQSeries::QueueManager
constructor, a time limit on MQCONNX() can be imposed, and applications
will be able to detect this situation, and take action, if so desired.
This functionality is implemented by forking a child process, which
sleeps for the duration of the ConnectTimeout, and then sends a signal
to the parent to interrupt the MQCONNX() call. If the MQCONNX() call
succeeds before the timeout is reached, then the parent kill the child
with the same signal.
By default, SIGUSR1 is used, and the handlers are installed locally,
so there should be no conflict with any signal handlers installed by
the application, unless you really need your own SIGUSR1 to be enabled
during the MQCONNX() call. You can customize the signal used via the
ConnectTimeoutSignal argument.
If the timeout occurs, it will be considered a retryable error. (See
the next section).
NOTE: This functionality is only supported on platforms that support
fork(), and signals, of course. Win32 is not supported, since it does
not support sending signals to other processes.
=head2 Connection Retry Support
Normally, when MQCONNX() fails, the method that called it (Connect() or
new()) also fails. It is possible to have the Connect() method retry
the MQCONNX() call for a specific set of reason codes.
By default, the retry logic is disabled, but it can be enabled by
setting the RetryCount to a non-zero value. The list of reason codes
defaults to a few reasonable values, but a list of retryable codes can
be specified via the RetryReasons argument.
You are probably wondering why this logic is useful for MQCONNX(). The
choice of the default RetryReasons is not without its own reason.
Consider an application that loses its connection to its queue
manager, and thus crashes and restarts. It may very well attempt to
reconnect before the queue manager has recovered, and this support
allows the application to retry the connection for a while, until it
succeeds.
Alternately, consider an application that is started at boot time,
possible in parallel with the queue manager. If the application comes
up before the queue manager, the MQCONNX() call will fail. Retrying
this initial connection will make the application startup more robust.
This makes it easier to have applications recover from queue manager
failures, or that have more robust startup logic, but note that this
retry logic only applies to the initial connection. Reconnecting at
arbitrary points in the code is far more complex, and it left as a
(painful) exercise to the reader.
=head2 Error Handling
Most methods return a true or false value indicating success of
failure, and internally, they will call the Carp subroutine (either
Carp::Carp, or something user-defined) with a text message indicating
the cause of the failure.
In addition, the most recent MQI Completion and Reason codes will be
available via the CompCode() and Reason() methods:
$qmgr->CompCode()
$qmgr->Reason()
When distribution lists are used, then it is possible for a list of
reason codes to be returned by the API. Normally, these are buried
inside the ObjDesc strucure, but they are also available via the
$qmgr->Reasons()
method. In this case, the $queue->Reason() will always return
MQRC_MULTIPLE_REASONS. The return value of the Reasons() method is an
array reference, and each array item is a hash reference with two
keys: CompCode and Reason. These correspond, respectively, with the
CompCode and Reason associated with the individual queues in the
distribution list.
For example, the Reason code associated with the 3rd queue in the list
would be:
$qmgr->Reasons()->[2]->{Reason}
In the case of the constructor new(), which returns nothing when it
fails, these methods are not available. Most applications will not
need to handle the specific CompCode and Reason when the instantiation
fails, but if necessary, these can be obtained in one of two ways.
The older method, which is supported for backwards compabitility but
strongly discouarged, is to pass references to scalar variables to
new(). See the new() documentation above for more details.
The newer method would be to explicitly call the Open() method, and
error check it yourself. This will mean that the constructor will now
fail only if there is an error processing the constructor arguments,
as opposed to an error in the MQSeries infrastructure.
Some examples should make this clear.
The simplest way to create an MQSeries::QueueManager object is:
my $qmgr = MQSeries::QueueManager->new
(
QueueManager => 'some.queue.manager',
) || die;
But in this case, the connection to the queue manager could fail, and
your application will not be able to determine why.
In order to explicitly have access to the CompCode and Reason one
would do the following:
my $qmgr = MQSeries::QueueManager->new
(
QueueManager => 'some.queue.manager',
AutoConnect => 0,
) || die "Unable to instantiate MQSeries::QueueManager object\n";
# Call the Connect method explicitly
unless ( $qmgr->Connect() ) {
die("Connection to queue manager failed\n" .
"CompCode => " . $qmgr->CompCode() . "\n" .
"Reason => " . $qmgr->Reason() . "\n");
}
=head2 Conversion Precedence
Once you have read all the MQSeries::* documentation, you might be
confused as to how the various PutConvert/GetConvert method arguments
and constructor arguments interact with the MQSeries::Message
PutConvert() and GetConvert() methods.
The following is the precedence of the various places you can specify
a PutConvert or GetConvert subroutine, from highest to lowest:
[A] Put(), Get(), and Put1() method arguments
[B] MQSeries::Message PutConvert() and GetConvert() methods
[C] MQSeries::Queue object defaults (set as arguments to new())
[C] MQSeries::QueueManager object defaults (set as arguments to new())
The cleanest way to code these is probably (and here your mileage will
vary wildly with your tastes) to implement a subclass of
MQSeries::Message which provides the appropriate GetConvert() and
PutConvert() methods, one seperate class for each type of data
conversion which is necessary.
Then the conversion happens "under the covers" when message objects of
that class are put to or gotten from a queue.
=head1 SEE ALSO
MQSeries(3), MQSeries::Queue(3), MQSeries::Message(3), MQSeries::Properties(3)
=cut