ext/wmq_queue_manager.c in rubywmq-1.0.0 vs ext/wmq_queue_manager.c in rubywmq-1.1.1

- old
+ new

@@ -1,1581 +1,1570 @@ -/* - * Copyright 2006 J. Reid Morrison. Dimension Solutions, Inc. - * - * Licensed 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. - */ - -#include "wmq.h" - -static ID ID_open; -static ID ID_call; -static ID ID_new; -static ID ID_backout; -static ID ID_connect_options; -static ID ID_q_mgr_name; -static ID ID_queue_manager; -static ID ID_exception_on_error; -static ID ID_descriptor; -static ID ID_message; -static ID ID_trace_level; - -/* MQCD ID's */ -static ID ID_channel_name; -static ID ID_transport_type; -static ID ID_mode_name; -static ID ID_tp_name; -static ID ID_security_exit; -static ID ID_send_exit; -static ID ID_receive_exit; -static ID ID_max_msg_length; -static ID ID_security_user_data; -static ID ID_send_user_data; -static ID ID_receive_user_data; -static ID ID_user_identifier; -static ID ID_password; -static ID ID_connection_name; -static ID ID_heartbeat_interval; -static ID ID_long_remote_user_id; -static ID ID_remote_security_id; -static ID ID_ssl_cipher_spec; -static ID ID_ssl_peer_name; -static ID ID_keep_alive_interval; -static ID ID_crypto_hardware; - -/* MQSCO ID's */ -static ID ID_key_repository; - -/* Admin ID's */ -static ID ID_create_queue; -static ID ID_q_name; -static ID ID_command; - -void QueueManager_id_init(void) -{ - ID_open = rb_intern("open"); - ID_call = rb_intern("call"); - ID_new = rb_intern("new"); - ID_backout = rb_intern("backout"); - ID_q_mgr_name = rb_intern("q_mgr_name"); - ID_queue_manager = rb_intern("queue_manager"); - ID_exception_on_error = rb_intern("exception_on_error"); - ID_connect_options = rb_intern("connect_options"); - ID_trace_level = rb_intern("trace_level"); - ID_descriptor = rb_intern("descriptor"); - ID_message = rb_intern("message"); - - /* MQCD ID's */ - ID_channel_name = rb_intern("channel_name"); - ID_transport_type = rb_intern("transport_type"); - ID_mode_name = rb_intern("mode_name"); - ID_tp_name = rb_intern("tp_name"); - ID_security_exit = rb_intern("security_exit"); - ID_send_exit = rb_intern("send_exit"); - ID_receive_exit = rb_intern("receive_exit"); - ID_max_msg_length = rb_intern("max_msg_length"); - ID_security_user_data = rb_intern("security_user_data"); - ID_send_user_data = rb_intern("send_user_data"); - ID_receive_user_data = rb_intern("receive_user_data"); - ID_user_identifier = rb_intern("user_identifier"); - ID_password = rb_intern("password"); - ID_connection_name = rb_intern("connection_name"); - ID_heartbeat_interval = rb_intern("heartbeat_interval"); - ID_long_remote_user_id = rb_intern("long_remote_user_id"); - ID_remote_security_id = rb_intern("remote_security_id"); - ID_ssl_cipher_spec = rb_intern("ssl_cipher_spec"); - ID_ssl_peer_name = rb_intern("ssl_peer_name"); - ID_keep_alive_interval = rb_intern("keep_alive_interval"); - - /* MQSCO ID's */ - ID_key_repository = rb_intern("key_repository"); - ID_crypto_hardware = rb_intern("crypto_hardware"); - - /* Admin ID's */ - ID_create_queue = rb_intern("create_queue"); - ID_q_name = rb_intern("q_name"); - ID_command = rb_intern("command"); -} - -/* -------------------------------------------------- - * C Structure to store MQ data types and other - * C internal values - * --------------------------------------------------*/ -void QUEUE_MANAGER_free(void* p) -{ - PQUEUE_MANAGER pqm = (PQUEUE_MANAGER)p; - - if(pqm->trace_level>1) printf("WMQ::QueueManager Freeing QUEUE_MANAGER structure\n"); - - if (pqm->hcon && !pqm->already_connected) /* Valid MQ handle means MQDISC was not called */ - { - printf("WMQ::QueueManager#free disconnect() was not called for Queue Manager instance!!\n"); - printf("WMQ::QueueManager#free Automatically calling back() and disconnect()\n"); - pqm->MQBACK(pqm->hcon, &pqm->comp_code, &pqm->reason_code); - pqm->MQDISC(&pqm->hcon, &pqm->comp_code, &pqm->reason_code); - } - #ifdef MQCD_VERSION_6 - free(pqm->long_remote_user_id_ptr); - #endif - #ifdef MQCD_VERSION_7 - free(pqm->ssl_peer_name_ptr); - #endif - #ifdef MQHB_UNUSABLE_HBAG - if (pqm->admin_bag != MQHB_UNUSABLE_HBAG) - { - pqm->mqDeleteBag(&pqm->admin_bag, &pqm->comp_code, &pqm->reason_code); - } - - if (pqm->reply_bag != MQHB_UNUSABLE_HBAG) - { - pqm->mqDeleteBag(&pqm->reply_bag, &pqm->comp_code, &pqm->reason_code); - } - #endif - Queue_manager_mq_free(pqm); - free(pqm->p_buffer); - free(p); -} - -VALUE QUEUE_MANAGER_alloc(VALUE klass) -{ - static MQCNO default_MQCNO = {MQCNO_DEFAULT}; /* MQCONNX Connection Options */ - #ifdef MQCNO_VERSION_2 - static MQCD default_MQCD = {MQCD_CLIENT_CONN_DEFAULT}; /* Client Connection */ - #endif - #ifdef MQCNO_VERSION_4 - static MQSCO default_MQSCO = {MQSCO_DEFAULT}; - #endif - - PQUEUE_MANAGER pqm = ALLOC(QUEUE_MANAGER); - - pqm->hcon = 0; - pqm->comp_code = 0; - pqm->reason_code = 0; - pqm->exception_on_error = 1; - pqm->already_connected = 0; - pqm->trace_level = 0; - memcpy(&pqm->connect_options, &default_MQCNO, sizeof(MQCNO)); - #ifdef MQCNO_VERSION_2 - memcpy(&pqm->client_conn, &default_MQCD, sizeof(MQCD)); - - /* Tell MQ to use Client Conn structures, etc. */ - pqm->connect_options.Version = MQCNO_CURRENT_VERSION; - pqm->connect_options.ClientConnPtr = &pqm->client_conn; - #endif - #ifdef MQCNO_VERSION_4 - memcpy(&pqm->ssl_config_opts, &default_MQSCO, sizeof(MQSCO)); - #endif - #ifdef MQCD_VERSION_6 - pqm->long_remote_user_id_ptr = 0; - #endif - #ifdef MQCD_VERSION_7 - pqm->ssl_peer_name_ptr = 0; - #endif - #ifdef MQHB_UNUSABLE_HBAG - pqm->admin_bag = MQHB_UNUSABLE_HBAG; - pqm->reply_bag = MQHB_UNUSABLE_HBAG; - #endif - pqm->buffer_size = 0; - pqm->p_buffer = 0; - - pqm->is_client_conn = 0; - pqm->mq_lib_handle = 0; - - return Data_Wrap_Struct(klass, 0, QUEUE_MANAGER_free, pqm); -} - -/* - * call-seq: - * new(...) - * - * Parameters: - * * Since the number of parameters can vary dramatically, all parameters are passed by name in a hash - * * See QueueManager.new for details on all the parameters - * - * Note: - * * It is not recommended to use this method, rather use QueueManager.connect, since - * it will automatically disconnect from the queue manager. It also deals with backing out - * the current unit of work in the event of an unhandled exception. E.g. Syntax Error - * * RuntimeError and ArgumentError exceptions are always thrown regardless of the - * value of :exception_on_error - * - * Todo: - * * Support multiple send and receive exits - */ -VALUE QueueManager_initialize(VALUE self, VALUE hash) -{ - VALUE val; - VALUE str; - size_t size; - size_t length; - PQUEUE_MANAGER pqm; - - Check_Type(hash, T_HASH); - - Data_Get_Struct(self, QUEUE_MANAGER, pqm); - - WMQ_HASH2MQLONG(hash,trace_level, pqm->trace_level) - - /* @name = options[:q_mgr_name] || '' # QMGR Name optional with Client Connection */ - val = rb_hash_aref(hash, ID2SYM(ID_q_mgr_name)); - if (NIL_P(val)) - { - rb_iv_set(self, "@name", rb_str_new2("")); - if(pqm->trace_level > 1) printf("WMQ::QueueManager#initialize() Queue Manager:[Not specified, use Default QMGR]\n"); - } - else - { - rb_iv_set(self, "@name", val); - if(pqm->trace_level > 1) printf("WMQ::QueueManager#initialize() Queue Manager:%s\n", RSTRING(val)->ptr); - } - - WMQ_HASH2BOOL(hash,exception_on_error, pqm->exception_on_error) - - /* - * All Client connection parameters are ignored if connection_name is missing - */ -#ifdef MQCNO_VERSION_2 - if(!NIL_P(rb_hash_aref(hash, ID2SYM(ID_connection_name)))) - { - PMQCD pmqcd = &pqm->client_conn; /* Process MQCD */ - pqm->is_client_conn = 1; /* Set to Client connection */ - - WMQ_HASH2MQCHARS(hash,connection_name, pmqcd->ConnectionName) - WMQ_HASH2MQLONG (hash,transport_type, pmqcd->TransportType) - WMQ_HASH2MQCHARS(hash,mode_name, pmqcd->ModeName) - WMQ_HASH2MQCHARS(hash,tp_name, pmqcd->TpName) - WMQ_HASH2MQCHARS(hash,security_exit, pmqcd->SecurityExit) - WMQ_HASH2MQCHARS(hash,send_exit, pmqcd->SendExit) - WMQ_HASH2MQCHARS(hash,receive_exit, pmqcd->ReceiveExit) - WMQ_HASH2MQLONG (hash,max_msg_length, pmqcd->MaxMsgLength) - WMQ_HASH2MQCHARS(hash,security_user_data, pmqcd->SecurityUserData) - WMQ_HASH2MQCHARS(hash,send_user_data, pmqcd->SendUserData) - WMQ_HASH2MQCHARS(hash,receive_user_data, pmqcd->ReceiveUserData) - WMQ_HASH2MQCHARS(hash,user_identifier, pmqcd->UserIdentifier) - WMQ_HASH2MQCHARS(hash,password, pmqcd->Password) - - /* Default channel name to system default */ - val = rb_hash_aref(hash, ID2SYM(ID_channel_name)); - if (NIL_P(val)) - { - strncpy(pmqcd->ChannelName, "SYSTEM.DEF.SVRCONN", sizeof(pmqcd->ChannelName)); - } - else - { - WMQ_HASH2MQCHARS(hash,channel_name, pmqcd->ChannelName) - } - - #ifdef MQCD_VERSION_4 - WMQ_HASH2MQLONG(hash,heartbeat_interval, pmqcd->HeartbeatInterval) - /* TODO: - WMQ_HASH2MQLONG(hash,exit_name_length, pmqcd->ExitNameLength) - WMQ_HASH2MQLONG(hash,exit_data_length, pmqcd->ExitDataLength) - WMQ_HASH2MQLONG(hash,send_exits_defined, pmqcd->SendExitsDefined) - WMQ_HASH2MQLONG(hash,receive_exits_defined, pmqcd->ReceiveExitsDefined) - TO_PTR (send_exit_ptr, pmqcd->SendExitPtr) - TO_PTR (send_user_data_ptr, pmqcd->SendUserDataPtr) - TO_PTR (receive_exit_ptr, pmqcd->ReceiveExitPtr) - TO_PTR (receive_user_data_ptr, pmqcd->ReceiveUserDataPtr) - */ - #endif - #ifdef MQCD_VERSION_6 - val = rb_hash_aref(hash, ID2SYM(ID_long_remote_user_id)); - if (!NIL_P(val)) - { - str = StringValue(val); - length = RSTRING(str)->len; - - if (length > 0) - { - MQPTR pBuffer; - if(pqm->trace_level > 1) - printf("WMQ::QueueManager#initialize() Setting long_remote_user_id:%s\n", - RSTRING(str)->ptr); - - /* Include null at end of string */ - pBuffer = ALLOC_N(char, length+1); - memcpy(pBuffer, RSTRING(str)->ptr, length+1); - - pmqcd->LongRemoteUserIdLength = length; - pmqcd->LongRemoteUserIdPtr = pBuffer; - pqm->long_remote_user_id_ptr = pBuffer; - } - } - WMQ_HASH2MQBYTES(hash,remote_security_id, pmqcd->RemoteSecurityId) - WMQ_HASH2MQCHARS(hash,ssl_cipher_spec, pmqcd->SSLCipherSpec) - #endif - #ifdef MQCD_VERSION_7 - val = rb_hash_aref(hash, ID2SYM(ID_ssl_peer_name)); - if (!NIL_P(val)) - { - str = StringValue(val); - length = RSTRING(str)->len; - - if (length > 0) - { - MQPTR pBuffer; - if(pqm->trace_level > 1) - printf("WMQ::QueueManager#initialize() Setting ssl_peer_name:%s\n", - RSTRING(str)->ptr); - - /* Include null at end of string */ - pBuffer = ALLOC_N(char, length+1); - memcpy(pBuffer, RSTRING(str)->ptr, length+1); - - pmqcd->SSLPeerNameLength = length; - pmqcd->SSLPeerNamePtr = pBuffer; - pqm->ssl_peer_name_ptr = pBuffer; - } - } - WMQ_HASH2MQLONG(hash,keep_alive_interval, pmqcd->KeepAliveInterval) - - /* Only set if SSL options are supplied, otherwise - * environment variables are ignored: MQSSLKEYR and MQSSLCRYP - * Any SSL info in the client channel definition tables is also ignored - */ - if (!NIL_P(rb_hash_aref(hash, ID2SYM(ID_key_repository))) || - !NIL_P(rb_hash_aref(hash, ID2SYM(ID_crypto_hardware)))) - { - /* Process MQSCO */ - WMQ_HASH2MQCHARS(hash,key_repository, pqm->ssl_config_opts.KeyRepository) - WMQ_HASH2MQCHARS(hash,crypto_hardware, pqm->ssl_config_opts.CryptoHardware) - - pqm->connect_options.SSLConfigPtr = &pqm->ssl_config_opts; - } - #endif - - } - else - { - pqm->is_client_conn = 0; /* Set to Server connection */ - } -#endif - -#ifdef MQCNO_VERSION_4 - /* Process MQCNO */ - WMQ_HASH2MQLONG(hash,connect_options, pqm->connect_options.Options) -#endif - - /* -------------------------------------------------- - * TODO: MQAIR Structure - LDAP Security - * --------------------------------------------------*/ - - return Qnil; -} - -/* - * Before working with any queues, it is necessary to connect - * to the queue manager. - * - * Returns: - * * true : On Success - * * false: On Failure - * - * comp_code and reason_code are also updated. - * reason will return a text description of the reason_code - * - * Throws: - * * WMQ::WMQException if comp_code != MQCC_OK - * * Except if :exception_on_error => false was supplied as a parameter - * to QueueManager.new - */ -VALUE QueueManager_connect(VALUE self) -{ - VALUE name; - - PQUEUE_MANAGER pqm; - Data_Get_Struct(self, QUEUE_MANAGER, pqm); - pqm->already_connected = 0; - - Queue_manager_mq_load(pqm); /* Load MQ Library */ - - name = rb_iv_get(self,"@name"); - name = StringValue(name); - - if(pqm->trace_level) - printf("WMQ::QueueManager#connect() Connect to Queue Manager:%s\n", RSTRING(name)->ptr); - - if (pqm->hcon) /* Disconnect from qmgr if already connected, ignore errors */ - { - if(pqm->trace_level) - printf("WMQ::QueueManager#connect() Already connected to Queue Manager:%s, Disconnecting first!\n", RSTRING(name)->ptr); - - pqm->MQDISC(&pqm->hcon, &pqm->comp_code, &pqm->reason_code); - } - - pqm->MQCONNX( - RSTRING(name)->ptr, /* queue manager */ - &pqm->connect_options, /* Connection Options */ - &pqm->hcon, /* connection handle */ - &pqm->comp_code, /* completion code */ - &pqm->reason_code); /* connect reason code */ - - if(pqm->trace_level) - printf("WMQ::QueueManager#connect() MQCONNX completed with reason:%s, Handle:%ld\n", - wmq_reason(pqm->reason_code), - (long)pqm->hcon); - - if (pqm->comp_code == MQCC_FAILED) - { - pqm->hcon = 0; - - if (pqm->exception_on_error) - { - rb_raise(wmq_exception, - "WMQ::QueueManager#connect(). Error connecting to Queue Manager:%s, reason:%s", - RSTRING(name)->ptr, - wmq_reason(pqm->reason_code)); - } - - return Qfalse; - } - - if (pqm->reason_code == MQRC_ALREADY_CONNECTED) - { - if(pqm->trace_level) printf("WMQ::QueueManager#connect() Already connected\n"); - pqm->already_connected = 1; - } - - return Qtrue; -} - -/* - * Disconnect from this QueueManager instance - * - * Returns: - * * true : On Success - * * false: On Failure - * - * comp_code and reason_code are also updated. - * reason will return a text description of the reason_code - * - * Throws: - * * WMQ::WMQException if comp_code != MQCC_OK - * * Except if :exception_on_error => false was supplied as a parameter - * to QueueManager.new - */ -VALUE QueueManager_disconnect(VALUE self) -{ - PQUEUE_MANAGER pqm; - Data_Get_Struct(self, QUEUE_MANAGER, pqm); - - if(pqm->trace_level) printf ("WMQ::QueueManager#disconnect() Queue Manager Handle:%ld\n", (long)pqm->hcon); - - if (!pqm->already_connected) - { - pqm->MQDISC(&pqm->hcon, &pqm->comp_code, &pqm->reason_code); - - if(pqm->trace_level) printf("WMQ::QueueManager#disconnect() MQDISC completed with reason:%s\n", wmq_reason(pqm->reason_code)); - - if (pqm->comp_code != MQCC_OK) - { - if (pqm->exception_on_error) - { - VALUE name = rb_iv_get(self,"@name"); - name = StringValue(name); - - rb_raise(wmq_exception, - "WMQ::QueueManager#disconnect(). Error disconnecting from Queue Manager:%s, reason:%s", - RSTRING(name)->ptr, - wmq_reason(pqm->reason_code)); - } - - return Qfalse; - } - } - else - { - pqm->comp_code = 0; - pqm->reason_code = 0; - - if(pqm->trace_level) printf ("WMQ::QueueManager#disconnect() Not calling MQDISC, since already connected on connect\n"); - } - - pqm->hcon = 0; - - return Qtrue; -} - -/* - * Commit the current unit of work for this QueueManager instance - * - * Note: - * * commit will have no effect if all put and get operations were performed - * without specifying :sync => true - * - * Returns: - * * true : On Success - * * false: On Failure - * - * comp_code and reason_code are also updated. - * reason will return a text description of the reason_code - * - * Throws: - * * WMQ::WMQException if comp_code != MQCC_OK - * * Except if :exception_on_error => false was supplied as a parameter - * to QueueManager.new - */ -VALUE QueueManager_commit(VALUE self) -{ - PQUEUE_MANAGER pqm; - Data_Get_Struct(self, QUEUE_MANAGER, pqm); - - if(pqm->trace_level) printf ("WMQ::QueueManager#commit() Queue Manager Handle:%ld\n", (long)pqm->hcon); - - pqm->MQCMIT(pqm->hcon, &pqm->comp_code, &pqm->reason_code); - - if(pqm->trace_level) printf("WMQ::QueueManager#commit() MQCMIT completed with reason:%s\n", wmq_reason(pqm->reason_code)); - - if (pqm->comp_code != MQCC_OK) - { - if (pqm->exception_on_error) - { - VALUE name = rb_iv_get(self,"@name"); - name = StringValue(name); - - rb_raise(wmq_exception, - "WMQ::QueueManager#commit(). Error commiting changes to Queue Manager:%s, reason:%s", - RSTRING(name)->ptr, - wmq_reason(pqm->reason_code)); - } - return Qfalse; - } - - return Qtrue; -} - -/* - * Backout the current unit of work for this QueueManager instance - * - * Since the last commit or rollback any messages put to a queue - * under synchpoint will be removed and any messages retrieved - * under synchpoint from any queues will be returned - * - * Note: - * * backout will have no effect if all put and get operations were performed - * without specifying :sync => true - * - * Returns: - * * true : On Success - * * false: On Failure - * - * comp_code and reason_code are also updated. - * reason will return a text description of the reason_code - * - * Throws: - * * WMQ::WMQException if comp_code != MQCC_OK - * * Except if :exception_on_error => false was supplied as a parameter - * to QueueManager.new - */ -VALUE QueueManager_backout(VALUE self) -{ - PQUEUE_MANAGER pqm; - Data_Get_Struct(self, QUEUE_MANAGER, pqm); - - if(pqm->trace_level) printf ("WMQ::QueueManager#backout() Queue Manager Handle:%ld\n", (long)pqm->hcon); - - pqm->MQBACK(pqm->hcon, &pqm->comp_code, &pqm->reason_code); - - if(pqm->trace_level) printf("WMQ::QueueManager#backout() MQBACK completed with reason:%s\n", wmq_reason(pqm->reason_code)); - - if (pqm->comp_code != MQCC_OK) - { - if (pqm->exception_on_error) - { - VALUE name = rb_iv_get(self,"@name"); - name = StringValue(name); - - rb_raise(wmq_exception, - "WMQ::QueueManager#backout(). Error backing out changes to Queue Manager:%s, reason:%s", - RSTRING(name)->ptr, - wmq_reason(pqm->reason_code)); - } - return Qfalse; - } - - return Qtrue; -} - -/* - * Advanced WebSphere MQ Use: - * - * Begin a unit of work between this QueueManager instance and another - * resource such as a Database - * - * Starts a new unit of work under which put and get can be called with - * with the parameter :sync => true - * - * Returns: - * * true : On Success - * * false: On Failure - * - * comp_code and reason_code are also updated. - * reason will return a text description of the reason_code - * - * Throws: - * * WMQ::WMQException if comp_code != MQCC_OK - * * Except if :exception_on_error => false was supplied as a parameter - * to QueueManager.new - */ -VALUE QueueManager_begin(VALUE self) -{ - PQUEUE_MANAGER pqm; - Data_Get_Struct(self, QUEUE_MANAGER, pqm); - - if(pqm->trace_level) printf ("WMQ::QueueManager#begin() Queue Manager Handle:%ld\n", (long)pqm->hcon); - - pqm->MQBEGIN(pqm->hcon, 0, &pqm->comp_code, &pqm->reason_code); - - if(pqm->trace_level) printf("WMQ::QueueManager#begin() MQBEGIN completed with reason:%s\n", wmq_reason(pqm->reason_code)); - - if (pqm->comp_code != MQCC_OK) - { - if (pqm->exception_on_error) - { - VALUE name = rb_iv_get(self,"@name"); - name = StringValue(name); - - rb_raise(wmq_exception, - "WMQ::QueueManager#begin(). Error starting unit of work on Queue Manager:%s, reason:%s", - RSTRING(name)->ptr, - wmq_reason(pqm->reason_code)); - } - return Qfalse; - } - - return Qtrue; -} - -/* - * call-seq: - * put(parameters) - * - * Put a message to the queue without having to first open the queue - * Recommended for reply queues that change frequently - * - * * parameters: a Hash consisting of one or more of the following parameters - * - * Summary of parameters and their WebSphere MQ equivalents - * queue.get( # WebSphere MQ Equivalents: - * :q_name => 'Queue Name', # MQOD.ObjectName - * :q_name => { queue_manager=>'QMGR_name', # MQOD.ObjectQMgrName - * q_name =>'q_name'} - * :message => my_message, # n/a : Instance of Message - * :data => "Hello World", # n/a : Data to send - * :sync => false, # MQGMO_SYNCPOINT - * :new_id => true, # MQPMO_NEW_MSG_ID & MQPMO_NEW_CORREL_ID - * :new_msg_id => true, # MQPMO_NEW_MSG_ID - * :new_correl_id => true, # MQPMO_NEW_CORREL_ID - * :fail_if_quiescing => true, # MQOO_FAIL_IF_QUIESCING - * :options => WMQ::MQPMO_FAIL_IF_QUIESCING # MQPMO_* - * ) - * - * Mandatory Parameters - * * :q_name => String - * * Name of the existing WebSphere MQ local queue, model queue or remote queue to open - * * To open remote queues for which a local remote queue definition is not available - * pass a Hash as q_name (see q_name => Hash) - * OR - * * :q_name => Hash - * * q_name => String - * * Name of the existing WebSphere MQ local queue, model queue or remote queue to open - * * :q_mgr_name => String - * * Name of the remote WebSphere MQ queue manager to send the message to. - * * This allows a message to be written to a queue on a remote queue manager - * where a remote queue definition is not defined locally - * * Commonly used to reply to messages from remote systems - * * If q_mgr_name is the same as the local queue manager name then the message - * is merely written to the local queue. - * * Note: q_mgr_name should only be supplied when putting messages to the queue. - * It is not possible to get messages from a queue on a queue manager other - * than the currently connected queue manager - * - * * Either :message or :data must be supplied - * * If both are supplied, then :data will be written to the queue. The data in :message - * will be ignored - * - * Optional Parameters - * * :data => String - * * Data to be written to the queue. Can be binary or text data - * - * * :message => Message - * * An instance of the WMQ::Message - * * The Message descriptor, headers and data is retrieved from :message - * * message.data is ignored if :data is supplied - * - * * :sync => true or false - * * Determines whether the get is performed under synchpoint. - * I.e. Under the current unit of work - * Default: false - * - * * :new_id => true or false - * * Generate a new message id and correlation id for this - * message. :new_msg_id and :new_correl_id will be ignored - * if this parameter is true - * Default: false - * - * * :new_msg_id => true or false - * * Generate a new message id for this message - * * Note: A blank message id will result in a new message id anyway. - * However, for subsequent puts using the same message descriptor, the same - * message id will be used. - * Default: false - * - * * :new_correl_id => true or false - * * Generate a new correlation id for this message - * Default: false - * - * * :fail_if_quiescing => true or false - * * Determines whether the WMQ::Queue#put call will fail if the queue manager is - * in the process of being quiesced. - * * Note: This interface differs from other WebSphere MQ interfaces, - * they do not default to true. - * Default: true - * Equivalent to: MQGMO_FAIL_IF_QUIESCING - * - * * Note: As part of the application design, carefull consideration - * should be given as to when to allow a transaction or - * unit of work to complete or fail under this condition. - * As such it is important to include this option where - * appropriate so that MQ Administrators can shutdown the - * queue managers without having to resort to the 'immediate' - * shutdown option. - * - * * :options => Fixnum (Advanced MQ Use only) - * * Numeric field containing any of the MQ Put message options or'd together - * * E.g. :options => WMQ::MQPMO_PASS_IDENTITY_CONTEXT | WMQ::MQPMO_ALTERNATE_USER_AUTHORITY - * * Note: If :options is supplied, it is applied first, then the above parameters are - * applied afterwards. - * * One or more of the following values: - * WMQ::MQPMO_NO_SYNCPOINT - * WMQ::MQPMO_LOGICAL_ORDER - * WMQ::MQPMO_NO_CONTEXT - * WMQ::MQPMO_DEFAULT_CONTEXT - * WMQ::MQPMO_PASS_IDENTITY_CONTEXT - * WMQ::MQPMO_PASS_ALL_CONTEXT - * WMQ::MQPMO_SET_IDENTITY_CONTEXT - * WMQ::MQPMO_SET_ALL_CONTEXT - * WMQ::MQPMO_ALTERNATE_USER_AUTHORITY - * WMQ::MQPMO_RESOLVE_LOCAL_Q - * WMQ::MQPMO_NONE - * * Please see the WebSphere MQ documentation for more details on the above options - * Default: WMQ::MQPMO_NONE - * - * Returns: - * * true : On Success - * * false: On Failure - * - * comp_code and reason_code are also updated. - * reason will return a text description of the reason_code - * - * Throws: - * * WMQ::WMQException if comp_code == MQCC_FAILED - * * Except if :exception_on_error => false was supplied as a parameter - * to QueueManager.new - */ -VALUE QueueManager_put(VALUE self, VALUE hash) -{ - MQLONG BufferLength; /* Length of the message in Buffer */ - PMQVOID pBuffer; /* Message data */ - MQMD md = {MQMD_DEFAULT}; /* Message Descriptor */ - MQPMO pmo = {MQPMO_DEFAULT}; /* put message options */ - MQOD od = {MQOD_DEFAULT}; /* Object Descriptor */ - VALUE q_name; - VALUE str; - size_t size; - size_t length; - VALUE val; - - PQUEUE_MANAGER pqm; - Data_Get_Struct(self, QUEUE_MANAGER, pqm); - - Check_Type(hash, T_HASH); - - q_name = rb_hash_aref(hash, ID2SYM(ID_q_name)); - - if (NIL_P(q_name)) - { - rb_raise(rb_eArgError, - "Mandatory parameter :q_name is missing from WMQ::QueueManager::put1()"); - } - - /* -------------------------------------------------- - * If :q_name is a hash, extract :q_name and :q_mgr_name - * --------------------------------------------------*/ - if(TYPE(q_name) == T_HASH) - { - WMQ_HASH2MQCHARS(q_name, q_mgr_name, od.ObjectQMgrName) - - q_name = rb_hash_aref(val, ID2SYM(ID_q_name)); - if (NIL_P(q_name)) - { - rb_raise(rb_eArgError, - "Mandatory parameter :q_name missing from :q_name hash passed to WMQ::QueueManager#put"); - } - } - - WMQ_STR2MQCHARS(q_name,od.ObjectName) - - Queue_extract_put_message_options(hash, &pmo); - Message_build(&pqm->p_buffer, &pqm->buffer_size, pqm->trace_level, - hash, &pBuffer, &BufferLength, &md); - - if(pqm->trace_level) printf("WMQ::QueueManager#put Queue Manager Handle:%ld\n", (long)pqm->hcon); - - pqm->MQPUT1( - pqm->hcon, /* connection handle */ - &od, /* object descriptor */ - &md, /* message descriptor */ - &pmo, /* put message options */ - BufferLength, /* message length */ - pBuffer, /* message buffer */ - &pqm->comp_code, /* completion code */ - &pqm->reason_code); /* reason code */ - - if(pqm->trace_level) printf("WMQ::QueueManager#put MQPUT1 ended with reason:%s\n", wmq_reason(pqm->reason_code)); - - if (pqm->reason_code != MQRC_NONE) - { - if (pqm->exception_on_error) - { - VALUE qmgr_name = QueueManager_name(self); - - rb_raise(wmq_exception, - "WMQ::QueueManager.put(). Error writing a message to Queue:%s on Queue Manager:%s reason:%s", - RSTRING(q_name)->ptr, - RSTRING(qmgr_name)->ptr, - wmq_reason(pqm->reason_code)); - } - return Qfalse; - } - else - { - VALUE message = rb_hash_aref(hash, ID2SYM(ID_message)); - if(!NIL_P(message)) - { - VALUE descriptor = rb_funcall(message, ID_descriptor, 0); - Message_from_mqmd(descriptor, &md); /* This could be optimized to output only fields */ - } - } - - return Qtrue; -} - -/* - * Return the completion code for the last MQ operation - * - * Returns => FixNum - * * WMQ::MQCC_OK 0 - * * WMQ::MQCC_WARNING 1 - * * WMQ::MQCC_FAILED 2 - * * WMQ::MQCC_UNKNOWN -1 - * - */ -VALUE QueueManager_comp_code(VALUE self) -{ - PQUEUE_MANAGER pqm; - Data_Get_Struct(self, QUEUE_MANAGER, pqm); - return LONG2NUM(pqm->comp_code); -} - -/* - * Return the reason code for the last MQ operation - * - * Returns => FixNum - * * For a complete list of reason codes, please see WMQ Constants or - * the WebSphere MQ documentation for Reason Codes - * - * Note - * * The list of Reason Codes varies depending on the version of WebSphere MQ - * and the operating system on which Ruby WMQ was compiled - */ -VALUE QueueManager_reason_code(VALUE self) -{ - PQUEUE_MANAGER pqm; - Data_Get_Struct(self, QUEUE_MANAGER, pqm); - return LONG2NUM(pqm->reason_code); -} - -/* - * Returns a textual representation of the reason_code for the last MQ operation - * - * Returns => String - * * For a complete list of reasons, please see WMQ Constants or - * the WebSphere MQ documentation for Reason Codes - * - * Note - * * The list of Reason Codes varies depending on the version of WebSphere MQ - * and the operating system on which Ruby WMQ was compiled - */ -VALUE QueueManager_reason(VALUE self) -{ - PQUEUE_MANAGER pqm; - Data_Get_Struct(self, QUEUE_MANAGER, pqm); - return rb_str_new2(wmq_reason(pqm->reason_code)); -} - -/* - * Returns whether this QueueManager instance is set - * to throw a WMQ::WMQException whenever an MQ operation fails - * - * Returns: - * * true : This QueueManager instance will throw a WMQ::WMQException whenever - * an MQ operation fails. I.e. if comp_code != WMQ::OK. - * * false: WMQ::WMQException will not be thrown - * - * Note: - * * RuntimeError and ArgumentError exceptions are always thrown regardless of the - * value of exception_on_error - */ -VALUE QueueManager_exception_on_error(VALUE self) -{ - PQUEUE_MANAGER pqm; - Data_Get_Struct(self, QUEUE_MANAGER, pqm); - if (pqm->exception_on_error) - { - return Qtrue; - } - - return Qfalse; -} - -/* - * Returns whether this QueueManager instance is currently - * connected to a WebSphere MQ queue manager - * - * Returns: - * * true : This QueueManager instance is connected to a local or remote queue manager - * * false: This QueueManager instance is not currently connected to a local or - * remote queue manager - */ -VALUE QueueManager_connected_q(VALUE self) -{ - PQUEUE_MANAGER pqm; - Data_Get_Struct(self, QUEUE_MANAGER, pqm); - if (pqm->hcon) - { - return Qtrue; - } - return Qfalse; -} - -/* - * Returns the QueueManager name => String - */ -VALUE QueueManager_name(VALUE self) -{ - return rb_iv_get(self,"@name"); -} - -static VALUE QueueManager_open_queue_block(VALUE message, VALUE proc) -{ - return rb_funcall(proc, ID_call, 1, message); -} - -static VALUE QueueManager_open_queue_each(VALUE parameters) -{ - return Queue_singleton_open(1, &parameters, wmq_queue); -} - -/* - * call-seq: - * open_queue(...) - * access_queue(...) - * - * Open the specified queue, then close it once the - * supplied code block has completed - * - * Parameters: - * * Since the number of parameters can vary dramatically, all parameters are passed by name in a hash - * * See Queue.open for the complete list of parameters, except that :queue_manager is *not* required - * since it is supplied automatically by this method - * - * Example: - * require 'wmq/wmq_client' - * - * WMQ::QueueManager.connect(:q_mgr_name=>'REID', :connection_name=>'localhost(1414)') do |qmgr| - * qmgr.open_queue(:q_name=>'TEST.QUEUE', :mode=>:output) do |queue| - * queue.put(:data => 'Hello World') - * end - * end - */ -VALUE QueueManager_open_queue(int argc, VALUE *argv, VALUE self) -{ - VALUE parameters; - VALUE proc; - - /* Extract parameters and code block (Proc) */ - rb_scan_args(argc, argv, "1&", &parameters, &proc); - - Check_Type(parameters, T_HASH); - rb_hash_aset(parameters, ID2SYM(ID_queue_manager), self); - - return rb_iterate(QueueManager_open_queue_each, parameters, QueueManager_open_queue_block, proc); -} - -struct QueueManager_singleton_connect_arg { - VALUE self; - VALUE proc; -}; - -static VALUE QueueManager_singleton_connect_body2(struct QueueManager_singleton_connect_arg* arg) -{ - return rb_funcall(arg->proc, ID_call, 1, arg->self); -} - -static VALUE QueueManager_singleton_connect_rescue(VALUE self) -{ - PQUEUE_MANAGER pqm; - VALUE exception; - Data_Get_Struct(self, QUEUE_MANAGER, pqm); - - if(pqm->trace_level) printf("WMQ::QueueManager.connect() Backing out due to unhandled exception\n"); - //exception = rb_gvar_get("$!"); - exception = rb_eval_string("$!"); /* $! holds the current exception */ - if(pqm->trace_level > 1) printf("WMQ::QueueManager.connect() Exception $! extracted\n"); - QueueManager_backout(self); /* Backout Unit of work */ - if(pqm->trace_level > 1) printf("WMQ::QueueManager.connect() Rethrowing Exception\n"); - rb_exc_raise(exception); /* Re-Raise Exception */ - return Qnil; -} - -static VALUE QueueManager_singleton_connect_body(struct QueueManager_singleton_connect_arg* arg) -{ - return rb_rescue2(QueueManager_singleton_connect_body2, (VALUE)arg, - QueueManager_singleton_connect_rescue, arg->self, - rb_eException, 0); -} - -static VALUE QueueManager_singleton_connect_ensure(VALUE self) -{ - return QueueManager_disconnect(self); -} - -/* - * call-seq: - * connect(...) - * - * Connect to the queue manager, then disconnect once the supplied code block completes - * - * Parameters: - * * Since the number of parameters can vary dramatically, all parameters are passed by name in a hash - * * Summary of parameters and their WebSphere MQ equivalents: - * WMQ::QueueManager.connect( # WebSphere MQ Equivalents: - * :q_mgr_name => 'queue_manager name', - * :exception_on_error => true, # n/a - * :connect_options => WMQ::MQCNO_FASTBATH_BINDING # MQCNO.Options - * - * :trace_level => 0, # n/a - * - * # Common client connection parameters - * :channel_name => 'svrconn channel name', # MQCD.ChannelName - * :connection_name => 'localhost(1414)', # MQCD.ConnectionName - * :transport_type => WMQ::MQXPT_TCP, # MQCD.TransportType - * - * # Advanced client connections parameters - * :max_msg_length => 65535, # MQCD.MaxMsgLength - * :security_exit => 'Name of security exit', # MQCD.SecurityExit - * :send_exit => 'Name of send exit', # MQCD.SendExit - * :receive_exit => 'Name of receive exit', # MQCD.ReceiveExit - * :security_user_data => 'Security exit User data', # MQCD.SecurityUserData - * :send_user_data => 'Send exit user data', # MQCD.SendUserData - * :receive_user_data => 'Receive exit user data', # MQCD.ReceiveUserData - * :heartbeat_interval => 1, # MQCD.HeartbeatInterval - * :remote_security_id => 'Remote Security id', # MQCD.RemoteSecurityId - * :ssl_cipher_spec => 'SSL Cipher Spec', # MQCD.SSLCipherSpec - * :keep_alive_interval=> -1, # MQCD.KeepAliveInterval - * :mode_name => 'LU6.2 Mode Name', # MQCD.ModeName - * :tp_name => 'LU6.2 Transaction pgm name', # MQCD.TpName - * :user_identifier => 'LU 6.2 Userid', # MQCD.UserIdentifier - * :password => 'LU6.2 Password', # MQCD.Password - * :long_remote_user_id=> 'Long remote user identifier', # MQCD.LongRemoteUserId (Ptr, Length) - * :ssl_peer_name => 'SSL Peer name', # MQCD.SSLPeerName (Ptr, Length) - * - * # SSL Options - * :key_repository => '/var/mqm/qmgrs/.../key', # MQSCO.KeyRepository - * :crypto_hardware => 'GSK_ACCELERATOR_NCIPHER_NF_ON', # MQSCO.CryptoHardware - * ) - * - * Optional Parameters - * * :q_mgr_name => String - * * Name of the existing WebSphere MQ Queue Manager to connect to - * - * * Default: - * - Server connections will connect to the default queue manager - * - Client connections will connect to whatever queue - * manager is found at the host and port number as specified - * by the connection_name - * - * * :exception_on_error => true or false - * Determines whether WMQ::WMQExceptions are thrown whenever - * an error occurs during a WebSphere MQ operation (connect, put, get, etc..) - * - * Default: true - * - * * :connect_options => FixNum - * * One or more of the following values: - * WMQ::MQCNO_STANDARD_BINDING - * WMQ::MQCNO_FASTPATH_BINDING - * WMQ::MQCNO_SHARED_BINDING - * WMQ::MQCNO_ISOLATED_BINDING - * WMQ::MQCNO_ACCOUNTING_MQI_ENABLED - * WMQ::MQCNO_ACCOUNTING_MQI_DISABLED - * WMQ::MQCNO_ACCOUNTING_Q_ENABLED - * WMQ::MQCNO_ACCOUNTING_Q_DISABLED - * WMQ::MQCNO_NONE - * - * * Multiple values can be or'd together. E.g. - * :connect_options=>WMQ::MQCNO_FASTPATH_BINDING | WMQ::MQCNO_ACCOUNTING_MQI_ENABLED - * - * * Please see the WebSphere MQ MQCNO data type documentation for more details - * Default: WMQ::MQCNO_NONE - * - * * :trace_level => FixNum - * * Turns on low-level tracing of the WebSphere MQ API calls to stdout. - * * 0: No tracing - * * 1: MQ API tracing only (MQCONNX, MQOPEN, MQPUT, etc..) - * * 2: Include Ruby WMQ tracing - * * 3: Verbose logging (Recommended for when reporting problems in Ruby WMQ) - * Default: 0 - * - * Common Client Connection Parameters (Client connections only) - * * :connection_name => String (Mandatory for client connections) - * * Connection name, made up of the host name (or ip address) and the port number - * * E.g. - * 'mymachine.domain.com(1414)' - * '192.168.0.1(1417)' - * - * * :channel_name => String - * * Name of SVRCONN channel defined on the QueueManager for Client Connections - * * Default Value: - * 'SYSTEM.DEF.SVRCONN' - * - * * :transport_type => WMQ::MQXPT_TCP, # MQCD.TransportType - * * Valid Values: - * WMQ::MQXPT_LOCAL - * WMQ::MQXPT_LU62 - * WMQ::MQXPT_TCP - * WMQ::MQXPT_NETBIOS - * WMQ::MQXPT_SPX - * WMQ::MQXPT_DECNET - * WMQ::MQXPT_UDP - * - * * Default Value: - * WMQ::MQXPT_TCP - * - * For the Advanced Client Connection parameters, please see the WebSphere MQ documentation - * - * Note: - * * If an exception is not caught in the code block, the current unit of work is - * automatically backed out, before disconnecting from the queue manager. - * - * Local Server Connection Example: - * require 'wmq/wmq' - * - * WMQ::QueueManager.connect(:q_mgr_name=>'REID') do |qmgr| - * qmgr.put(:q_name=>'TEST.QUEUE', :data => 'Hello World') - * end - * - * Client Connection Example: - * require 'wmq/wmq_client' - * - * WMQ::QueueManager.connect( - * :channel_name => 'SYSTEM.DEF.SVRCONN', - * :transport_type => WMQ::MQXPT_TCP, - * :connection_name => 'localhost(1414)' ) do |qmgr| - * qmgr.open_queue(:q_name=>'TEST.QUEUE', :mode=>:input) do |queue| - * - * message = WMQ::Message.new - * if queue.get(:message => message) - * puts "Data Received: #{message.data}" - * else - * puts 'No message available' - * end - * end - * end - */ -VALUE QueueManager_singleton_connect(int argc, VALUE *argv, VALUE self) -{ - VALUE proc, parameters, queue_manager; - - /* Extract parameters and code block (Proc) */ - rb_scan_args(argc, argv, "1&", &parameters, &proc); - - queue_manager = rb_funcall(wmq_queue_manager, ID_new, 1, parameters); - if(!NIL_P(proc)) - { - if(Qtrue == QueueManager_connect(queue_manager)) - { - struct QueueManager_singleton_connect_arg arg; - arg.self = queue_manager; - arg.proc = proc; - rb_ensure(QueueManager_singleton_connect_body, (VALUE)&arg, - QueueManager_singleton_connect_ensure, queue_manager); - } - else - { - return Qfalse; - } - } - return queue_manager; -} - -/* What can I say, Ruby blocks have spoilt me :) */ -#define CHECK_COMPLETION_CODE(ACTION) \ -if(pqm->trace_level > 1) \ - printf ("WMQ::QueueManager#execute() %s:%s\n", ACTION, wmq_reason(pqm->reason_code)); \ -if(pqm->comp_code != MQCC_OK) \ -{ \ - if (pqm->exception_on_error) \ - { \ - if(pqm->trace_level) \ - printf ("WMQ::QueueManager#execute() raise WMQ::WMQException\n"); \ - \ - rb_raise(wmq_exception, \ - "WMQ::QueueManager#execute(). Failed:%s, reason:%s", \ - ACTION, \ - wmq_reason(pqm->reason_code)); \ - } \ - return Qfalse; \ -} - -#if RUBY_VERSION_CODE > 183 -static int QueueManager_execute_each (VALUE key, VALUE value, PQUEUE_MANAGER pqm) -{ -#else -static int QueueManager_execute_each (VALUE array, PQUEUE_MANAGER pqm) -{ - VALUE key = rb_ary_shift(array); - VALUE value = rb_ary_shift(array); -#endif - MQLONG selector_type, selector; - VALUE str; - ID selector_id = rb_to_id(key); - - if(pqm->trace_level > 1) - { - str = rb_funcall(key, rb_intern("to_s"), 0); - printf ("WMQ::QueueManager#execute_each Key:%s\n", RSTRING(str)->ptr); - } - - if (ID_command == selector_id) // Skip :command - { - return 0; - } - - wmq_selector(selector_id, &selector_type, &selector); - if(NIL_P(value)) - { - pqm->mqAddInquiry(pqm->admin_bag, selector, &pqm->comp_code, &pqm->reason_code); - CHECK_COMPLETION_CODE("Adding Inquiry to the admin bag") - return 0; - } - - switch (selector_type) - { - case MQIT_INTEGER: - if(TYPE(value) == T_SYMBOL) /* Translate symbol to MQ selector value */ - { - MQLONG val_selector, val_selector_type; - wmq_selector(rb_to_id(value), &val_selector_type, &val_selector); - - pqm->mqAddInteger(pqm->admin_bag, selector, val_selector, &pqm->comp_code, &pqm->reason_code); - } - else - { - pqm->mqAddInteger(pqm->admin_bag, selector, NUM2LONG(value), &pqm->comp_code, &pqm->reason_code); - } - CHECK_COMPLETION_CODE("Adding Queue Type to the admin bag") - break; - - case MQIT_STRING: - str = StringValue(value); - pqm->mqAddString(pqm->admin_bag, selector, MQBL_NULL_TERMINATED, RSTRING(str)->ptr, &pqm->comp_code, &pqm->reason_code); - CHECK_COMPLETION_CODE("Adding Queue name to the admin bag") - break; - - default: - rb_raise(rb_eArgError, "WMQ::QueueManager#execute_each Unknown selector type returned by wmq_selector()"); - break; - } - return 0; -} - -/* - * call-seq: - * execute(...) - * - * Execute an Administration command against the local queue manager - * - * Parameters: - * * Since the number of parameters can vary dramatically, all parameters are passed by name in a hash - * * The entire MQ Administration interface has been implemented. - * Rather than re-documentation the hundreds of options, a standard - * convention has been used to map the MQ constants to Symbols in Ruby. - * - * For all MQ Admin commands, just drop the MQAI_ off the front and - * convert the command name to lower case. - * * E.g. MQAI_INQUIRE_Q becomes inquire_q - * - * For the hundreds of parameters, a similiar technique is followed. - * Remove the prefixes: MQCA_, MQIA_, etc.. and convert to lowercase - * * E.g. MQCA_Q_NAME becomes :q_name - * - * Example - * WMQ::QueueManager.connect do |qmgr| - * result = qmgr.execute( - * :command => :inquire_q, - * :q_name => 'MY.LOCAL.QUEUE', - * :q_type => WMQ::MQQT_LOCAL, - * :current_q_depth => nil - * ) - * # OR, we can replace the method name execute with the MQAI command: - * result = qmgr.inquire_q( - * :q_name => 'MY.LOCAL.QUEUE', - * :q_type => WMQ::MQQT_LOCAL, - * :current_q_depth => nil - * ) - * - * Complete Example: - * require 'wmq/wmq' - * require 'wmq/wmq_const_admin' - * WMQ::QueueManager.connect(:q_mgr_name=>'REID', :connection_name=>'localhost(1414)') do |qmgr| - * qmgr.reset_q_stats(:q_name=>'*').each {|item| p item } - * end - * - * Some one line examples - * qmgr.inquire_q(:q_name=>'TEST*').each {|item| p item } - * - * qmgr.inquire_q(:q_name=>'TEST*', :q_type=>WMQ::MQQT_LOCAL, :current_q_depth=>nil).each {|item| p item } - * - * qmgr.inquire_process(:process_name=>'*').each {|item| p item } - * - * qmgr.ping_q_mgr.each {|item| p item } - * - * qmgr.refresh_security.each {|item| p item } - * - * qmgr.inquire_q_status(:q_name=>'TEST*', :q_status_type=>:q_status, :q_status_attrs=>:process_id).each {|item| p item } - * - * qmgr.start_channel_listener.each {|item| p item } - * - * qmgr.inquire_channel_status(:channel_name=>'*').each {|item| p item } - */ -VALUE QueueManager_execute(VALUE self, VALUE hash) -{ -#ifdef MQHB_UNUSABLE_HBAG - VALUE val; - PQUEUE_MANAGER pqm; - Data_Get_Struct(self, QUEUE_MANAGER, pqm); - - Check_Type(hash, T_HASH); - - if (pqm->admin_bag == MQHB_UNUSABLE_HBAG) /* Lazy create admin bag */ - { - pqm->mqCreateBag(MQCBO_ADMIN_BAG, &pqm->admin_bag, &pqm->comp_code, &pqm->reason_code); - CHECK_COMPLETION_CODE("Creating the admin bag") - } - else - { - pqm->mqClearBag(pqm->admin_bag, &pqm->comp_code, &pqm->reason_code); - CHECK_COMPLETION_CODE("Clearing the admin bag") - } - - if (pqm->reply_bag == MQHB_UNUSABLE_HBAG) /* Lazy create reply bag */ - { - pqm->mqCreateBag(MQCBO_ADMIN_BAG, &pqm->reply_bag, &pqm->comp_code, &pqm->reason_code); - CHECK_COMPLETION_CODE("Creating the reply bag") - } - else - { - pqm->mqClearBag(pqm->reply_bag, &pqm->comp_code, &pqm->reason_code); - CHECK_COMPLETION_CODE("Clearing the reply bag") - } - - val = rb_hash_aref(hash, ID2SYM(ID_command)); /* :command */ - if (NIL_P(val)) - { - rb_raise(rb_eArgError, "WMQ::QueueManager#execute Mandatory parameter :command missing"); - } -#if RUBY_VERSION_CODE > 183 - rb_hash_foreach(hash, QueueManager_execute_each, (VALUE)pqm); -#else - rb_iterate (rb_each, hash, QueueManager_execute_each, (VALUE)pqm); -#endif - if(pqm->trace_level) printf ("WMQ::QueueManager#execute() Queue Manager Handle:%ld\n", (long)pqm->hcon); - - pqm->mqExecute( - pqm->hcon, /* MQ connection handle */ - wmq_command_lookup(rb_to_id(val)), /* Command to be executed */ - MQHB_NONE, /* No options bag */ - pqm->admin_bag, /* Handle to bag containing commands */ - pqm->reply_bag, /* Handle to bag to receive the response*/ - MQHO_NONE, /* Put msg on SYSTEM.ADMIN.COMMAND.QUEUE*/ - MQHO_NONE, /* Create a dynamic q for the response */ - &pqm->comp_code, /* Completion code from the mqexecute */ - &pqm->reason_code); /* Reason code from mqexecute call */ - - if(pqm->trace_level) printf("WMQ::QueueManager#execute() completed with reason:%s\n", wmq_reason(pqm->reason_code)); - - if (pqm->comp_code == MQCC_OK) - { - MQLONG numberOfBags; /* number of bags in response bag */ - MQHBAG qAttrsBag; /* bag containing q attributes */ - VALUE array; - MQLONG size; - MQLONG length; - MQCHAR inquiry_buffer[WMQ_EXEC_STRING_INQ_BUFFER_SIZE]; - PMQCHAR pChar; - - MQLONG qDepth; /* depth of queue */ - MQLONG item_type; - MQLONG selector; - MQLONG number_of_items; - int bag_index, items, k; - - pqm->mqCountItems(pqm->reply_bag, MQHA_BAG_HANDLE, &numberOfBags, &pqm->comp_code, &pqm->reason_code); - CHECK_COMPLETION_CODE("Counting number of bags returned from the command server") - - if(pqm->trace_level > 1) printf("WMQ::QueueManager#execute() %ld bags returned\n", (long)numberOfBags); - array = rb_ary_new2(numberOfBags); - - for ( bag_index=0; bag_index<numberOfBags; bag_index++) /* For each bag, extract the queue depth */ - { - hash = rb_hash_new(); - - pqm->mqInquireBag(pqm->reply_bag, MQHA_BAG_HANDLE, bag_index, &qAttrsBag, &pqm->comp_code, &pqm->reason_code); - CHECK_COMPLETION_CODE("Inquiring for the attribute bag handle") - - pqm->mqCountItems(qAttrsBag, MQSEL_ALL_SELECTORS, &number_of_items, &pqm->comp_code, &pqm->reason_code); - CHECK_COMPLETION_CODE("Counting number of items in this bag") - - if(pqm->trace_level > 1) printf("WMQ::QueueManager#execute() Bag %d contains %ld items\n", bag_index, (long)number_of_items); - - for (items=0; items<number_of_items; items++) /* For each item, extract it's value */ - { - pqm->mqInquireItemInfo( - qAttrsBag, /* I: Bag handle */ - MQSEL_ANY_SELECTOR, /* I: Item selector */ - items, /* I: Item index */ - &selector, /* O: Selector of item */ - &item_type, /* O: Data type of item */ - &pqm->comp_code, - &pqm->reason_code); - CHECK_COMPLETION_CODE("Inquiring Item details") - - if (selector > 0) /* Skip system selectors */ - { - switch (item_type) - { - case MQIT_INTEGER: - pqm->mqInquireInteger(qAttrsBag, MQSEL_ALL_SELECTORS, items, &qDepth, &pqm->comp_code, &pqm->reason_code); - CHECK_COMPLETION_CODE("Inquiring Integer item") - - if(pqm->trace_level > 1) - printf("WMQ::QueueManager#execute() Item %d: Integer:%ld, selector:%ld\n", items, (long)qDepth, (long)selector); - - rb_hash_aset(hash, ID2SYM(wmq_selector_id(selector)), LONG2NUM(qDepth)); - break; - - case MQIT_STRING: - pqm->mqInquireString(qAttrsBag, MQSEL_ALL_SELECTORS, items, WMQ_EXEC_STRING_INQ_BUFFER_SIZE-1, inquiry_buffer, - &size, NULL, &pqm->comp_code, &pqm->reason_code); - if(pqm->trace_level > 2) - printf("WMQ::QueueManager#execute() mqInquireString buffer size: %d, string size:%ld\n", - WMQ_EXEC_STRING_INQ_BUFFER_SIZE,(long)size); - CHECK_COMPLETION_CODE("Inquiring String item") - - length = 0; - pChar = inquiry_buffer + size-1; - for (k = size; k > 0; k--) - { - if (*pChar != ' ' && *pChar != 0) - { - length = k; - break; - } - pChar--; - } - rb_hash_aset(hash, ID2SYM(wmq_selector_id(selector)), rb_str_new(inquiry_buffer, length)); - - if(pqm->trace_level > 1) - { - inquiry_buffer[length] = '\0'; - printf("WMQ::QueueManager#execute() Item %d: String:'%s', selector:%ld\n", - items, inquiry_buffer, (long)selector); - } - break; - - case MQIT_BAG: - printf("Ignoring Bag at this level\n"); - break; - - default: - printf("Ignoring Unknown type:%ld\n", (long)item_type); - break; - } - } - } - rb_ary_push(array, hash); - } - return array; - } - else - { - VALUE name = rb_iv_get(self,"@name"); - name = StringValue(name); - - if (pqm->reason_code == MQRCCF_COMMAND_FAILED) - { - /* Find out why admin command failed */ - MQLONG result_comp_code, result_reason_code; - MQHBAG result_bag; - - pqm->mqInquireBag(pqm->reply_bag, MQHA_BAG_HANDLE, 0, &result_bag, &pqm->comp_code, &pqm->reason_code); - CHECK_COMPLETION_CODE("Getting the result bag handle") - - pqm->mqInquireInteger(result_bag, MQIASY_COMP_CODE, MQIND_NONE, &result_comp_code, &pqm->comp_code, &pqm->reason_code); - CHECK_COMPLETION_CODE("Getting the completion code from the result bag") - - pqm->mqInquireInteger(result_bag, MQIASY_REASON, MQIND_NONE, &result_reason_code, &pqm->comp_code, &pqm->reason_code); - CHECK_COMPLETION_CODE("Getting the reason code from the result bag") - - pqm->comp_code = result_comp_code; - pqm->reason_code = result_reason_code; - - if(pqm->trace_level) - printf("WMQ::QueueManager#execute() Error returned by command server:%s\n", wmq_reason(pqm->reason_code)); - } - - if (pqm->exception_on_error) - { - if (pqm->reason_code == MQRC_CMD_SERVER_NOT_AVAILABLE) - { - rb_raise(wmq_exception, - "WMQ::QueueManager#execute(). Please start the WebSphere MQ Command Server : 'strmqcsv %s', reason:%s", - RSTRING(name)->ptr, - wmq_reason(pqm->reason_code)); - } - else - { - rb_raise(wmq_exception, - "WMQ::QueueManager#execute(). Error executing admin command on Queue Manager:%s, reason:%s", - RSTRING(name)->ptr, - wmq_reason(pqm->reason_code)); - } - } - return Qfalse; - } - return Qnil; -#else - rb_notimplement(); - return Qfalse; -#endif -} +/* + * Copyright 2006 J. Reid Morrison. Dimension Solutions, Inc. + * + * Licensed 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. + */ + +#include "wmq.h" + +static ID ID_open; +static ID ID_call; +static ID ID_new; +static ID ID_backout; +static ID ID_connect_options; +static ID ID_q_mgr_name; +static ID ID_queue_manager; +static ID ID_exception_on_error; +static ID ID_descriptor; +static ID ID_message; +static ID ID_trace_level; + +/* MQCD ID's */ +static ID ID_channel_name; +static ID ID_transport_type; +static ID ID_mode_name; +static ID ID_tp_name; +static ID ID_security_exit; +static ID ID_send_exit; +static ID ID_receive_exit; +static ID ID_max_msg_length; +static ID ID_security_user_data; +static ID ID_send_user_data; +static ID ID_receive_user_data; +static ID ID_user_identifier; +static ID ID_password; +static ID ID_connection_name; +static ID ID_heartbeat_interval; +static ID ID_long_remote_user_id; +static ID ID_remote_security_id; +static ID ID_ssl_cipher_spec; +static ID ID_ssl_peer_name; +static ID ID_keep_alive_interval; +static ID ID_crypto_hardware; + +/* MQSCO ID's */ +static ID ID_key_repository; + +/* Admin ID's */ +static ID ID_create_queue; +static ID ID_q_name; +static ID ID_command; + +void QueueManager_id_init(void) +{ + ID_open = rb_intern("open"); + ID_call = rb_intern("call"); + ID_new = rb_intern("new"); + ID_backout = rb_intern("backout"); + ID_q_mgr_name = rb_intern("q_mgr_name"); + ID_queue_manager = rb_intern("queue_manager"); + ID_exception_on_error = rb_intern("exception_on_error"); + ID_connect_options = rb_intern("connect_options"); + ID_trace_level = rb_intern("trace_level"); + ID_descriptor = rb_intern("descriptor"); + ID_message = rb_intern("message"); + + /* MQCD ID's */ + ID_channel_name = rb_intern("channel_name"); + ID_transport_type = rb_intern("transport_type"); + ID_mode_name = rb_intern("mode_name"); + ID_tp_name = rb_intern("tp_name"); + ID_security_exit = rb_intern("security_exit"); + ID_send_exit = rb_intern("send_exit"); + ID_receive_exit = rb_intern("receive_exit"); + ID_max_msg_length = rb_intern("max_msg_length"); + ID_security_user_data = rb_intern("security_user_data"); + ID_send_user_data = rb_intern("send_user_data"); + ID_receive_user_data = rb_intern("receive_user_data"); + ID_user_identifier = rb_intern("user_identifier"); + ID_password = rb_intern("password"); + ID_connection_name = rb_intern("connection_name"); + ID_heartbeat_interval = rb_intern("heartbeat_interval"); + ID_long_remote_user_id = rb_intern("long_remote_user_id"); + ID_remote_security_id = rb_intern("remote_security_id"); + ID_ssl_cipher_spec = rb_intern("ssl_cipher_spec"); + ID_ssl_peer_name = rb_intern("ssl_peer_name"); + ID_keep_alive_interval = rb_intern("keep_alive_interval"); + + /* MQSCO ID's */ + ID_key_repository = rb_intern("key_repository"); + ID_crypto_hardware = rb_intern("crypto_hardware"); + + /* Admin ID's */ + ID_create_queue = rb_intern("create_queue"); + ID_q_name = rb_intern("q_name"); + ID_command = rb_intern("command"); +} + +/* -------------------------------------------------- + * C Structure to store MQ data types and other + * C internal values + * --------------------------------------------------*/ +void QUEUE_MANAGER_free(void* p) +{ + PQUEUE_MANAGER pqm = (PQUEUE_MANAGER)p; + + if(pqm->trace_level>1) printf("WMQ::QueueManager Freeing QUEUE_MANAGER structure\n"); + + if (pqm->hcon && !pqm->already_connected) /* Valid MQ handle means MQDISC was not called */ + { + printf("WMQ::QueueManager#free disconnect() was not called for Queue Manager instance!!\n"); + printf("WMQ::QueueManager#free Automatically calling back() and disconnect()\n"); + pqm->MQBACK(pqm->hcon, &pqm->comp_code, &pqm->reason_code); + pqm->MQDISC(&pqm->hcon, &pqm->comp_code, &pqm->reason_code); + } + #ifdef MQCD_VERSION_6 + free(pqm->long_remote_user_id_ptr); + #endif + #ifdef MQCD_VERSION_7 + free(pqm->ssl_peer_name_ptr); + #endif + #ifdef MQHB_UNUSABLE_HBAG + if (pqm->admin_bag != MQHB_UNUSABLE_HBAG) + { + pqm->mqDeleteBag(&pqm->admin_bag, &pqm->comp_code, &pqm->reason_code); + } + + if (pqm->reply_bag != MQHB_UNUSABLE_HBAG) + { + pqm->mqDeleteBag(&pqm->reply_bag, &pqm->comp_code, &pqm->reason_code); + } + #endif + Queue_manager_mq_free(pqm); + free(pqm->p_buffer); + free(p); +} + +VALUE QUEUE_MANAGER_alloc(VALUE klass) +{ + static MQCNO default_MQCNO = {MQCNO_DEFAULT}; /* MQCONNX Connection Options */ + #ifdef MQCNO_VERSION_2 + static MQCD default_MQCD = {MQCD_CLIENT_CONN_DEFAULT}; /* Client Connection */ + #endif + #ifdef MQCNO_VERSION_4 + static MQSCO default_MQSCO = {MQSCO_DEFAULT}; + #endif + + PQUEUE_MANAGER pqm = ALLOC(QUEUE_MANAGER); + + pqm->hcon = 0; + pqm->comp_code = 0; + pqm->reason_code = 0; + pqm->exception_on_error = 1; + pqm->already_connected = 0; + pqm->trace_level = 0; + memcpy(&pqm->connect_options, &default_MQCNO, sizeof(MQCNO)); + #ifdef MQCNO_VERSION_2 + memcpy(&pqm->client_conn, &default_MQCD, sizeof(MQCD)); + + /* Tell MQ to use Client Conn structures, etc. */ + pqm->connect_options.Version = MQCNO_CURRENT_VERSION; + pqm->connect_options.ClientConnPtr = &pqm->client_conn; + #endif + #ifdef MQCNO_VERSION_4 + memcpy(&pqm->ssl_config_opts, &default_MQSCO, sizeof(MQSCO)); + #endif + #ifdef MQCD_VERSION_6 + pqm->long_remote_user_id_ptr = 0; + #endif + #ifdef MQCD_VERSION_7 + pqm->ssl_peer_name_ptr = 0; + #endif + #ifdef MQHB_UNUSABLE_HBAG + pqm->admin_bag = MQHB_UNUSABLE_HBAG; + pqm->reply_bag = MQHB_UNUSABLE_HBAG; + #endif + pqm->buffer_size = 0; + pqm->p_buffer = 0; + + pqm->is_client_conn = 0; + pqm->mq_lib_handle = 0; + + return Data_Wrap_Struct(klass, 0, QUEUE_MANAGER_free, pqm); +} + +/* + * call-seq: + * new(...) + * + * Parameters: + * * Since the number of parameters can vary dramatically, all parameters are passed by name in a hash + * * See QueueManager.new for details on all the parameters + * + * Note: + * * It is not recommended to use this method, rather use QueueManager.connect, since + * it will automatically disconnect from the queue manager. It also deals with backing out + * the current unit of work in the event of an unhandled exception. E.g. Syntax Error + * * RuntimeError and ArgumentError exceptions are always thrown regardless of the + * value of :exception_on_error + * + * Todo: + * * Support multiple send and receive exits + */ +VALUE QueueManager_initialize(VALUE self, VALUE hash) +{ + VALUE val; + VALUE str; + size_t size; + size_t length; + PQUEUE_MANAGER pqm; + + Check_Type(hash, T_HASH); + + Data_Get_Struct(self, QUEUE_MANAGER, pqm); + + WMQ_HASH2MQLONG(hash,trace_level, pqm->trace_level) + + /* @name = options[:q_mgr_name] || '' # QMGR Name optional with Client Connection */ + val = rb_hash_aref(hash, ID2SYM(ID_q_mgr_name)); + if (NIL_P(val)) + { + rb_iv_set(self, "@name", rb_str_new2("")); + if(pqm->trace_level > 1) printf("WMQ::QueueManager#initialize() Queue Manager:[Not specified, use Default QMGR]\n"); + } + else + { + rb_iv_set(self, "@name", val); + if(pqm->trace_level > 1) printf("WMQ::QueueManager#initialize() Queue Manager:%s\n", RSTRING_PTR(val)); + } + + WMQ_HASH2BOOL(hash,exception_on_error, pqm->exception_on_error) + + /* + * All Client connection parameters are ignored if connection_name is missing + */ +#ifdef MQCNO_VERSION_2 + if(!NIL_P(rb_hash_aref(hash, ID2SYM(ID_connection_name)))) + { + PMQCD pmqcd = &pqm->client_conn; /* Process MQCD */ + pqm->is_client_conn = 1; /* Set to Client connection */ + + WMQ_HASH2MQCHARS(hash,connection_name, pmqcd->ConnectionName) + WMQ_HASH2MQLONG (hash,transport_type, pmqcd->TransportType) + WMQ_HASH2MQCHARS(hash,mode_name, pmqcd->ModeName) + WMQ_HASH2MQCHARS(hash,tp_name, pmqcd->TpName) + WMQ_HASH2MQCHARS(hash,security_exit, pmqcd->SecurityExit) + WMQ_HASH2MQCHARS(hash,send_exit, pmqcd->SendExit) + WMQ_HASH2MQCHARS(hash,receive_exit, pmqcd->ReceiveExit) + WMQ_HASH2MQLONG (hash,max_msg_length, pmqcd->MaxMsgLength) + WMQ_HASH2MQCHARS(hash,security_user_data, pmqcd->SecurityUserData) + WMQ_HASH2MQCHARS(hash,send_user_data, pmqcd->SendUserData) + WMQ_HASH2MQCHARS(hash,receive_user_data, pmqcd->ReceiveUserData) + WMQ_HASH2MQCHARS(hash,user_identifier, pmqcd->UserIdentifier) + WMQ_HASH2MQCHARS(hash,password, pmqcd->Password) + + /* Default channel name to system default */ + val = rb_hash_aref(hash, ID2SYM(ID_channel_name)); + if (NIL_P(val)) + { + strncpy(pmqcd->ChannelName, "SYSTEM.DEF.SVRCONN", sizeof(pmqcd->ChannelName)); + } + else + { + WMQ_HASH2MQCHARS(hash,channel_name, pmqcd->ChannelName) + } + + #ifdef MQCD_VERSION_4 + WMQ_HASH2MQLONG(hash,heartbeat_interval, pmqcd->HeartbeatInterval) + /* TODO: + WMQ_HASH2MQLONG(hash,exit_name_length, pmqcd->ExitNameLength) + WMQ_HASH2MQLONG(hash,exit_data_length, pmqcd->ExitDataLength) + WMQ_HASH2MQLONG(hash,send_exits_defined, pmqcd->SendExitsDefined) + WMQ_HASH2MQLONG(hash,receive_exits_defined, pmqcd->ReceiveExitsDefined) + TO_PTR (send_exit_ptr, pmqcd->SendExitPtr) + TO_PTR (send_user_data_ptr, pmqcd->SendUserDataPtr) + TO_PTR (receive_exit_ptr, pmqcd->ReceiveExitPtr) + TO_PTR (receive_user_data_ptr, pmqcd->ReceiveUserDataPtr) + */ + #endif + #ifdef MQCD_VERSION_6 + val = rb_hash_aref(hash, ID2SYM(ID_long_remote_user_id)); + if (!NIL_P(val)) + { + str = StringValue(val); + length = RSTRING_LEN(str); + + if (length > 0) + { + MQPTR pBuffer; + if(pqm->trace_level > 1) + printf("WMQ::QueueManager#initialize() Setting long_remote_user_id:%s\n", + RSTRING_PTR(str)); + + /* Include null at end of string */ + pBuffer = ALLOC_N(char, length+1); + memcpy(pBuffer, RSTRING_PTR(str), length+1); + + pmqcd->LongRemoteUserIdLength = length; + pmqcd->LongRemoteUserIdPtr = pBuffer; + pqm->long_remote_user_id_ptr = pBuffer; + } + } + WMQ_HASH2MQBYTES(hash,remote_security_id, pmqcd->RemoteSecurityId) + WMQ_HASH2MQCHARS(hash,ssl_cipher_spec, pmqcd->SSLCipherSpec) + #endif + #ifdef MQCD_VERSION_7 + val = rb_hash_aref(hash, ID2SYM(ID_ssl_peer_name)); + if (!NIL_P(val)) + { + str = StringValue(val); + length = RSTRING_LEN(str); + + if (length > 0) + { + MQPTR pBuffer; + if(pqm->trace_level > 1) + printf("WMQ::QueueManager#initialize() Setting ssl_peer_name:%s\n", + RSTRING_PTR(str)); + + /* Include null at end of string */ + pBuffer = ALLOC_N(char, length+1); + memcpy(pBuffer, RSTRING_PTR(str), length+1); + + pmqcd->SSLPeerNameLength = length; + pmqcd->SSLPeerNamePtr = pBuffer; + pqm->ssl_peer_name_ptr = pBuffer; + } + } + WMQ_HASH2MQLONG(hash,keep_alive_interval, pmqcd->KeepAliveInterval) + + /* Only set if SSL options are supplied, otherwise + * environment variables are ignored: MQSSLKEYR and MQSSLCRYP + * Any SSL info in the client channel definition tables is also ignored + */ + if (!NIL_P(rb_hash_aref(hash, ID2SYM(ID_key_repository))) || + !NIL_P(rb_hash_aref(hash, ID2SYM(ID_crypto_hardware)))) + { + /* Process MQSCO */ + WMQ_HASH2MQCHARS(hash,key_repository, pqm->ssl_config_opts.KeyRepository) + WMQ_HASH2MQCHARS(hash,crypto_hardware, pqm->ssl_config_opts.CryptoHardware) + + pqm->connect_options.SSLConfigPtr = &pqm->ssl_config_opts; + } + #endif + + } + else + { + pqm->is_client_conn = 0; /* Set to Server connection */ + } +#endif + +#ifdef MQCNO_VERSION_4 + /* Process MQCNO */ + WMQ_HASH2MQLONG(hash,connect_options, pqm->connect_options.Options) +#endif + + /* -------------------------------------------------- + * TODO: MQAIR Structure - LDAP Security + * --------------------------------------------------*/ + + return Qnil; +} + +/* + * Before working with any queues, it is necessary to connect + * to the queue manager. + * + * Returns: + * * true : On Success + * * false: On Failure + * + * comp_code and reason_code are also updated. + * reason will return a text description of the reason_code + * + * Throws: + * * WMQ::WMQException if comp_code != MQCC_OK + * * Except if :exception_on_error => false was supplied as a parameter + * to QueueManager.new + */ +VALUE QueueManager_connect(VALUE self) +{ + VALUE name; + + PQUEUE_MANAGER pqm; + Data_Get_Struct(self, QUEUE_MANAGER, pqm); + pqm->already_connected = 0; + + Queue_manager_mq_load(pqm); /* Load MQ Library */ + + name = rb_iv_get(self,"@name"); + name = StringValue(name); + + if(pqm->trace_level) + printf("WMQ::QueueManager#connect() Connect to Queue Manager:%s\n", RSTRING_PTR(name)); + + if (pqm->hcon) /* Disconnect from qmgr if already connected, ignore errors */ + { + if(pqm->trace_level) + printf("WMQ::QueueManager#connect() Already connected to Queue Manager:%s, Disconnecting first!\n", RSTRING_PTR(name)); + + pqm->MQDISC(&pqm->hcon, &pqm->comp_code, &pqm->reason_code); + } + + pqm->MQCONNX( + RSTRING_PTR(name), /* queue manager */ + &pqm->connect_options, /* Connection Options */ + &pqm->hcon, /* connection handle */ + &pqm->comp_code, /* completion code */ + &pqm->reason_code); /* connect reason code */ + + if(pqm->trace_level) + printf("WMQ::QueueManager#connect() MQCONNX completed with reason:%s, Handle:%ld\n", + wmq_reason(pqm->reason_code), + (long)pqm->hcon); + + if (pqm->comp_code == MQCC_FAILED) + { + pqm->hcon = 0; + + if (pqm->exception_on_error) + { + rb_raise(wmq_exception, + "WMQ::QueueManager#connect(). Error connecting to Queue Manager:%s, reason:%s", + RSTRING_PTR(name), + wmq_reason(pqm->reason_code)); + } + + return Qfalse; + } + + if (pqm->reason_code == MQRC_ALREADY_CONNECTED) + { + if(pqm->trace_level) printf("WMQ::QueueManager#connect() Already connected\n"); + pqm->already_connected = 1; + } + + return Qtrue; +} + +/* + * Disconnect from this QueueManager instance + * + * Returns: + * * true : On Success + * * false: On Failure + * + * comp_code and reason_code are also updated. + * reason will return a text description of the reason_code + * + * Throws: + * * WMQ::WMQException if comp_code != MQCC_OK + * * Except if :exception_on_error => false was supplied as a parameter + * to QueueManager.new + */ +VALUE QueueManager_disconnect(VALUE self) +{ + PQUEUE_MANAGER pqm; + Data_Get_Struct(self, QUEUE_MANAGER, pqm); + + if(pqm->trace_level) printf ("WMQ::QueueManager#disconnect() Queue Manager Handle:%ld\n", (long)pqm->hcon); + + if (!pqm->already_connected) + { + pqm->MQDISC(&pqm->hcon, &pqm->comp_code, &pqm->reason_code); + + if(pqm->trace_level) printf("WMQ::QueueManager#disconnect() MQDISC completed with reason:%s\n", wmq_reason(pqm->reason_code)); + + if (pqm->comp_code != MQCC_OK) + { + if (pqm->exception_on_error) + { + VALUE name = rb_iv_get(self,"@name"); + name = StringValue(name); + + rb_raise(wmq_exception, + "WMQ::QueueManager#disconnect(). Error disconnecting from Queue Manager:%s, reason:%s", + RSTRING_PTR(name), + wmq_reason(pqm->reason_code)); + } + + return Qfalse; + } + } + else + { + pqm->comp_code = 0; + pqm->reason_code = 0; + + if(pqm->trace_level) printf ("WMQ::QueueManager#disconnect() Not calling MQDISC, since already connected on connect\n"); + } + + pqm->hcon = 0; + + return Qtrue; +} + +/* + * Commit the current unit of work for this QueueManager instance + * + * Note: + * * commit will have no effect if all put and get operations were performed + * without specifying :sync => true + * + * Returns: + * * true : On Success + * * false: On Failure + * + * comp_code and reason_code are also updated. + * reason will return a text description of the reason_code + * + * Throws: + * * WMQ::WMQException if comp_code != MQCC_OK + * * Except if :exception_on_error => false was supplied as a parameter + * to QueueManager.new + */ +VALUE QueueManager_commit(VALUE self) +{ + PQUEUE_MANAGER pqm; + Data_Get_Struct(self, QUEUE_MANAGER, pqm); + + if(pqm->trace_level) printf ("WMQ::QueueManager#commit() Queue Manager Handle:%ld\n", (long)pqm->hcon); + + pqm->MQCMIT(pqm->hcon, &pqm->comp_code, &pqm->reason_code); + + if(pqm->trace_level) printf("WMQ::QueueManager#commit() MQCMIT completed with reason:%s\n", wmq_reason(pqm->reason_code)); + + if (pqm->comp_code != MQCC_OK) + { + if (pqm->exception_on_error) + { + VALUE name = rb_iv_get(self,"@name"); + name = StringValue(name); + + rb_raise(wmq_exception, + "WMQ::QueueManager#commit(). Error commiting changes to Queue Manager:%s, reason:%s", + RSTRING_PTR(name), + wmq_reason(pqm->reason_code)); + } + return Qfalse; + } + + return Qtrue; +} + +/* + * Backout the current unit of work for this QueueManager instance + * + * Since the last commit or rollback any messages put to a queue + * under synchpoint will be removed and any messages retrieved + * under synchpoint from any queues will be returned + * + * Note: + * * backout will have no effect if all put and get operations were performed + * without specifying :sync => true + * + * Returns: + * * true : On Success + * * false: On Failure + * + * comp_code and reason_code are also updated. + * reason will return a text description of the reason_code + * + * Throws: + * * WMQ::WMQException if comp_code != MQCC_OK + * * Except if :exception_on_error => false was supplied as a parameter + * to QueueManager.new + */ +VALUE QueueManager_backout(VALUE self) +{ + PQUEUE_MANAGER pqm; + Data_Get_Struct(self, QUEUE_MANAGER, pqm); + + if(pqm->trace_level) printf ("WMQ::QueueManager#backout() Queue Manager Handle:%ld\n", (long)pqm->hcon); + + pqm->MQBACK(pqm->hcon, &pqm->comp_code, &pqm->reason_code); + + if(pqm->trace_level) printf("WMQ::QueueManager#backout() MQBACK completed with reason:%s\n", wmq_reason(pqm->reason_code)); + + if (pqm->comp_code != MQCC_OK) + { + if (pqm->exception_on_error) + { + VALUE name = rb_iv_get(self,"@name"); + name = StringValue(name); + + rb_raise(wmq_exception, + "WMQ::QueueManager#backout(). Error backing out changes to Queue Manager:%s, reason:%s", + RSTRING_PTR(name), + wmq_reason(pqm->reason_code)); + } + return Qfalse; + } + + return Qtrue; +} + +/* + * Advanced WebSphere MQ Use: + * + * Begin a unit of work between this QueueManager instance and another + * resource such as a Database + * + * Starts a new unit of work under which put and get can be called with + * with the parameter :sync => true + * + * Returns: + * * true : On Success + * * false: On Failure + * + * comp_code and reason_code are also updated. + * reason will return a text description of the reason_code + * + * Throws: + * * WMQ::WMQException if comp_code != MQCC_OK + * * Except if :exception_on_error => false was supplied as a parameter + * to QueueManager.new + */ +VALUE QueueManager_begin(VALUE self) +{ + PQUEUE_MANAGER pqm; + Data_Get_Struct(self, QUEUE_MANAGER, pqm); + + if(pqm->trace_level) printf ("WMQ::QueueManager#begin() Queue Manager Handle:%ld\n", (long)pqm->hcon); + + pqm->MQBEGIN(pqm->hcon, 0, &pqm->comp_code, &pqm->reason_code); + + if(pqm->trace_level) printf("WMQ::QueueManager#begin() MQBEGIN completed with reason:%s\n", wmq_reason(pqm->reason_code)); + + if (pqm->comp_code != MQCC_OK) + { + if (pqm->exception_on_error) + { + VALUE name = rb_iv_get(self,"@name"); + name = StringValue(name); + + rb_raise(wmq_exception, + "WMQ::QueueManager#begin(). Error starting unit of work on Queue Manager:%s, reason:%s", + RSTRING_PTR(name), + wmq_reason(pqm->reason_code)); + } + return Qfalse; + } + + return Qtrue; +} + +/* + * call-seq: + * put(parameters) + * + * Put a message to the queue without having to first open the queue + * Recommended for reply queues that change frequently + * + * * parameters: a Hash consisting of one or more of the following parameters + * + * Summary of parameters and their WebSphere MQ equivalents + * queue.get( # WebSphere MQ Equivalents: + * :q_name => 'Queue Name', # MQOD.ObjectName + * :q_name => { queue_manager=>'QMGR_name', # MQOD.ObjectQMgrName + * q_name =>'q_name'} + * :message => my_message, # n/a : Instance of Message + * :data => "Hello World", # n/a : Data to send + * :sync => false, # MQGMO_SYNCPOINT + * :new_id => true, # MQPMO_NEW_MSG_ID & MQPMO_NEW_CORREL_ID + * :new_msg_id => true, # MQPMO_NEW_MSG_ID + * :new_correl_id => true, # MQPMO_NEW_CORREL_ID + * :fail_if_quiescing => true, # MQOO_FAIL_IF_QUIESCING + * :options => WMQ::MQPMO_FAIL_IF_QUIESCING # MQPMO_* + * ) + * + * Mandatory Parameters + * * :q_name => String + * * Name of the existing WebSphere MQ local queue, model queue or remote queue to open + * * To open remote queues for which a local remote queue definition is not available + * pass a Hash as q_name (see q_name => Hash) + * OR + * * :q_name => Hash + * * q_name => String + * * Name of the existing WebSphere MQ local queue, model queue or remote queue to open + * * :q_mgr_name => String + * * Name of the remote WebSphere MQ queue manager to send the message to. + * * This allows a message to be written to a queue on a remote queue manager + * where a remote queue definition is not defined locally + * * Commonly used to reply to messages from remote systems + * * If q_mgr_name is the same as the local queue manager name then the message + * is merely written to the local queue. + * * Note: q_mgr_name should only be supplied when putting messages to the queue. + * It is not possible to get messages from a queue on a queue manager other + * than the currently connected queue manager + * + * * Either :message or :data must be supplied + * * If both are supplied, then :data will be written to the queue. The data in :message + * will be ignored + * + * Optional Parameters + * * :data => String + * * Data to be written to the queue. Can be binary or text data + * + * * :message => Message + * * An instance of the WMQ::Message + * * The Message descriptor, headers and data is retrieved from :message + * * message.data is ignored if :data is supplied + * + * * :sync => true or false + * * Determines whether the get is performed under synchpoint. + * I.e. Under the current unit of work + * Default: false + * + * * :new_id => true or false + * * Generate a new message id and correlation id for this + * message. :new_msg_id and :new_correl_id will be ignored + * if this parameter is true + * Default: false + * + * * :new_msg_id => true or false + * * Generate a new message id for this message + * * Note: A blank message id will result in a new message id anyway. + * However, for subsequent puts using the same message descriptor, the same + * message id will be used. + * Default: false + * + * * :new_correl_id => true or false + * * Generate a new correlation id for this message + * Default: false + * + * * :fail_if_quiescing => true or false + * * Determines whether the WMQ::Queue#put call will fail if the queue manager is + * in the process of being quiesced. + * * Note: This interface differs from other WebSphere MQ interfaces, + * they do not default to true. + * Default: true + * Equivalent to: MQGMO_FAIL_IF_QUIESCING + * + * * Note: As part of the application design, carefull consideration + * should be given as to when to allow a transaction or + * unit of work to complete or fail under this condition. + * As such it is important to include this option where + * appropriate so that MQ Administrators can shutdown the + * queue managers without having to resort to the 'immediate' + * shutdown option. + * + * * :options => Fixnum (Advanced MQ Use only) + * * Numeric field containing any of the MQ Put message options or'd together + * * E.g. :options => WMQ::MQPMO_PASS_IDENTITY_CONTEXT | WMQ::MQPMO_ALTERNATE_USER_AUTHORITY + * * Note: If :options is supplied, it is applied first, then the above parameters are + * applied afterwards. + * * One or more of the following values: + * WMQ::MQPMO_NO_SYNCPOINT + * WMQ::MQPMO_LOGICAL_ORDER + * WMQ::MQPMO_NO_CONTEXT + * WMQ::MQPMO_DEFAULT_CONTEXT + * WMQ::MQPMO_PASS_IDENTITY_CONTEXT + * WMQ::MQPMO_PASS_ALL_CONTEXT + * WMQ::MQPMO_SET_IDENTITY_CONTEXT + * WMQ::MQPMO_SET_ALL_CONTEXT + * WMQ::MQPMO_ALTERNATE_USER_AUTHORITY + * WMQ::MQPMO_RESOLVE_LOCAL_Q + * WMQ::MQPMO_NONE + * * Please see the WebSphere MQ documentation for more details on the above options + * Default: WMQ::MQPMO_NONE + * + * Returns: + * * true : On Success + * * false: On Failure + * + * comp_code and reason_code are also updated. + * reason will return a text description of the reason_code + * + * Throws: + * * WMQ::WMQException if comp_code == MQCC_FAILED + * * Except if :exception_on_error => false was supplied as a parameter + * to QueueManager.new + */ +VALUE QueueManager_put(VALUE self, VALUE hash) +{ + MQLONG BufferLength; /* Length of the message in Buffer */ + PMQVOID pBuffer; /* Message data */ + MQMD md = {MQMD_DEFAULT}; /* Message Descriptor */ + MQPMO pmo = {MQPMO_DEFAULT}; /* put message options */ + MQOD od = {MQOD_DEFAULT}; /* Object Descriptor */ + VALUE q_name; + VALUE str; + size_t size; + size_t length; + VALUE val; + + PQUEUE_MANAGER pqm; + Data_Get_Struct(self, QUEUE_MANAGER, pqm); + + Check_Type(hash, T_HASH); + + q_name = rb_hash_aref(hash, ID2SYM(ID_q_name)); + + if (NIL_P(q_name)) + { + rb_raise(rb_eArgError, + "Mandatory parameter :q_name is missing from WMQ::QueueManager::put1()"); + } + + /* -------------------------------------------------- + * If :q_name is a hash, extract :q_name and :q_mgr_name + * --------------------------------------------------*/ + if(TYPE(q_name) == T_HASH) + { + WMQ_HASH2MQCHARS(q_name, q_mgr_name, od.ObjectQMgrName) + + q_name = rb_hash_aref(val, ID2SYM(ID_q_name)); + if (NIL_P(q_name)) + { + rb_raise(rb_eArgError, + "Mandatory parameter :q_name missing from :q_name hash passed to WMQ::QueueManager#put"); + } + } + + WMQ_STR2MQCHARS(q_name,od.ObjectName) + + Queue_extract_put_message_options(hash, &pmo); + Message_build(&pqm->p_buffer, &pqm->buffer_size, pqm->trace_level, + hash, &pBuffer, &BufferLength, &md); + + if(pqm->trace_level) printf("WMQ::QueueManager#put Queue Manager Handle:%ld\n", (long)pqm->hcon); + + pqm->MQPUT1( + pqm->hcon, /* connection handle */ + &od, /* object descriptor */ + &md, /* message descriptor */ + &pmo, /* put message options */ + BufferLength, /* message length */ + pBuffer, /* message buffer */ + &pqm->comp_code, /* completion code */ + &pqm->reason_code); /* reason code */ + + if(pqm->trace_level) printf("WMQ::QueueManager#put MQPUT1 ended with reason:%s\n", wmq_reason(pqm->reason_code)); + + if (pqm->reason_code != MQRC_NONE) + { + if (pqm->exception_on_error) + { + VALUE qmgr_name = QueueManager_name(self); + + rb_raise(wmq_exception, + "WMQ::QueueManager.put(). Error writing a message to Queue:%s on Queue Manager:%s reason:%s", + RSTRING_PTR(q_name), + RSTRING_PTR(qmgr_name), + wmq_reason(pqm->reason_code)); + } + return Qfalse; + } + else + { + VALUE message = rb_hash_aref(hash, ID2SYM(ID_message)); + if(!NIL_P(message)) + { + VALUE descriptor = rb_funcall(message, ID_descriptor, 0); + Message_from_mqmd(descriptor, &md); /* This could be optimized to output only fields */ + } + } + + return Qtrue; +} + +/* + * Return the completion code for the last MQ operation + * + * Returns => FixNum + * * WMQ::MQCC_OK 0 + * * WMQ::MQCC_WARNING 1 + * * WMQ::MQCC_FAILED 2 + * * WMQ::MQCC_UNKNOWN -1 + * + */ +VALUE QueueManager_comp_code(VALUE self) +{ + PQUEUE_MANAGER pqm; + Data_Get_Struct(self, QUEUE_MANAGER, pqm); + return LONG2NUM(pqm->comp_code); +} + +/* + * Return the reason code for the last MQ operation + * + * Returns => FixNum + * * For a complete list of reason codes, please see WMQ Constants or + * the WebSphere MQ documentation for Reason Codes + * + * Note + * * The list of Reason Codes varies depending on the version of WebSphere MQ + * and the operating system on which Ruby WMQ was compiled + */ +VALUE QueueManager_reason_code(VALUE self) +{ + PQUEUE_MANAGER pqm; + Data_Get_Struct(self, QUEUE_MANAGER, pqm); + return LONG2NUM(pqm->reason_code); +} + +/* + * Returns a textual representation of the reason_code for the last MQ operation + * + * Returns => String + * * For a complete list of reasons, please see WMQ Constants or + * the WebSphere MQ documentation for Reason Codes + * + * Note + * * The list of Reason Codes varies depending on the version of WebSphere MQ + * and the operating system on which Ruby WMQ was compiled + */ +VALUE QueueManager_reason(VALUE self) +{ + PQUEUE_MANAGER pqm; + Data_Get_Struct(self, QUEUE_MANAGER, pqm); + return rb_str_new2(wmq_reason(pqm->reason_code)); +} + +/* + * Returns whether this QueueManager instance is set + * to throw a WMQ::WMQException whenever an MQ operation fails + * + * Returns: + * * true : This QueueManager instance will throw a WMQ::WMQException whenever + * an MQ operation fails. I.e. if comp_code != WMQ::OK. + * * false: WMQ::WMQException will not be thrown + * + * Note: + * * RuntimeError and ArgumentError exceptions are always thrown regardless of the + * value of exception_on_error + */ +VALUE QueueManager_exception_on_error(VALUE self) +{ + PQUEUE_MANAGER pqm; + Data_Get_Struct(self, QUEUE_MANAGER, pqm); + if (pqm->exception_on_error) + { + return Qtrue; + } + + return Qfalse; +} + +/* + * Returns whether this QueueManager instance is currently + * connected to a WebSphere MQ queue manager + * + * Returns: + * * true : This QueueManager instance is connected to a local or remote queue manager + * * false: This QueueManager instance is not currently connected to a local or + * remote queue manager + */ +VALUE QueueManager_connected_q(VALUE self) +{ + PQUEUE_MANAGER pqm; + Data_Get_Struct(self, QUEUE_MANAGER, pqm); + if (pqm->hcon) + { + return Qtrue; + } + return Qfalse; +} + +/* + * Returns the QueueManager name => String + */ +VALUE QueueManager_name(VALUE self) +{ + return rb_iv_get(self,"@name"); +} + +static VALUE QueueManager_open_queue_block(VALUE message, VALUE proc) +{ + return rb_funcall(proc, ID_call, 1, message); +} + +static VALUE QueueManager_open_queue_each(VALUE parameters) +{ + return Queue_singleton_open(1, &parameters, wmq_queue); +} + +/* + * call-seq: + * open_queue(...) + * access_queue(...) + * + * Open the specified queue, then close it once the + * supplied code block has completed + * + * Parameters: + * * Since the number of parameters can vary dramatically, all parameters are passed by name in a hash + * * See Queue.open for the complete list of parameters, except that :queue_manager is *not* required + * since it is supplied automatically by this method + * + * Example: + * require 'wmq/wmq_client' + * + * WMQ::QueueManager.connect(:q_mgr_name=>'REID', :connection_name=>'localhost(1414)') do |qmgr| + * qmgr.open_queue(:q_name=>'TEST.QUEUE', :mode=>:output) do |queue| + * queue.put(:data => 'Hello World') + * end + * end + */ +VALUE QueueManager_open_queue(int argc, VALUE *argv, VALUE self) +{ + VALUE parameters; + VALUE proc; + + /* Extract parameters and code block (Proc) */ + rb_scan_args(argc, argv, "1&", &parameters, &proc); + + Check_Type(parameters, T_HASH); + rb_hash_aset(parameters, ID2SYM(ID_queue_manager), self); + + return rb_iterate(QueueManager_open_queue_each, parameters, QueueManager_open_queue_block, proc); +} + +struct QueueManager_singleton_connect_arg { + VALUE self; + VALUE proc; +}; + +static VALUE QueueManager_singleton_connect_body2(struct QueueManager_singleton_connect_arg* arg) +{ + return rb_funcall(arg->proc, ID_call, 1, arg->self); +} + +static VALUE QueueManager_singleton_connect_rescue(VALUE self) +{ + PQUEUE_MANAGER pqm; + VALUE exception; + Data_Get_Struct(self, QUEUE_MANAGER, pqm); + + if(pqm->trace_level) printf("WMQ::QueueManager.connect() Backing out due to unhandled exception\n"); + //exception = rb_gvar_get("$!"); + exception = rb_eval_string("$!"); /* $! holds the current exception */ + if(pqm->trace_level > 1) printf("WMQ::QueueManager.connect() Exception $! extracted\n"); + QueueManager_backout(self); /* Backout Unit of work */ + if(pqm->trace_level > 1) printf("WMQ::QueueManager.connect() Rethrowing Exception\n"); + rb_exc_raise(exception); /* Re-Raise Exception */ + return Qnil; +} + +static VALUE QueueManager_singleton_connect_body(struct QueueManager_singleton_connect_arg* arg) +{ + return rb_rescue2(QueueManager_singleton_connect_body2, (VALUE)arg, + QueueManager_singleton_connect_rescue, arg->self, + rb_eException, 0); +} + +static VALUE QueueManager_singleton_connect_ensure(VALUE self) +{ + return QueueManager_disconnect(self); +} + +/* + * call-seq: + * connect(...) + * + * Connect to the queue manager, then disconnect once the supplied code block completes + * + * Parameters: + * * Since the number of parameters can vary dramatically, all parameters are passed by name in a hash + * * Summary of parameters and their WebSphere MQ equivalents: + * WMQ::QueueManager.connect( # WebSphere MQ Equivalents: + * :q_mgr_name => 'queue_manager name', + * :exception_on_error => true, # n/a + * :connect_options => WMQ::MQCNO_FASTBATH_BINDING # MQCNO.Options + * + * :trace_level => 0, # n/a + * + * # Common client connection parameters + * :channel_name => 'svrconn channel name', # MQCD.ChannelName + * :connection_name => 'localhost(1414)', # MQCD.ConnectionName + * :transport_type => WMQ::MQXPT_TCP, # MQCD.TransportType + * + * # Advanced client connections parameters + * :max_msg_length => 65535, # MQCD.MaxMsgLength + * :security_exit => 'Name of security exit', # MQCD.SecurityExit + * :send_exit => 'Name of send exit', # MQCD.SendExit + * :receive_exit => 'Name of receive exit', # MQCD.ReceiveExit + * :security_user_data => 'Security exit User data', # MQCD.SecurityUserData + * :send_user_data => 'Send exit user data', # MQCD.SendUserData + * :receive_user_data => 'Receive exit user data', # MQCD.ReceiveUserData + * :heartbeat_interval => 1, # MQCD.HeartbeatInterval + * :remote_security_id => 'Remote Security id', # MQCD.RemoteSecurityId + * :ssl_cipher_spec => 'SSL Cipher Spec', # MQCD.SSLCipherSpec + * :keep_alive_interval=> -1, # MQCD.KeepAliveInterval + * :mode_name => 'LU6.2 Mode Name', # MQCD.ModeName + * :tp_name => 'LU6.2 Transaction pgm name', # MQCD.TpName + * :user_identifier => 'LU 6.2 Userid', # MQCD.UserIdentifier + * :password => 'LU6.2 Password', # MQCD.Password + * :long_remote_user_id=> 'Long remote user identifier', # MQCD.LongRemoteUserId (Ptr, Length) + * :ssl_peer_name => 'SSL Peer name', # MQCD.SSLPeerName (Ptr, Length) + * + * # SSL Options + * :key_repository => '/var/mqm/qmgrs/.../key', # MQSCO.KeyRepository + * :crypto_hardware => 'GSK_ACCELERATOR_NCIPHER_NF_ON', # MQSCO.CryptoHardware + * ) + * + * Optional Parameters + * * :q_mgr_name => String + * * Name of the existing WebSphere MQ Queue Manager to connect to + * + * * Default: + * - Server connections will connect to the default queue manager + * - Client connections will connect to whatever queue + * manager is found at the host and port number as specified + * by the connection_name + * + * * :exception_on_error => true or false + * Determines whether WMQ::WMQExceptions are thrown whenever + * an error occurs during a WebSphere MQ operation (connect, put, get, etc..) + * + * Default: true + * + * * :connect_options => FixNum + * * One or more of the following values: + * WMQ::MQCNO_STANDARD_BINDING + * WMQ::MQCNO_FASTPATH_BINDING + * WMQ::MQCNO_SHARED_BINDING + * WMQ::MQCNO_ISOLATED_BINDING + * WMQ::MQCNO_ACCOUNTING_MQI_ENABLED + * WMQ::MQCNO_ACCOUNTING_MQI_DISABLED + * WMQ::MQCNO_ACCOUNTING_Q_ENABLED + * WMQ::MQCNO_ACCOUNTING_Q_DISABLED + * WMQ::MQCNO_NONE + * + * * Multiple values can be or'd together. E.g. + * :connect_options=>WMQ::MQCNO_FASTPATH_BINDING | WMQ::MQCNO_ACCOUNTING_MQI_ENABLED + * + * * Please see the WebSphere MQ MQCNO data type documentation for more details + * Default: WMQ::MQCNO_NONE + * + * * :trace_level => FixNum + * * Turns on low-level tracing of the WebSphere MQ API calls to stdout. + * * 0: No tracing + * * 1: MQ API tracing only (MQCONNX, MQOPEN, MQPUT, etc..) + * * 2: Include Ruby WMQ tracing + * * 3: Verbose logging (Recommended for when reporting problems in Ruby WMQ) + * Default: 0 + * + * Common Client Connection Parameters (Client connections only) + * * :connection_name => String (Mandatory for client connections) + * * Connection name, made up of the host name (or ip address) and the port number + * * E.g. + * 'mymachine.domain.com(1414)' + * '192.168.0.1(1417)' + * + * * :channel_name => String + * * Name of SVRCONN channel defined on the QueueManager for Client Connections + * * Default Value: + * 'SYSTEM.DEF.SVRCONN' + * + * * :transport_type => WMQ::MQXPT_TCP, # MQCD.TransportType + * * Valid Values: + * WMQ::MQXPT_LOCAL + * WMQ::MQXPT_LU62 + * WMQ::MQXPT_TCP + * WMQ::MQXPT_NETBIOS + * WMQ::MQXPT_SPX + * WMQ::MQXPT_DECNET + * WMQ::MQXPT_UDP + * + * * Default Value: + * WMQ::MQXPT_TCP + * + * For the Advanced Client Connection parameters, please see the WebSphere MQ documentation + * + * Note: + * * If an exception is not caught in the code block, the current unit of work is + * automatically backed out, before disconnecting from the queue manager. + * + * Local Server Connection Example: + * require 'wmq/wmq' + * + * WMQ::QueueManager.connect(:q_mgr_name=>'REID') do |qmgr| + * qmgr.put(:q_name=>'TEST.QUEUE', :data => 'Hello World') + * end + * + * Client Connection Example: + * require 'wmq/wmq_client' + * + * WMQ::QueueManager.connect( + * :channel_name => 'SYSTEM.DEF.SVRCONN', + * :transport_type => WMQ::MQXPT_TCP, + * :connection_name => 'localhost(1414)' ) do |qmgr| + * qmgr.open_queue(:q_name=>'TEST.QUEUE', :mode=>:input) do |queue| + * + * message = WMQ::Message.new + * if queue.get(:message => message) + * puts "Data Received: #{message.data}" + * else + * puts 'No message available' + * end + * end + * end + */ +VALUE QueueManager_singleton_connect(int argc, VALUE *argv, VALUE self) +{ + VALUE proc, parameters, queue_manager; + + /* Extract parameters and code block (Proc) */ + rb_scan_args(argc, argv, "1&", &parameters, &proc); + + queue_manager = rb_funcall(wmq_queue_manager, ID_new, 1, parameters); + if(!NIL_P(proc)) + { + if(Qtrue == QueueManager_connect(queue_manager)) + { + struct QueueManager_singleton_connect_arg arg; + arg.self = queue_manager; + arg.proc = proc; + rb_ensure(QueueManager_singleton_connect_body, (VALUE)&arg, + QueueManager_singleton_connect_ensure, queue_manager); + } + else + { + return Qfalse; + } + } + return queue_manager; +} + +/* What can I say, Ruby blocks have spoilt me :) */ +#define CHECK_COMPLETION_CODE(ACTION) \ +if(pqm->trace_level > 1) \ + printf ("WMQ::QueueManager#execute() %s:%s\n", ACTION, wmq_reason(pqm->reason_code)); \ +if(pqm->comp_code != MQCC_OK) \ +{ \ + if (pqm->exception_on_error) \ + { \ + if(pqm->trace_level) \ + printf ("WMQ::QueueManager#execute() raise WMQ::WMQException\n"); \ + \ + rb_raise(wmq_exception, \ + "WMQ::QueueManager#execute(). Failed:%s, reason:%s", \ + ACTION, \ + wmq_reason(pqm->reason_code)); \ + } \ + return Qfalse; \ +} + +static int QueueManager_execute_each (VALUE key, VALUE value, PQUEUE_MANAGER pqm) +{ + MQLONG selector_type, selector; + VALUE str; + ID selector_id = rb_to_id(key); + + if(pqm->trace_level > 1) + { + str = rb_funcall(key, rb_intern("to_s"), 0); + printf ("WMQ::QueueManager#execute_each Key:%s\n", RSTRING_PTR(str)); + } + + if (ID_command == selector_id) // Skip :command + { + return 0; + } + + wmq_selector(selector_id, &selector_type, &selector); + if(NIL_P(value)) + { + pqm->mqAddInquiry(pqm->admin_bag, selector, &pqm->comp_code, &pqm->reason_code); + CHECK_COMPLETION_CODE("Adding Inquiry to the admin bag") + return 0; + } + + switch (selector_type) + { + case MQIT_INTEGER: + if(TYPE(value) == T_SYMBOL) /* Translate symbol to MQ selector value */ + { + MQLONG val_selector, val_selector_type; + wmq_selector(rb_to_id(value), &val_selector_type, &val_selector); + + pqm->mqAddInteger(pqm->admin_bag, selector, val_selector, &pqm->comp_code, &pqm->reason_code); + } + else + { + pqm->mqAddInteger(pqm->admin_bag, selector, NUM2LONG(value), &pqm->comp_code, &pqm->reason_code); + } + CHECK_COMPLETION_CODE("Adding Queue Type to the admin bag") + break; + + case MQIT_STRING: + str = StringValue(value); + pqm->mqAddString(pqm->admin_bag, selector, MQBL_NULL_TERMINATED, RSTRING_PTR(str), &pqm->comp_code, &pqm->reason_code); + CHECK_COMPLETION_CODE("Adding Queue name to the admin bag") + break; + + default: + rb_raise(rb_eArgError, "WMQ::QueueManager#execute_each Unknown selector type returned by wmq_selector()"); + break; + } + return 0; +} + +/* + * call-seq: + * execute(...) + * + * Execute an Administration command against the local queue manager + * + * Parameters: + * * Since the number of parameters can vary dramatically, all parameters are passed by name in a hash + * * The entire MQ Administration interface has been implemented. + * Rather than re-documentation the hundreds of options, a standard + * convention has been used to map the MQ constants to Symbols in Ruby. + * + * For all MQ Admin commands, just drop the MQAI_ off the front and + * convert the command name to lower case. + * * E.g. MQAI_INQUIRE_Q becomes inquire_q + * + * For the hundreds of parameters, a similiar technique is followed. + * Remove the prefixes: MQCA_, MQIA_, etc.. and convert to lowercase + * * E.g. MQCA_Q_NAME becomes :q_name + * + * Example + * WMQ::QueueManager.connect do |qmgr| + * result = qmgr.execute( + * :command => :inquire_q, + * :q_name => 'MY.LOCAL.QUEUE', + * :q_type => WMQ::MQQT_LOCAL, + * :current_q_depth => nil + * ) + * # OR, we can replace the method name execute with the MQAI command: + * result = qmgr.inquire_q( + * :q_name => 'MY.LOCAL.QUEUE', + * :q_type => WMQ::MQQT_LOCAL, + * :current_q_depth => nil + * ) + * + * Complete Example: + * require 'wmq/wmq' + * require 'wmq/wmq_const_admin' + * WMQ::QueueManager.connect(:q_mgr_name=>'REID', :connection_name=>'localhost(1414)') do |qmgr| + * qmgr.reset_q_stats(:q_name=>'*').each {|item| p item } + * end + * + * Some one line examples + * qmgr.inquire_q(:q_name=>'TEST*').each {|item| p item } + * + * qmgr.inquire_q(:q_name=>'TEST*', :q_type=>WMQ::MQQT_LOCAL, :current_q_depth=>nil).each {|item| p item } + * + * qmgr.inquire_process(:process_name=>'*').each {|item| p item } + * + * qmgr.ping_q_mgr.each {|item| p item } + * + * qmgr.refresh_security.each {|item| p item } + * + * qmgr.inquire_q_status(:q_name=>'TEST*', :q_status_type=>:q_status, :q_status_attrs=>:process_id).each {|item| p item } + * + * qmgr.start_channel_listener.each {|item| p item } + * + * qmgr.inquire_channel_status(:channel_name=>'*').each {|item| p item } + */ +VALUE QueueManager_execute(VALUE self, VALUE hash) +{ +#ifdef MQHB_UNUSABLE_HBAG + VALUE val; + PQUEUE_MANAGER pqm; + Data_Get_Struct(self, QUEUE_MANAGER, pqm); + + Check_Type(hash, T_HASH); + + if (pqm->admin_bag == MQHB_UNUSABLE_HBAG) /* Lazy create admin bag */ + { + pqm->mqCreateBag(MQCBO_ADMIN_BAG, &pqm->admin_bag, &pqm->comp_code, &pqm->reason_code); + CHECK_COMPLETION_CODE("Creating the admin bag") + } + else + { + pqm->mqClearBag(pqm->admin_bag, &pqm->comp_code, &pqm->reason_code); + CHECK_COMPLETION_CODE("Clearing the admin bag") + } + + if (pqm->reply_bag == MQHB_UNUSABLE_HBAG) /* Lazy create reply bag */ + { + pqm->mqCreateBag(MQCBO_ADMIN_BAG, &pqm->reply_bag, &pqm->comp_code, &pqm->reason_code); + CHECK_COMPLETION_CODE("Creating the reply bag") + } + else + { + pqm->mqClearBag(pqm->reply_bag, &pqm->comp_code, &pqm->reason_code); + CHECK_COMPLETION_CODE("Clearing the reply bag") + } + + val = rb_hash_aref(hash, ID2SYM(ID_command)); /* :command */ + if (NIL_P(val)) + { + rb_raise(rb_eArgError, "WMQ::QueueManager#execute Mandatory parameter :command missing"); + } + rb_hash_foreach(hash, QueueManager_execute_each, (VALUE)pqm); + if(pqm->trace_level) printf ("WMQ::QueueManager#execute() Queue Manager Handle:%ld\n", (long)pqm->hcon); + + pqm->mqExecute( + pqm->hcon, /* MQ connection handle */ + wmq_command_lookup(rb_to_id(val)), /* Command to be executed */ + MQHB_NONE, /* No options bag */ + pqm->admin_bag, /* Handle to bag containing commands */ + pqm->reply_bag, /* Handle to bag to receive the response*/ + MQHO_NONE, /* Put msg on SYSTEM.ADMIN.COMMAND.QUEUE*/ + MQHO_NONE, /* Create a dynamic q for the response */ + &pqm->comp_code, /* Completion code from the mqexecute */ + &pqm->reason_code); /* Reason code from mqexecute call */ + + if(pqm->trace_level) printf("WMQ::QueueManager#execute() completed with reason:%s\n", wmq_reason(pqm->reason_code)); + + if (pqm->comp_code == MQCC_OK) + { + MQLONG numberOfBags; /* number of bags in response bag */ + MQHBAG qAttrsBag; /* bag containing q attributes */ + VALUE array; + MQLONG size; + MQLONG length; + MQCHAR inquiry_buffer[WMQ_EXEC_STRING_INQ_BUFFER_SIZE]; + PMQCHAR pChar; + + MQLONG qDepth; /* depth of queue */ + MQLONG item_type; + MQLONG selector; + MQLONG number_of_items; + int bag_index, items, k; + + pqm->mqCountItems(pqm->reply_bag, MQHA_BAG_HANDLE, &numberOfBags, &pqm->comp_code, &pqm->reason_code); + CHECK_COMPLETION_CODE("Counting number of bags returned from the command server") + + if(pqm->trace_level > 1) printf("WMQ::QueueManager#execute() %ld bags returned\n", (long)numberOfBags); + array = rb_ary_new2(numberOfBags); + + for ( bag_index=0; bag_index<numberOfBags; bag_index++) /* For each bag, extract the queue depth */ + { + hash = rb_hash_new(); + + pqm->mqInquireBag(pqm->reply_bag, MQHA_BAG_HANDLE, bag_index, &qAttrsBag, &pqm->comp_code, &pqm->reason_code); + CHECK_COMPLETION_CODE("Inquiring for the attribute bag handle") + + pqm->mqCountItems(qAttrsBag, MQSEL_ALL_SELECTORS, &number_of_items, &pqm->comp_code, &pqm->reason_code); + CHECK_COMPLETION_CODE("Counting number of items in this bag") + + if(pqm->trace_level > 1) printf("WMQ::QueueManager#execute() Bag %d contains %ld items\n", bag_index, (long)number_of_items); + + for (items=0; items<number_of_items; items++) /* For each item, extract it's value */ + { + pqm->mqInquireItemInfo( + qAttrsBag, /* I: Bag handle */ + MQSEL_ANY_SELECTOR, /* I: Item selector */ + items, /* I: Item index */ + &selector, /* O: Selector of item */ + &item_type, /* O: Data type of item */ + &pqm->comp_code, + &pqm->reason_code); + CHECK_COMPLETION_CODE("Inquiring Item details") + + if (selector > 0) /* Skip system selectors */ + { + switch (item_type) + { + case MQIT_INTEGER: + pqm->mqInquireInteger(qAttrsBag, MQSEL_ALL_SELECTORS, items, &qDepth, &pqm->comp_code, &pqm->reason_code); + CHECK_COMPLETION_CODE("Inquiring Integer item") + + if(pqm->trace_level > 1) + printf("WMQ::QueueManager#execute() Item %d: Integer:%ld, selector:%ld\n", items, (long)qDepth, (long)selector); + + rb_hash_aset(hash, ID2SYM(wmq_selector_id(selector)), LONG2NUM(qDepth)); + break; + + case MQIT_STRING: + pqm->mqInquireString(qAttrsBag, MQSEL_ALL_SELECTORS, items, WMQ_EXEC_STRING_INQ_BUFFER_SIZE-1, inquiry_buffer, + &size, NULL, &pqm->comp_code, &pqm->reason_code); + if(pqm->trace_level > 2) + printf("WMQ::QueueManager#execute() mqInquireString buffer size: %d, string size:%ld\n", + WMQ_EXEC_STRING_INQ_BUFFER_SIZE,(long)size); + CHECK_COMPLETION_CODE("Inquiring String item") + + length = 0; + pChar = inquiry_buffer + size-1; + for (k = size; k > 0; k--) + { + if (*pChar != ' ' && *pChar != 0) + { + length = k; + break; + } + pChar--; + } + rb_hash_aset(hash, ID2SYM(wmq_selector_id(selector)), rb_str_new(inquiry_buffer, length)); + + if(pqm->trace_level > 1) + { + inquiry_buffer[length] = '\0'; + printf("WMQ::QueueManager#execute() Item %d: String:'%s', selector:%ld\n", + items, inquiry_buffer, (long)selector); + } + break; + + case MQIT_BAG: + printf("Ignoring Bag at this level\n"); + break; + + default: + printf("Ignoring Unknown type:%ld\n", (long)item_type); + break; + } + } + } + rb_ary_push(array, hash); + } + return array; + } + else + { + VALUE name = rb_iv_get(self,"@name"); + name = StringValue(name); + + if (pqm->reason_code == MQRCCF_COMMAND_FAILED) + { + /* Find out why admin command failed */ + MQLONG result_comp_code, result_reason_code; + MQHBAG result_bag; + + pqm->mqInquireBag(pqm->reply_bag, MQHA_BAG_HANDLE, 0, &result_bag, &pqm->comp_code, &pqm->reason_code); + CHECK_COMPLETION_CODE("Getting the result bag handle") + + pqm->mqInquireInteger(result_bag, MQIASY_COMP_CODE, MQIND_NONE, &result_comp_code, &pqm->comp_code, &pqm->reason_code); + CHECK_COMPLETION_CODE("Getting the completion code from the result bag") + + pqm->mqInquireInteger(result_bag, MQIASY_REASON, MQIND_NONE, &result_reason_code, &pqm->comp_code, &pqm->reason_code); + CHECK_COMPLETION_CODE("Getting the reason code from the result bag") + + pqm->comp_code = result_comp_code; + pqm->reason_code = result_reason_code; + + if(pqm->trace_level) + printf("WMQ::QueueManager#execute() Error returned by command server:%s\n", wmq_reason(pqm->reason_code)); + } + + if (pqm->exception_on_error) + { + if (pqm->reason_code == MQRC_CMD_SERVER_NOT_AVAILABLE) + { + rb_raise(wmq_exception, + "WMQ::QueueManager#execute(). Please start the WebSphere MQ Command Server : 'strmqcsv %s', reason:%s", + RSTRING_PTR(name), + wmq_reason(pqm->reason_code)); + } + else + { + rb_raise(wmq_exception, + "WMQ::QueueManager#execute(). Error executing admin command on Queue Manager:%s, reason:%s", + RSTRING_PTR(name), + wmq_reason(pqm->reason_code)); + } + } + return Qfalse; + } + return Qnil; +#else + rb_notimplement(); + return Qfalse; +#endif +}