ekaranto-rubywmq 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. data/.document +4 -0
  2. data/LICENSE.txt +201 -0
  3. data/README.md +408 -0
  4. data/Rakefile +87 -0
  5. data/examples/each_a.rb +16 -0
  6. data/examples/each_b.rb +25 -0
  7. data/examples/each_header.rb +22 -0
  8. data/examples/files_to_q.cfg +24 -0
  9. data/examples/files_to_q.rb +31 -0
  10. data/examples/get_a.rb +19 -0
  11. data/examples/get_client.rb +35 -0
  12. data/examples/put1_a.rb +9 -0
  13. data/examples/put1_b.rb +17 -0
  14. data/examples/put1_c.rb +16 -0
  15. data/examples/put_a.rb +19 -0
  16. data/examples/put_b.rb +27 -0
  17. data/examples/put_dlh.rb +25 -0
  18. data/examples/put_dynamic_q.rb +22 -0
  19. data/examples/put_group_a.rb +35 -0
  20. data/examples/put_group_b.rb +37 -0
  21. data/examples/put_rfh.rb +51 -0
  22. data/examples/put_rfh2_a.rb +27 -0
  23. data/examples/put_rfh2_b.rb +27 -0
  24. data/examples/put_xmit_q.rb +17 -0
  25. data/examples/q_to_files.cfg +17 -0
  26. data/examples/q_to_files.rb +32 -0
  27. data/examples/request.rb +44 -0
  28. data/examples/server.rb +81 -0
  29. data/ext/decode_rfh.c +348 -0
  30. data/ext/decode_rfh.h +45 -0
  31. data/ext/extconf.rb +30 -0
  32. data/ext/extconf_client.rb +24 -0
  33. data/ext/generate/generate_const.rb +140 -0
  34. data/ext/generate/generate_reason.rb +233 -0
  35. data/ext/generate/generate_structs.rb +82 -0
  36. data/ext/generate/wmq_structs.erb +341 -0
  37. data/ext/wmq.c +90 -0
  38. data/ext/wmq.h +371 -0
  39. data/ext/wmq_message.c +671 -0
  40. data/ext/wmq_mq_load.c +217 -0
  41. data/ext/wmq_queue.c +1411 -0
  42. data/ext/wmq_queue_manager.c +1570 -0
  43. data/lib/rubywmq.rb +1 -0
  44. data/lib/wmq/message.rb +70 -0
  45. data/lib/wmq/queue_manager.rb +110 -0
  46. data/lib/wmq/version.rb +3 -0
  47. data/lib/wmq.rb +16 -0
  48. data/nbproject/project.properties +11 -0
  49. data/nbproject/project.xml +17 -0
  50. data/tests/test.rb +318 -0
  51. metadata +115 -0
data/ext/wmq_queue.c ADDED
@@ -0,0 +1,1411 @@
1
+ /* --------------------------------------------------------------------------
2
+ * Copyright 2006 J. Reid Morrison
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ * --------------------------------------------------------------------------*/
16
+
17
+ #include "wmq.h"
18
+ /* --------------------------------------------------
19
+ * Initialize Ruby ID's for Queue Class
20
+ *
21
+ * This function is called when the library is loaded
22
+ * by ruby
23
+ * --------------------------------------------------*/
24
+ static ID ID_new;
25
+ static ID ID_call;
26
+ static ID ID_close;
27
+ static ID ID_input;
28
+ static ID ID_input_shared;
29
+ static ID ID_input_exclusive;
30
+ static ID ID_output;
31
+ static ID ID_browse;
32
+ static ID ID_sync;
33
+ static ID ID_new_id;
34
+ static ID ID_new_msg_id;
35
+ static ID ID_new_correl_id;
36
+ static ID ID_convert;
37
+ static ID ID_wait;
38
+ static ID ID_q_name;
39
+ static ID ID_q_mgr_name;
40
+ static ID ID_match;
41
+ static ID ID_options;
42
+ static ID ID_open_options;
43
+ static ID ID_mode;
44
+ static ID ID_fail_if_quiescing;
45
+ static ID ID_queue_manager;
46
+ static ID ID_dynamic_q_name;
47
+ static ID ID_close_options;
48
+ static ID ID_fail_if_exists;
49
+ static ID ID_alternate_user_id;
50
+ static ID ID_alternate_security_id;
51
+ static ID ID_message;
52
+ static ID ID_descriptor;
53
+
54
+ void Queue_id_init()
55
+ {
56
+ ID_new = rb_intern("new");
57
+ ID_call = rb_intern("call");
58
+ ID_close = rb_intern("close");
59
+
60
+ ID_input = rb_intern("input");
61
+ ID_input_shared = rb_intern("input_shared");
62
+ ID_input_exclusive = rb_intern("input_exclusive");
63
+ ID_output = rb_intern("output");
64
+ ID_browse = rb_intern("browse");
65
+ ID_q_name = rb_intern("q_name");
66
+ ID_q_mgr_name = rb_intern("q_mgr_name");
67
+ ID_queue_manager = rb_intern("queue_manager");
68
+
69
+ ID_sync = rb_intern("sync");
70
+ ID_new_id = rb_intern("new_id");
71
+ ID_new_msg_id = rb_intern("new_msg_id");
72
+ ID_new_correl_id = rb_intern("new_correl_id");
73
+ ID_convert = rb_intern("convert");
74
+ ID_wait = rb_intern("wait");
75
+ ID_match = rb_intern("match");
76
+ ID_options = rb_intern("options");
77
+ ID_open_options = rb_intern("open_options");
78
+ ID_mode = rb_intern("mode");
79
+
80
+ ID_message = rb_intern("message");
81
+ ID_descriptor = rb_intern("descriptor");
82
+
83
+ ID_fail_if_quiescing = rb_intern("fail_if_quiescing");
84
+ ID_dynamic_q_name = rb_intern("dynamic_q_name");
85
+ ID_close_options = rb_intern("close_options");
86
+ ID_fail_if_exists = rb_intern("fail_if_exists");
87
+ ID_alternate_security_id = rb_intern("alternate_security_id");
88
+ ID_alternate_user_id = rb_intern("alternate_user_id");
89
+ }
90
+
91
+ typedef struct tagQUEUE QUEUE;
92
+ typedef QUEUE MQPOINTER PQUEUE;
93
+
94
+ struct tagQUEUE {
95
+ MQHOBJ hobj; /* object handle */
96
+ MQHCONN hcon; /* connection handle */
97
+ MQOD od; /* Object Descriptor */
98
+ MQLONG comp_code; /* completion code */
99
+ MQLONG reason_code; /* reason code for MQCONN */
100
+ MQLONG open_options; /* MQOPEN options */
101
+ MQLONG close_options; /* MQCLOSE options */
102
+ MQLONG exception_on_error; /* Non-Zero means throw exception*/
103
+ MQLONG fail_if_exists; /* Non-Zero means open dynamic_q_name directly */
104
+ MQLONG trace_level; /* Trace level. 0==None, 1==Info 2==Debug ..*/
105
+ MQCHAR q_name[MQ_Q_NAME_LENGTH+1]; /* queue name plus null character */
106
+ PMQBYTE p_buffer; /* message buffer */
107
+ MQLONG buffer_size; /* Allocated size of buffer */
108
+
109
+ void(*MQCLOSE)(MQHCONN,PMQHOBJ,MQLONG,PMQLONG,PMQLONG);
110
+ void(*MQGET) (MQHCONN,MQHOBJ,PMQVOID,PMQVOID,MQLONG,PMQVOID,PMQLONG,PMQLONG,PMQLONG);
111
+ void(*MQPUT) (MQHCONN,MQHOBJ,PMQVOID,PMQVOID,MQLONG,PMQVOID,PMQLONG,PMQLONG);
112
+ };
113
+
114
+ /* --------------------------------------------------
115
+ * C Structure to store MQ data types and other
116
+ * C internal values
117
+ * --------------------------------------------------*/
118
+ void QUEUE_free(void* p)
119
+ {
120
+ PQUEUE pq = (PQUEUE)p;
121
+ if(pq->trace_level) printf("WMQ::Queue Freeing QUEUE structure\n");
122
+
123
+ if (pq->hobj) /* Valid Q handle means MQCLOSE was not called */
124
+ {
125
+ printf("WMQ::Queue#close was not called. Automatically calling close()\n");
126
+ pq->MQCLOSE(pq->hcon, &pq->hobj, pq->close_options, &pq->comp_code, &pq->reason_code);
127
+ }
128
+ free(pq->p_buffer);
129
+ free(p);
130
+ }
131
+
132
+ VALUE QUEUE_alloc(VALUE klass)
133
+ {
134
+ static MQOD default_MQOD = {MQOD_DEFAULT};
135
+ PQUEUE pq = ALLOC(QUEUE);
136
+
137
+ pq->hobj = 0;
138
+ pq->hcon = 0;
139
+ memcpy(&pq->od, &default_MQOD, sizeof(MQOD));
140
+ pq->comp_code = 0;
141
+ pq->reason_code = 0;
142
+ pq->open_options = 0;
143
+ pq->close_options = MQCO_NONE;
144
+ pq->exception_on_error = 1;
145
+ pq->trace_level = 0;
146
+ pq->fail_if_exists = 1;
147
+ memset(&pq->q_name, 0, sizeof(pq->q_name));
148
+ pq->buffer_size = 16384;
149
+ pq->p_buffer = ALLOC_N(unsigned char, pq->buffer_size);
150
+
151
+ return Data_Wrap_Struct(klass, 0, QUEUE_free, pq);
152
+ }
153
+
154
+ static MQLONG Queue_extract_open_options(VALUE hash, VALUE name)
155
+ {
156
+ VALUE val;
157
+ MQLONG flag;
158
+ MQLONG mq_open_options = 0;
159
+
160
+ WMQ_HASH2MQLONG(hash,open_options, mq_open_options)
161
+
162
+ val = rb_hash_aref(hash, ID2SYM(ID_mode)); /* :mode */
163
+ if (!NIL_P(val))
164
+ {
165
+ ID mode_id = rb_to_id(val);
166
+
167
+ if(mode_id == ID_output) mq_open_options |= MQOO_OUTPUT;
168
+ else if(mode_id == ID_input) mq_open_options |= MQOO_INPUT_AS_Q_DEF;
169
+ else if(mode_id == ID_input_shared) mq_open_options |= MQOO_INPUT_SHARED;
170
+ else if(mode_id == ID_input_exclusive) mq_open_options |= MQOO_INPUT_EXCLUSIVE;
171
+ else if(mode_id == ID_browse) mq_open_options |= MQOO_BROWSE;
172
+ else
173
+ {
174
+ rb_raise(rb_eArgError,
175
+ "Unknown mode supplied for Queue:%s",
176
+ RSTRING_PTR(name));
177
+ }
178
+ }
179
+ else if (!mq_open_options)
180
+ {
181
+ rb_raise(rb_eArgError,
182
+ "Either :mode or :options is required. Both are missing from hash passed to initialize() for Queue: %s",
183
+ RSTRING_PTR(name));
184
+ }
185
+
186
+ IF_TRUE(fail_if_quiescing, 1) /* Defaults to true */
187
+ {
188
+ mq_open_options |= MQOO_FAIL_IF_QUIESCING;
189
+ }
190
+
191
+ return mq_open_options;
192
+ }
193
+
194
+ void Queue_extract_put_message_options(VALUE hash, PMQPMO ppmo)
195
+ {
196
+ VALUE val;
197
+ MQLONG flag;
198
+
199
+ WMQ_HASH2MQLONG(hash,options, ppmo->Options)
200
+
201
+ IF_TRUE(sync, 0) /* :sync */
202
+ {
203
+ ppmo->Options |= MQPMO_SYNCPOINT;
204
+ }
205
+
206
+ IF_TRUE(fail_if_quiescing, 1) /* Defaults to true */
207
+ {
208
+ ppmo->Options |= MQPMO_FAIL_IF_QUIESCING;
209
+ }
210
+
211
+ IF_TRUE(new_id, 0) /* :new_id */
212
+ {
213
+ ppmo->Options |= MQPMO_NEW_MSG_ID;
214
+ ppmo->Options |= MQPMO_NEW_CORREL_ID;
215
+ }
216
+ else
217
+ {
218
+ IF_TRUE(new_msg_id, 0) /* :new_msg_id */
219
+ {
220
+ ppmo->Options |= MQPMO_NEW_MSG_ID;
221
+ }
222
+
223
+ IF_TRUE(new_correl_id, 0) /* new_correl_id */
224
+ {
225
+ ppmo->Options |= MQPMO_NEW_CORREL_ID;
226
+ }
227
+ }
228
+ return;
229
+ }
230
+
231
+ /* Future Use:
232
+ * *:q_name => ['q_name1', 'q_name2'] # Not implemented: Future Use!!
233
+ * *:q_name => [ {queue_manager=>'QMGR_name',
234
+ * queue =>'queue name1'}, # Future Use!!
235
+ * { ... }
236
+ * ]
237
+ * *:resolved => { queue => 'Resolved queue name',
238
+ * queue_manager => 'Resolved queue manager name' }
239
+ */
240
+
241
+ /*
242
+ * call-seq:
243
+ * new(...)
244
+ *
245
+ * Note:
246
+ * * It is _not_ recommended to create instances of Queue directly, rather user Queue.open. Which
247
+ * creates the queue, opens the queue, executes a supplied code block and then ensures the queue
248
+ * is closed.
249
+ *
250
+ * Parameters:
251
+ * * Since the number of parameters can vary dramatically, all parameters are passed by name in a hash
252
+ * * See Queue.open for details on all parameters
253
+ *
254
+ */
255
+ VALUE Queue_initialize(VALUE self, VALUE hash)
256
+ {
257
+ VALUE str;
258
+ size_t size;
259
+ size_t length;
260
+ VALUE val;
261
+ VALUE q_name;
262
+ PQUEUE pq;
263
+
264
+ Check_Type(hash, T_HASH);
265
+
266
+ Data_Get_Struct(self, QUEUE, pq);
267
+
268
+ val = rb_hash_aref(hash, ID2SYM(ID_queue_manager)); /* :queue_manager */
269
+ if (NIL_P(val))
270
+ {
271
+ rb_raise(rb_eArgError, "Mandatory parameter :queue_manager missing from WMQ::Queue::new");
272
+ }
273
+ else
274
+ {
275
+ PQUEUE_MANAGER pqm;
276
+ Data_Get_Struct(val, QUEUE_MANAGER, pqm);
277
+ pq->exception_on_error = pqm->exception_on_error; /* Copy exception_on_error from Queue Manager setting */
278
+ pq->trace_level = pqm->trace_level; /* Copy trace_level from Queue Manager setting */
279
+
280
+ rb_iv_set(self, "@queue_manager", val);
281
+ }
282
+
283
+ q_name = rb_hash_aref(hash, ID2SYM(ID_q_name)); /* :q_name */
284
+ if (NIL_P(q_name))
285
+ {
286
+ rb_raise(rb_eArgError, "Mandatory parameter :q_name missing from WMQ::Queue::new");
287
+ }
288
+
289
+ /* --------------------------------------------------
290
+ * If :q_name is a hash, extract :q_name and :q_mgr_name
291
+ * --------------------------------------------------*/
292
+ if(TYPE(q_name) == T_HASH)
293
+ {
294
+ if(pq->trace_level)
295
+ printf ("WMQ::Queue::new q_name is a hash\n");
296
+
297
+ WMQ_HASH2MQCHARS(q_name,q_mgr_name, pq->od.ObjectQMgrName)
298
+
299
+ q_name = rb_hash_aref(q_name, ID2SYM(ID_q_name));
300
+ if (NIL_P(q_name))
301
+ {
302
+ rb_raise(rb_eArgError,
303
+ "Mandatory parameter :q_name missing from :q_name hash passed to WMQ::Queue::new");
304
+ }
305
+ }
306
+
307
+ str = StringValue(q_name);
308
+ rb_iv_set(self, "@original_name", str); /* Store original queue name */
309
+ strncpy(pq->q_name, RSTRING_PTR(str), sizeof(pq->q_name));
310
+
311
+ pq->open_options = Queue_extract_open_options(hash, q_name);
312
+
313
+ if(pq->trace_level > 1) printf("WMQ::Queue::new Queue:%s\n", pq->q_name);
314
+
315
+ val = rb_hash_aref(hash, ID2SYM(ID_dynamic_q_name)); /* :dynamic_q_name */
316
+ rb_iv_set(self, "@dynamic_q_name", val);
317
+
318
+ WMQ_HASH2MQBYTES(hash,alternate_security_id, pq->od.AlternateSecurityId)
319
+ WMQ_HASH2MQLONG(hash,close_options, pq->close_options)
320
+ WMQ_HASH2BOOL(hash,fail_if_exists, pq->fail_if_exists)
321
+
322
+ val = rb_hash_aref(hash, ID2SYM(ID_alternate_user_id)); /* :alternate_user_id */
323
+ if (!NIL_P(val))
324
+ {
325
+ WMQ_HASH2MQCHARS(hash,alternate_user_id, pq->od.AlternateUserId)
326
+ pq->open_options |= MQOO_ALTERNATE_USER_AUTHORITY;
327
+ }
328
+
329
+ return Qnil;
330
+ }
331
+
332
+ /*
333
+ * call-seq:
334
+ * open()
335
+ *
336
+ * Open the queue
337
+ *
338
+ * Note:
339
+ * * It is not recommended to use this method to open a queue, since the queue will
340
+ * have to be closed explicitly.
341
+ * * Rather use WMQ::QueueManager#open_queue
342
+ * * If the queue is already open, it will be closed and re-opened.
343
+ * Any errors that occur while closing the queue are ignored.
344
+ * * Custom behavior for Dynamic Queues:
345
+ * When :dynamic_q_name is supplied and MQ fails to
346
+ * open the queue with MQRC_OBJECT_ALREADY_EXISTS,
347
+ * this method will automatically open the existing
348
+ * queue by replacing the queue name with :dynamic_q_name
349
+ *
350
+ * This technique allows programs to dynamically create
351
+ * queues, without being concerned with first checking if
352
+ * the queue is already defined.
353
+ * I.e. Removes the need to have to explicitly create
354
+ * required queues in advance
355
+ * However, in order for this approach to work a
356
+ * Permanent model queue must be used. A Temporary
357
+ * model queue is automatically erased by WMQ when the
358
+ * queue is closed.
359
+ *
360
+ * Persistent messages cannot be put to a
361
+ * temporary dynamic queue!
362
+ *
363
+ * Returns:
364
+ * * true : On Success
365
+ * * false: On Failure
366
+ *
367
+ * comp_code and reason_code are also updated.
368
+ * reason will return a text description of the reason_code
369
+ *
370
+ * Throws:
371
+ * * WMQ::WMQException if comp_code == MQCC_FAILED
372
+ * * Except if :exception_on_error => false was supplied as a parameter
373
+ * to QueueManager.new
374
+ *
375
+ * Example:
376
+ * require 'wmq/wmq_client'
377
+ * queue_manager = WMQ::QueueManager.new(:q_mgr_name =>'REID',
378
+ * :connection_name=>'localhost(1414)')
379
+ * begin
380
+ * queue_manager.connect
381
+ *
382
+ * # Create Queue and clear any messages from the queue
383
+ * in_queue = WMQ::Queue.new(:queue_manager =>queue_manager,
384
+ * :mode =>:input,
385
+ * :dynamic_q_name=>'UNIT.TEST',
386
+ * :q_name =>'SYSTEM.DEFAULT.MODEL.QUEUE',
387
+ * :fail_if_exists=>false)
388
+ * begin
389
+ * in_queue.open
390
+ * in_queue.each { |message| p message.data }
391
+ * ensure
392
+ * # Note: Very important: Must close the queue explicitly
393
+ * in_queue.close
394
+ * end
395
+ * rescue => exc
396
+ * queue_manager.backout
397
+ * raise exc
398
+ * ensure
399
+ * # Note: Very important: Must disconnect from the queue manager explicitly
400
+ * queue_manager.disconnect
401
+ * end
402
+ */
403
+ VALUE Queue_open(VALUE self)
404
+ {
405
+ VALUE name;
406
+ VALUE val;
407
+ VALUE dynamic_q_name;
408
+ MQOD od = {MQOD_DEFAULT}; /* Object Descriptor */
409
+ VALUE queue_manager;
410
+ PQUEUE_MANAGER pqm;
411
+ PQUEUE pq;
412
+ Data_Get_Struct(self, QUEUE, pq);
413
+
414
+ name = rb_iv_get(self,"@original_name"); /* Always open original name */
415
+ if (NIL_P(name))
416
+ {
417
+ rb_raise(rb_eRuntimeError, "Fatal: Queue Name not found in Queue instance");
418
+ }
419
+ name = StringValue(name);
420
+
421
+ strncpy(od.ObjectName, RSTRING_PTR(name), (size_t)MQ_Q_NAME_LENGTH);
422
+
423
+ dynamic_q_name = rb_iv_get(self,"@dynamic_q_name");
424
+ if (!NIL_P(dynamic_q_name))
425
+ {
426
+ val = StringValue(dynamic_q_name);
427
+ strncpy(od.DynamicQName, RSTRING_PTR(dynamic_q_name), (size_t) MQ_Q_NAME_LENGTH);
428
+ if(pq->trace_level>1) printf("WMQ::Queue#open() Using dynamic queue name:%s\n", RSTRING_PTR(dynamic_q_name));
429
+ }
430
+
431
+ queue_manager = rb_iv_get(self,"@queue_manager");
432
+ if (NIL_P(queue_manager))
433
+ {
434
+ rb_raise(rb_eRuntimeError, "Fatal: Queue Manager object not found in Queue instance");
435
+ }
436
+ Data_Get_Struct(queue_manager, QUEUE_MANAGER, pqm);
437
+ pq->MQCLOSE= pqm->MQCLOSE;
438
+ pq->MQGET = pqm->MQGET;
439
+ pq->MQPUT = pqm->MQPUT;
440
+
441
+ pq->hcon = pqm->hcon; /* Store Queue Manager handle for subsequent calls */
442
+
443
+ if(pq->trace_level)
444
+ printf ("WMQ::Queue#open() Opening Queue:%s, Queue Manager Handle:%ld\n", RSTRING_PTR(name), (long)pq->hcon);
445
+
446
+ if(pq->hobj) /* Close queue if already open, ignore errors */
447
+ {
448
+ if(pq->trace_level)
449
+ printf ("WMQ::Queue#open() Queue:%s Already open, closing it!\n", RSTRING_PTR(name));
450
+
451
+ pqm->MQCLOSE(pq->hcon, &pq->hobj, pq->close_options, &pq->comp_code, &pq->reason_code);
452
+ }
453
+
454
+ pqm->MQOPEN(pq->hcon, &od, pq->open_options, &pq->hobj, &pq->comp_code, &pq->reason_code);
455
+
456
+ /* --------------------------------------------------
457
+ * If the Dynamic Queue already exists, just open the
458
+ * dynamic queue name directly
459
+ * --------------------------------------------------*/
460
+ if (pq->reason_code == MQRC_OBJECT_ALREADY_EXISTS &&
461
+ !pq->fail_if_exists &&
462
+ !NIL_P(dynamic_q_name))
463
+ {
464
+ strncpy(od.ObjectName, od.DynamicQName, (size_t) MQ_Q_MGR_NAME_LENGTH);
465
+ od.DynamicQName[0] = 0;
466
+
467
+ if(pq->trace_level)
468
+ printf("WMQ::Queue#open() Queue already exists, re-trying with queue name:%s\n",
469
+ RSTRING_PTR(dynamic_q_name));
470
+
471
+ pqm->MQOPEN(pq->hcon, &od, pq->open_options, &pq->hobj, &pq->comp_code, &pq->reason_code);
472
+ }
473
+
474
+ if(pq->trace_level)
475
+ printf("WMQ::Queue#open() MQOPEN completed with reason:%s, Handle:%ld\n",
476
+ wmq_reason(pq->reason_code),
477
+ (long)pq->hobj);
478
+
479
+ if (pq->comp_code == MQCC_FAILED)
480
+ {
481
+ pq->hobj = 0;
482
+ pq->hcon = 0;
483
+
484
+ if (pq->exception_on_error)
485
+ {
486
+ VALUE name = rb_iv_get(self,"@original_name");
487
+ name = StringValue(name);
488
+
489
+ rb_raise(wmq_exception,
490
+ "WMQ::Queue#open(). Error opening Queue:%s, reason:%s",
491
+ RSTRING_PTR(name),
492
+ wmq_reason(pq->reason_code));
493
+ }
494
+ return Qfalse;
495
+ }
496
+ else
497
+ {
498
+ size_t size;
499
+ size_t length;
500
+ size_t i;
501
+ char* pChar;
502
+
503
+ WMQ_MQCHARS2STR(od.ObjectName, val)
504
+ rb_iv_set(self, "@name", val); /* Store actual queue name E.g. Dynamic Queue */
505
+
506
+ if(pq->trace_level>1) printf("WMQ::Queue#open() Actual Queue Name opened:%s\n", RSTRING_PTR(val));
507
+ }
508
+
509
+ /* Future Use:
510
+ WMQ_MQCHARS2HASH(hash,resolved_q_name, pmqod->ResolvedQName)
511
+ WMQ_MQCHARS2HASH(hash,resolved_q_mgr_name, pmqod->ResolvedQMgrName)
512
+ */
513
+
514
+ return Qtrue;
515
+ }
516
+
517
+ /*
518
+ * Close the queue
519
+ *
520
+ * Returns:
521
+ * * true : On Success
522
+ * * false: On Failure
523
+ *
524
+ * comp_code and reason_code are also updated.
525
+ * reason will return a text description of the reason_code
526
+ *
527
+ * Throws:
528
+ * * WMQ::WMQException if comp_code == MQCC_FAILED
529
+ * * Except if :exception_on_error => false was supplied as a parameter
530
+ * to QueueManager.new
531
+ */
532
+ VALUE Queue_close(VALUE self)
533
+ {
534
+ PQUEUE pq;
535
+ Data_Get_Struct(self, QUEUE, pq);
536
+
537
+ /* Check if queue is open */
538
+ if (!pq->hcon)
539
+ {
540
+ if(pq->trace_level) printf ("WMQ::Queue#close() Queue not open\n");
541
+ return Qtrue;
542
+ }
543
+
544
+ if(pq->trace_level) printf ("WMQ::Queue#close() Queue Handle:%ld, Queue Manager Handle:%ld\n", (long)pq->hobj, (long)pq->hcon);
545
+
546
+ pq->MQCLOSE(pq->hcon, &pq->hobj, pq->close_options, &pq->comp_code, &pq->reason_code);
547
+
548
+ pq->hcon = 0; /* Every time the queue is opened, the qmgr handle must be fetched again! */
549
+ pq->hobj = 0;
550
+
551
+ if(pq->trace_level) printf("WMQ::Queue#close() MQCLOSE ended with reason:%s\n", wmq_reason(pq->reason_code));
552
+
553
+ if (pq->comp_code == MQCC_FAILED)
554
+ {
555
+ if (pq->exception_on_error)
556
+ {
557
+ VALUE name = Queue_name(self);
558
+
559
+ rb_raise(wmq_exception,
560
+ "WMQ::Queue#close(). Error closing Queue:%s, reason:%s",
561
+ RSTRING_PTR(name),
562
+ wmq_reason(pq->reason_code));
563
+ }
564
+ return Qfalse;
565
+ }
566
+
567
+ return Qtrue;
568
+ }
569
+
570
+ /*
571
+ * call-seq:
572
+ * get(...)
573
+ *
574
+ * Get a message from the opened queue
575
+ *
576
+ * Parameters:
577
+ * * a Hash consisting of one or more of the named parameters
578
+ * * Summary of parameters and their WebSphere MQ equivalents:
579
+ * queue.get( # WebSphere MQ Equivalents:
580
+ * :message => my_message, # n/a : Instance of Message
581
+ * :sync => false, # MQGMO_SYNCPOINT
582
+ * :wait => 0, # MQGMO_WAIT, duration in ms
583
+ * :match => WMQ::MQMO_NONE, # MQMO_*
584
+ * :convert => false, # MQGMO_CONVERT
585
+ * :fail_if_quiescing => true # MQOO_FAIL_IF_QUIESCING
586
+ * :options => WMQ::MQGMO_FAIL_IF_QUIESCING # MQGMO_*
587
+ * )
588
+ *
589
+ * Mandatory Parameters
590
+ * * :message => Message
591
+ * * An instance of the WMQ::Message
592
+ *
593
+ * Optional Parameters
594
+ * * :sync => true or false
595
+ * * Determines whether the get is performed under synchpoint.
596
+ * I.e. Under the current unit of work
597
+ * Default: false
598
+ *
599
+ * * :wait => FixNum
600
+ * * The time in milli-seconds to wait for a message if one is not immediately available
601
+ * on the queue
602
+ * * Note: Under the covers the put option MQGMO_WAIT is automatically set when :wait
603
+ * is supplied
604
+ * Default: Wait forever
605
+ *
606
+ * * :match => FixNum
607
+ * * One or more of the following values:
608
+ * WMQ::MQMO_MATCH_MSG_ID
609
+ * WMQ::MQMO_MATCH_CORREL_ID
610
+ * WMQ::MQMO_MATCH_GROUP_ID
611
+ * WMQ::MQMO_MATCH_MSG_SEQ_NUMBER
612
+ * WMQ::MQMO_MATCH_OFFSET
613
+ * WMQ::MQMO_MATCH_MSG_TOKEN
614
+ * WMQ::MQMO_NONE
615
+ * * Multiple values can be or'd together. E.g.
616
+ * :match=>WMQ::MQMO_MATCH_MSG_ID | WMQ::MQMO_MATCH_CORREL_ID
617
+ * * Please see the WebSphere MQ documentation for more details on the above options
618
+ * Default: WMQ::MQMO_MATCH_MSG_ID | WMQ::MQMO_MATCH_CORREL_ID
619
+ *
620
+ * * :convert => true or false
621
+ * * When true, convert results in messages being converted to the local code page.
622
+ * E.g. When an EBCDIC text message from a mainframe is received, it will be converted
623
+ * to ASCII before passing the message data to the application.
624
+ * Default: false
625
+ *
626
+ * * :fail_if_quiescing => true or false
627
+ * * Determines whether the WMQ::Queue#get call will fail if the queue manager is
628
+ * in the process of being quiesced.
629
+ * * Note: This interface differs from other WebSphere MQ interfaces,
630
+ * they do not default to true.
631
+ * Default: true
632
+ *
633
+ * * :options => Fixnum (Advanced MQ Use only)
634
+ * * Numeric field containing any of the MQ Get message options or'd together
635
+ * * E.g. :options => WMQ::MQGMO_SYNCPOINT_IF_PERSISTENT | WMQ::MQGMO_MARK_SKIP_BACKOUT
636
+ * * Note: If :options is supplied, it is applied first, then the above parameters are
637
+ * applied afterwards.
638
+ * * One or more of the following values:
639
+ * WMQ::MQGMO_SYNCPOINT_IF_PERSISTENT
640
+ * WMQ::MQGMO_NO_SYNCPOINT
641
+ * WMQ::MQGMO_MARK_SKIP_BACKOUT
642
+ * WMQ::MQGMO_BROWSE_FIRST
643
+ * WMQ::MQGMO_BROWSE_NEXT
644
+ * WMQ::MQGMO_BROWSE_MSG_UNDER_CURSOR
645
+ * WMQ::MQGMO_MSG_UNDER_CURSOR
646
+ * WMQ::MQGMO_LOCK
647
+ * WMQ::MQGMO_UNLOCK
648
+ * WMQ::MQGMO_LOGICAL_ORDER
649
+ * WMQ::MQGMO_COMPLETE_MSG
650
+ * WMQ::MQGMO_ALL_MSGS_AVAILABLE
651
+ * WMQ::MQGMO_ALL_SEGMENTS_AVAILABLE
652
+ * WMQ::MQGMO_DELETE_MSG
653
+ * WMQ::MQGMO_NONE
654
+ * * Please see the WebSphere MQ documentation for more details on the above options
655
+ * Default: WMQ::MQGMO_NONE
656
+ *
657
+ * Returns:
658
+ * * true : On Success
659
+ * * false: On Failure, or if no message was found on the queue during the wait interval
660
+ *
661
+ * comp_code and reason_code are also updated.
662
+ * reason will return a text description of the reason_code
663
+ *
664
+ * Throws:
665
+ * * WMQ::WMQException if comp_code == MQCC_FAILED
666
+ * * Except if :exception_on_error => false was supplied as a parameter
667
+ * to QueueManager.new
668
+ *
669
+ * Example:
670
+ * require 'wmq/wmq'
671
+ *
672
+ * WMQ::QueueManager.connect(:q_mgr_name=>'REID') do |qmgr|
673
+ * qmgr.open_queue(:q_name=>'TEST.QUEUE', :mode=>:input) do |queue|
674
+ * message = WMQ::Message.new
675
+ * if queue.get(:message => message)
676
+ * puts "Data Received: #{message.data}"
677
+ * else
678
+ * puts 'No message available'
679
+ * end
680
+ * end
681
+ * end
682
+ */
683
+ VALUE Queue_get(VALUE self, VALUE hash)
684
+ {
685
+ VALUE val;
686
+ VALUE message;
687
+ PQUEUE pq;
688
+ MQLONG flag;
689
+ MQLONG messlen; /* message length received */
690
+
691
+ MQMD md = {MQMD_DEFAULT}; /* Message Descriptor */
692
+ MQGMO gmo = {MQGMO_DEFAULT}; /* get message options */
693
+
694
+ md.Version = MQMD_CURRENT_VERSION; /* Allow Group Options */
695
+ gmo.Version = MQGMO_CURRENT_VERSION; /* Allow MatchOptions */
696
+
697
+ Check_Type(hash, T_HASH);
698
+
699
+ Data_Get_Struct(self, QUEUE, pq);
700
+
701
+ /* Automatically open the queue if not already open */
702
+ if (!pq->hcon && (Queue_open(self) == Qfalse))
703
+ {
704
+ return Qfalse;
705
+ }
706
+
707
+ message = rb_hash_aref(hash, ID2SYM(ID_message));
708
+ if (NIL_P(message))
709
+ {
710
+ VALUE name = Queue_name(self);
711
+
712
+ rb_raise(rb_eArgError,
713
+ "Mandatory key :message is missing from hash passed to get() for Queue: %s",
714
+ RSTRING_PTR(name));
715
+ }
716
+
717
+ Message_build_mqmd(message, &md);
718
+
719
+ WMQ_HASH2MQLONG(hash,options, gmo.Options) /* :options */
720
+
721
+ IF_TRUE(sync, 0) /* :sync defaults to false */
722
+ {
723
+ gmo.Options |= MQGMO_SYNCPOINT;
724
+ }
725
+
726
+ IF_TRUE(fail_if_quiescing, 1) /* :fail_if_quiescing defaults to true */
727
+ {
728
+ gmo.Options |= MQGMO_FAIL_IF_QUIESCING;
729
+ }
730
+
731
+ IF_TRUE(convert, 0) /* :convert defaults to false */
732
+ {
733
+ gmo.Options |= MQGMO_CONVERT;
734
+ }
735
+
736
+ val = rb_hash_aref(hash, ID2SYM(ID_wait)); /* :wait */
737
+ if (!NIL_P(val))
738
+ {
739
+ gmo.Options |= MQGMO_WAIT;
740
+ gmo.WaitInterval = NUM2LONG(val);
741
+ }
742
+
743
+ WMQ_HASH2MQLONG(hash,match, gmo.MatchOptions) /* :match */
744
+
745
+ if(pq->trace_level > 1) printf("WMQ::Queue#get() Get Message Option: MatchOptions=%ld\n", (long)gmo.MatchOptions);
746
+ if(pq->trace_level) printf("WMQ::Queue#get() Queue Handle:%ld, Queue Manager Handle:%ld\n", (long)pq->hobj, (long)pq->hcon);
747
+
748
+ /* If descriptor is re-used
749
+
750
+ md.Encoding = MQENC_NATIVE;
751
+ md.CodedCharSetId = MQCCSI_Q_MGR;
752
+ */
753
+
754
+ /*
755
+ * Auto-Grow buffer size
756
+ *
757
+ * Note: If msg size is 70,000, we grow to 70,000, but then another program gets that
758
+ * message. The next message could be say 80,000 bytes in size, we need to
759
+ * grow the buffer again.
760
+ */
761
+ do
762
+ {
763
+ pq->MQGET(
764
+ pq->hcon, /* connection handle */
765
+ pq->hobj, /* object handle */
766
+ &md, /* message descriptor */
767
+ &gmo, /* get message options */
768
+ pq->buffer_size, /* message buffer size */
769
+ pq->p_buffer, /* message buffer */
770
+ &messlen, /* message length */
771
+ &pq->comp_code, /* completion code */
772
+ &pq->reason_code); /* reason code */
773
+
774
+ /* report reason, if any */
775
+ if (pq->reason_code != MQRC_NONE)
776
+ {
777
+ if(pq->trace_level>1) printf("WMQ::Queue#get() Growing buffer size from %ld to %ld\n", (long)pq->buffer_size, (long)messlen);
778
+ /* TODO: Add support for autogrow buffer here */
779
+ if (pq->reason_code == MQRC_TRUNCATED_MSG_FAILED)
780
+ {
781
+ if(pq->trace_level>2)
782
+ printf ("WMQ::Queue#reallocate Resizing buffer from %ld to %ld bytes\n", (long)pq->buffer_size, (long)messlen);
783
+
784
+ free(pq->p_buffer);
785
+ pq->buffer_size = messlen;
786
+ pq->p_buffer = ALLOC_N(unsigned char, messlen);
787
+ }
788
+ }
789
+ }
790
+ while (pq->reason_code == MQRC_TRUNCATED_MSG_FAILED);
791
+
792
+ if(pq->trace_level) printf("WMQ::Queue#get() MQGET ended with reason:%s\n", wmq_reason(pq->reason_code));
793
+
794
+ if (pq->comp_code != MQCC_FAILED)
795
+ {
796
+ Message_deblock(message, &md, pq->p_buffer, messlen, pq->trace_level); /* Extract MQMD and any other known MQ headers */
797
+ return Qtrue;
798
+ }
799
+ else
800
+ {
801
+ Message_clear(message);
802
+
803
+ /* --------------------------------------------------
804
+ * Do not throw exception when no more messages to be read
805
+ * --------------------------------------------------*/
806
+ if (pq->exception_on_error && (pq->reason_code != MQRC_NO_MSG_AVAILABLE))
807
+ {
808
+ VALUE name = Queue_name(self);
809
+
810
+ rb_raise(wmq_exception,
811
+ "WMQ::Queue#get(). Error reading a message from Queue:%s, reason:%s",
812
+ RSTRING_PTR(name),
813
+ wmq_reason(pq->reason_code));
814
+ }
815
+ return Qfalse;
816
+ }
817
+ }
818
+
819
+ /*
820
+ * call-seq:
821
+ * put(...)
822
+ *
823
+ * Put a message to the WebSphere MQ queue
824
+ *
825
+ * Parameters:
826
+ * * A Hash consisting of one or more of the named parameters
827
+ * * Summary of parameters and their WebSphere MQ equivalents
828
+ * queue.put( # WebSphere MQ Equivalents:
829
+ * :message => my_message, # n/a : Instance of Message
830
+ * :data => "Hello World", # n/a : Data to send
831
+ * :sync => false, # MQGMO_SYNCPOINT
832
+ * :new_id => true, # MQPMO_NEW_MSG_ID & MQPMO_NEW_CORREL_ID
833
+ * :new_msg_id => true, # MQPMO_NEW_MSG_ID
834
+ * :new_correl_id => true, # MQPMO_NEW_CORREL_ID
835
+ * :fail_if_quiescing => true, # MQOO_FAIL_IF_QUIESCING
836
+ * :options => WMQ::MQPMO_FAIL_IF_QUIESCING # MQPMO_*
837
+ * )
838
+ *
839
+ * Mandatory Parameters:
840
+ *
841
+ * * Either :message or :data must be supplied
842
+ * * If both are supplied, then :data will be written to the queue. The data in :message
843
+ * will be ignored
844
+ *
845
+ * Optional Parameters:
846
+ * * :data => String
847
+ * * Data to be written to the queue. Can be binary or text data
848
+ * * Takes precendence over the data in :message
849
+ *
850
+ * * :message => Message
851
+ * * An instance of the WMQ::Message
852
+ * * The Message descriptor, headers and data is retrieved from :message
853
+ * * message.data is ignored if :data is supplied
854
+ *
855
+ * * :sync => true or false
856
+ * * Determines whether the get is performed under synchpoint.
857
+ * I.e. Under the current unit of work
858
+ * Default: false
859
+ *
860
+ * * :new_id => true or false
861
+ * * Generate a new message id and correlation id for this
862
+ * message. :new_msg_id and :new_correl_id will be ignored
863
+ * if this parameter is true
864
+ * Default: false
865
+ *
866
+ * * :new_msg_id => true or false
867
+ * * Generate a new message id for this message
868
+ * * Note: A blank message id will result in a new message id anyway.
869
+ * However, for subsequent puts using the same message descriptor, the same
870
+ * message id will be used.
871
+ * Default: false
872
+ *
873
+ * * :new_correl_id => true or false
874
+ * * Generate a new correlation id for this message
875
+ * Default: false
876
+ *
877
+ * * :fail_if_quiescing => true or false
878
+ * * Determines whether the WMQ::Queue#put call will fail if the queue manager is
879
+ * in the process of being quiesced.
880
+ * * Note: This interface differs from other WebSphere MQ interfaces,
881
+ * they do not default to true.
882
+ * Default: true
883
+ * Equivalent to: MQGMO_FAIL_IF_QUIESCING
884
+ *
885
+ * * Note: As part of the application design, carefull consideration
886
+ * should be given as to when to allow a transaction or
887
+ * unit of work to complete or fail under this condition.
888
+ * As such it is important to include this option where
889
+ * appropriate so that MQ Administrators can shutdown the
890
+ * queue managers without having to resort to the 'immediate'
891
+ * shutdown option.
892
+ *
893
+ * * :options => Fixnum (Advanced MQ Use only)
894
+ * * Numeric field containing any of the MQ Put message options or'd together
895
+ * * E.g. :options => WMQ::MQPMO_PASS_IDENTITY_CONTEXT | WMQ::MQPMO_ALTERNATE_USER_AUTHORITY
896
+ * * Note: If :options is supplied, it is applied first, then the above parameters are
897
+ * applied afterwards.
898
+ * * One or more of the following values:
899
+ * WMQ::MQPMO_NO_SYNCPOINT
900
+ * WMQ::MQPMO_LOGICAL_ORDER
901
+ * WMQ::MQPMO_NO_CONTEXT
902
+ * WMQ::MQPMO_DEFAULT_CONTEXT
903
+ * WMQ::MQPMO_PASS_IDENTITY_CONTEXT
904
+ * WMQ::MQPMO_PASS_ALL_CONTEXT
905
+ * WMQ::MQPMO_SET_IDENTITY_CONTEXT
906
+ * WMQ::MQPMO_SET_ALL_CONTEXT
907
+ * WMQ::MQPMO_ALTERNATE_USER_AUTHORITY
908
+ * WMQ::MQPMO_RESOLVE_LOCAL_Q
909
+ * WMQ::MQPMO_NONE
910
+ * * Please see the WebSphere MQ documentation for more details on the above options
911
+ * Default: WMQ::MQPMO_NONE
912
+ *
913
+ * Returns:
914
+ * * true : On Success
915
+ * * false: On Failure
916
+ *
917
+ * comp_code and reason_code are also updated.
918
+ * reason will return a text description of the reason_code
919
+ *
920
+ * Throws:
921
+ * * WMQ::WMQException if comp_code == MQCC_FAILED
922
+ * * Except if :exception_on_error => false was supplied as a parameter
923
+ * to QueueManager.new
924
+ *
925
+ * Example:
926
+ * require 'wmq/wmq_client'
927
+ *
928
+ * WMQ::QueueManager.connect(:q_mgr_name=>'REID', :connection_name=>'localhost(1414)') do |qmgr|
929
+ * qmgr.open_queue(:q_name=>'TEST.QUEUE', :mode=>:output) do |queue|
930
+ *
931
+ * # First message
932
+ * queue.put(:data => 'Hello World')
933
+ *
934
+ * # Set Format of message to string
935
+ * message = WMQ::Message.new
936
+ * message.descriptor[:format] = WMQ::MQFMT_STRING
937
+ * queue.put(:message=>message, :data => 'Hello Again')
938
+ *
939
+ * # Or, pass the data in the message
940
+ * message = WMQ::Message.new
941
+ * message.descriptor[:format] = WMQ::MQFMT_STRING
942
+ * message.data = 'Hello Again'
943
+ * queue.put(:message=>message)
944
+ * end
945
+ * end
946
+ */
947
+ VALUE Queue_put(VALUE self, VALUE hash)
948
+ {
949
+ MQPMO pmo = {MQPMO_DEFAULT}; /* put message options */
950
+ MQMD md = {MQMD_DEFAULT}; /* Message Descriptor */
951
+ PQUEUE pq;
952
+ MQLONG BufferLength = 0; /* Length of the message in Buffer */
953
+ PMQVOID pBuffer = 0; /* Message data */
954
+
955
+ md.Version = MQMD_CURRENT_VERSION; /* Allow Group Options */
956
+
957
+ Check_Type(hash, T_HASH);
958
+
959
+ Data_Get_Struct(self, QUEUE, pq);
960
+
961
+ /* Automatically open the queue if not already open */
962
+ if (!pq->hcon && (Queue_open(self) == Qfalse))
963
+ {
964
+ return Qfalse;
965
+ }
966
+
967
+ Queue_extract_put_message_options(hash, &pmo);
968
+ Message_build(&pq->p_buffer, &pq->buffer_size, pq->trace_level,
969
+ hash, &pBuffer, &BufferLength, &md);
970
+
971
+ if(pq->trace_level) printf("WMQ::Queue#put() Queue Handle:%ld, Queue Manager Handle:%ld\n", (long)pq->hobj, (long)pq->hcon);
972
+
973
+ pq->MQPUT(
974
+ pq->hcon, /* connection handle */
975
+ pq->hobj, /* object handle */
976
+ &md, /* message descriptor */
977
+ &pmo, /* put message options */
978
+ BufferLength, /* message length */
979
+ pBuffer, /* message buffer */
980
+ &pq->comp_code, /* completion code */
981
+ &pq->reason_code); /* reason code */
982
+
983
+ if(pq->trace_level) printf("WMQ::Queue#put() MQPUT ended with reason:%s\n", wmq_reason(pq->reason_code));
984
+
985
+ if (pq->reason_code != MQRC_NONE)
986
+ {
987
+ if (pq->exception_on_error)
988
+ {
989
+ VALUE name = Queue_name(self);
990
+
991
+ rb_raise(wmq_exception,
992
+ "WMQ::Queue#put(). Error writing a message to Queue:%s, reason:%s",
993
+ RSTRING_PTR(name),
994
+ wmq_reason(pq->reason_code));
995
+ }
996
+ return Qfalse;
997
+ }
998
+ else
999
+ {
1000
+ VALUE message = rb_hash_aref(hash, ID2SYM(ID_message));
1001
+ if(!NIL_P(message))
1002
+ {
1003
+ VALUE descriptor = rb_funcall(message, ID_descriptor, 0);
1004
+ Message_from_mqmd(descriptor, &md); /* This should be optimized to output only fields */
1005
+ }
1006
+ }
1007
+
1008
+ return Qtrue;
1009
+ }
1010
+
1011
+ /*
1012
+ * Returns the queue name => String
1013
+ */
1014
+ VALUE Queue_name(VALUE self)
1015
+ {
1016
+ /* If Queue is open, return opened name, otherwise return original name */
1017
+ PQUEUE pq;
1018
+ Data_Get_Struct(self, QUEUE, pq);
1019
+ if (pq->hobj)
1020
+ {
1021
+ return rb_iv_get(self,"@name");
1022
+ }
1023
+ return rb_iv_get(self,"@original_name");
1024
+ }
1025
+
1026
+ struct Queue_singleton_open_arg {
1027
+ VALUE queue;
1028
+ VALUE proc;
1029
+ };
1030
+
1031
+ static VALUE Queue_singleton_open_body(struct Queue_singleton_open_arg* arg)
1032
+ {
1033
+ rb_funcall(arg->proc, ID_call, 1, arg->queue);
1034
+ return Qnil;
1035
+ }
1036
+
1037
+ static VALUE Queue_singleton_open_ensure(VALUE queue)
1038
+ {
1039
+ return Queue_close(queue);
1040
+ }
1041
+
1042
+ /*
1043
+ * call-seq:
1044
+ * open(...)
1045
+ *
1046
+ * Open a queue, then close the queue once the supplied code block completes
1047
+ *
1048
+ * Parameters:
1049
+ * * Since the number of parameters can vary dramatically, all parameters are passed by name in a hash
1050
+ * * Summary of parameters and their WebSphere MQ equivalents:
1051
+ * queue = Queue.new( # WebSphere MQ Equivalents:
1052
+ * :queue_manager => queue_manager, # n/a : Instance of QueueManager
1053
+ * :q_name => 'Queue Name', # MQOD.ObjectName
1054
+ * :q_name => { queue_manager=>'QMGR_name', # MQOD.ObjectQMgrName
1055
+ * q_name =>'q_name'}
1056
+ * :mode => :input or :input_shared or :input_exclusive or :output,
1057
+ * :fail_if_quiescing => true # MQOO_FAIL_IF_QUIESCING
1058
+ * :fail_if_exists => true, # For dynamic queues, fail if it already exists
1059
+ * :open_options => WMQ::MQOO_BIND_ON_OPEN | ... # MQOO_*
1060
+ * :close_options => WMQ::MQCO_DELETE_PURGE # MQCO_*
1061
+ * :dynamic_q_name => 'Name of Dynamic Queue' # MQOD.DynamicQName
1062
+ * :alternate_user_id => 'userid', # MQOD.AlternateUserId
1063
+ * :alternate_security_id => '' # MQOD.AlternateSecurityId
1064
+ * )
1065
+ *
1066
+ * Mandatory Parameters
1067
+ * * :queue_manager
1068
+ * * An instance of the WMQ::QueueManager class. E.g. QueueManager.new
1069
+ * * Note: This is _not_ the queue manager name!
1070
+ *
1071
+ * * :q_name => String
1072
+ * * Name of the existing WebSphere MQ local queue, model queue or remote queue to open
1073
+ * * To open remote queues for which a local remote queue definition is not available
1074
+ * pass a Hash as q_name (see q_name => Hash)
1075
+ * OR
1076
+ * * :q_name => Hash
1077
+ * * q_name => String
1078
+ * * Name of the existing WebSphere MQ local queue, model queue or remote queue to open
1079
+ * * :q_mgr_name => String
1080
+ * * Name of the remote WebSphere MQ queue manager to send the message to.
1081
+ * * This allows a message to be written to a queue on a remote queue manager
1082
+ * where a remote queue definition is not defined locally
1083
+ * * Commonly used to reply to messages from remote systems
1084
+ * * If q_mgr_name is the same as the local queue manager name then the message
1085
+ * is merely written to the local queue.
1086
+ * * Note: q_mgr_name should only be supplied when putting messages to the queue.
1087
+ * It is not possible to get messages from a queue on a queue manager other
1088
+ * than the currently connected queue manager
1089
+ *
1090
+ * * :mode => Symbol
1091
+ * * Specify how the queue is to be opened
1092
+ * * :output
1093
+ * * Open the queue for output. I.e. WMQ::Queue#put will be called
1094
+ * Equivalent to: MQOO_OUTPUT
1095
+ * * :input
1096
+ * * Open the queue for input. I.e. WMQ::Queue#get will be called.
1097
+ * * Queue sharing for reading from the queue is defined by the queue itself.
1098
+ * By default most queues are set to shared. I.e. Multiple applications
1099
+ * can read and/or write from the queue at the same time
1100
+ * Equivalent to: MQOO_INPUT_AS_Q_DEF
1101
+ * * :input_shared
1102
+ * * Open the queue for input. I.e. WMQ::Queue#get will be called.
1103
+ * * Explicitly open the queue so that other applications can read or write
1104
+ * from the same queue
1105
+ * Equivalent to: MQOO_INPUT_SHARED
1106
+ * * :input_exclusive
1107
+ * * Open the queue for input. I.e. WMQ::Queue#get will be called.
1108
+ * * Explicitly open the queue so that other applications cannot read
1109
+ * from the same queue. Does _not_ affect applications writing to the queue.
1110
+ * * Note: If :input_exclusive is used and connectivity the queue manager is lost.
1111
+ * Upon restart the queue can still be "locked". The application should retry
1112
+ * every minute or so until the queue becomes available. Otherwise, of course,
1113
+ * another application has the queue open exclusively.
1114
+ * Equivalent to: MQOO_INPUT_EXCLUSIVE
1115
+ * * :browse
1116
+ * * Browse the messages on the queue _without_ removing them from the queue
1117
+ * * Open the queue for input. I.e. WMQ::Queue#get will be called.
1118
+ * * Note: It is necessary to specify WMQ::MQGMO_BROWSE_FIRST before the
1119
+ * first get, then set WMQ::MQGMO_BROWSE_NEXT for subsequent calls.
1120
+ * * Note: For now it is also necessary to specify these options when calling
1121
+ * WMQ::Queue#each. A change will be made to each to address this.
1122
+ * Equivalent to: MQOO_BROWSE
1123
+ *
1124
+ * Optional Parameters
1125
+ * * :fail_if_quiescing => true or false
1126
+ * * Determines whether the WMQ::Queue#open call will fail if the queue manager is
1127
+ * in the process of being quiesced.
1128
+ * * Note: If set to false, the MQOO_FAIL_IF_QUIESCING flag will not be removed if
1129
+ * it was also supplied in :open_options. However, if set to true it will override
1130
+ * this value in :open_options
1131
+ * * Note: This interface differs from other WebSphere MQ interfaces,
1132
+ * they do not default to true.
1133
+ * Default: true
1134
+ * Equivalent to: MQOO_FAIL_IF_QUIESCING
1135
+ *
1136
+ * * :open_options => FixNum
1137
+ * * One or more of the following values:
1138
+ * WMQ::MQOO_INQUIRE
1139
+ * WMQ::MQOO_SET
1140
+ * WMQ::MQOO_BIND_ON_OPEN
1141
+ * WMQ::MQOO_BIND_NOT_FIXED
1142
+ * WMQ::MQOO_BIND_AS_Q_DEF
1143
+ * WMQ::MQOO_SAVE_ALL_CONTEXT
1144
+ * WMQ::MQOO_PASS_IDENTITY_CONTEXT
1145
+ * WMQ::MQOO_PASS_ALL_CONTEXT
1146
+ * WMQ::MQOO_SET_IDENTITY_CONTEXT
1147
+ * WMQ::MQOO_SET_ALL_CONTEXT
1148
+ * * Multiple values can be or'd together. E.g.
1149
+ * :open_options=>WMQ::MQOO_BIND_ON_OPEN | WMQ::MQOO_SAVE_ALL_CONTEXT
1150
+ * * Please see the WebSphere MQ documentation for more details on the above options
1151
+ *
1152
+ * * :close_options => FixNum
1153
+ * * One of the following values:
1154
+ * WMQ::MQCO_DELETE
1155
+ * WMQ::MQCO_DELETE_PURGE
1156
+ * * Please see the WebSphere MQ documentation for more details on the above options
1157
+ *
1158
+ * * :dynamic_q_name => String
1159
+ * * If a model queue name is supplied to :q_name then the final queue name that is
1160
+ * created is specified using :dynamic_q_name
1161
+ * * A complete queue name can be specified. E.g. 'MY.LOCAL.QUEUE'
1162
+ * * Or, a partial queue name can be supplied. E.g. 'MY.REPLY.QUEUE.*'
1163
+ * In this case WebSphere MQ will automatically add numbers to the end
1164
+ * of 'MY.REPLY.QUEUE.' to ensure this queue name is unique.
1165
+ * * The most common use of :dynamic_q_name is to create a temporary dynamic queue
1166
+ * to which replies can be posted for this instance of the program
1167
+ * * When opening a model queue, :dynamic_q_name is optional. However it's use is
1168
+ * recommended in order to make it easier to identify which application a
1169
+ * dynamic queue belongs to.
1170
+ *
1171
+ * * :fail_if_exists => true or false
1172
+ * * Only applicable when opening a model queue
1173
+ * * When opening a queue dynamically, sometimes the :dynamic_q_name already
1174
+ * exists. Under this condition, if :fail_if_exists is false, the queue is
1175
+ * automatically re-opened using the :dynamic_q_name. The :q_name field is ignored.
1176
+ * * This feature is usefull when creating _permanent_ dynamic queues.
1177
+ * (The model queue has the definition type set to Permanent: DEFTYPE(PERMDYN) ).
1178
+ * * In this way it is not necessary to create the queues before running the program.
1179
+ * Default: true
1180
+ *
1181
+ * * :alternate_user_id [String]
1182
+ * * Sets the alternate userid to use when messages are put to the queue
1183
+ * * Note: It is not necessary to supply WMQ::MQOO_ALTERNATE_USER_AUTHORITY
1184
+ * since it is automatically added to the :open_options when :alternate_user_id
1185
+ * is supplied
1186
+ * * See WebSphere MQ Application Programming Reference: MQOD.AlternateUserId
1187
+ *
1188
+ * * :alternate_security_id [String]
1189
+ * * Sets the alternate security id to use when messages are put to the queue
1190
+ * * See WebSphere MQ Application Programming Reference: MQOD.AlternateSecurityId
1191
+ *
1192
+ * Note:
1193
+ * * It is more convenient to use WMQ::QueueManager#open_queue, since it automatically supplies
1194
+ * the parameter :queue_manager
1195
+ * * That way :queue_manager parameter is _not_ required
1196
+ *
1197
+ * Example:
1198
+ * # Put 10 Hello World messages onto a queue
1199
+ * require 'wmq/wmq_client'
1200
+ *
1201
+ * WMQ::QueueManager.connect(:q_mgr_name=>'REID', :connection_name=>'localhost(1414)') do |qmgr|
1202
+ * WMQ::Queue.open(:queue_manager=>qmgr,
1203
+ * :q_name =>'TEST.QUEUE',
1204
+ * :mode =>:output) do |queue|
1205
+ * 10.times { |counter| queue.put(:data => "Hello World #{counter}") }
1206
+ * end
1207
+ * end
1208
+ */
1209
+ VALUE Queue_singleton_open(int argc, VALUE *argv, VALUE self)
1210
+ {
1211
+ VALUE proc, parameters, queue;
1212
+
1213
+ /* Extract parameters and code block (Proc) */
1214
+ rb_scan_args(argc, argv, "1&", &parameters, &proc);
1215
+
1216
+ queue = rb_funcall(wmq_queue, ID_new, 1, parameters);
1217
+ if(!NIL_P(proc))
1218
+ {
1219
+ if(Qtrue == Queue_open(queue))
1220
+ {
1221
+ struct Queue_singleton_open_arg arg;
1222
+ arg.queue = queue;
1223
+ arg.proc = proc;
1224
+ rb_ensure(Queue_singleton_open_body, (VALUE)&arg, Queue_singleton_open_ensure, queue);
1225
+ }
1226
+ else
1227
+ {
1228
+ return Qfalse;
1229
+ }
1230
+ }
1231
+ return queue;
1232
+ }
1233
+
1234
+ /*
1235
+ * For each message found on the queue, the supplied block is executed
1236
+ *
1237
+ * Note:
1238
+ * * If no messages are found on the queue during the supplied wait interval,
1239
+ * then the supplied block will not be called at all
1240
+ * * If :mode=>:browse is supplied when opening the queue then Queue#each will automatically
1241
+ * set MQGMO_BROWSE_FIRST and MQGMO_BROWSE_NEXT as required
1242
+ *
1243
+ * Returns:
1244
+ * * true: If at least one message was succesfully processed
1245
+ * * false: If no messages were retrieved from the queue
1246
+ *
1247
+ * Example:
1248
+ * require 'wmq/wmq'
1249
+ *
1250
+ * WMQ::QueueManager.connect(:q_mgr_name=>'REID') do |qmgr|
1251
+ * qmgr.open_queue(:q_name=>'TEST.QUEUE', :mode=>:input) do |queue|
1252
+ * queue.each do |message|
1253
+ * puts "Data Received: #{message.data}"
1254
+ * end
1255
+ * end
1256
+ * puts 'Completed.'
1257
+ * end
1258
+ */
1259
+ VALUE Queue_each(int argc, VALUE *argv, VALUE self)
1260
+ {
1261
+ VALUE message = Qnil;
1262
+ VALUE match = Qnil;
1263
+ VALUE options = Qnil;
1264
+ VALUE result = Qfalse;
1265
+ VALUE proc, hash;
1266
+ MQLONG browse = 0;
1267
+
1268
+ PQUEUE pq;
1269
+ Data_Get_Struct(self, QUEUE, pq);
1270
+
1271
+ /* Extract parameters and code block (Proc) */
1272
+ rb_scan_args(argc, argv, "01&", &hash, &proc);
1273
+
1274
+ if(NIL_P(hash))
1275
+ {
1276
+ hash = rb_hash_new();
1277
+ }
1278
+ else
1279
+ {
1280
+ message = rb_hash_aref(hash, ID2SYM(ID_message));
1281
+ match = rb_hash_aref(hash, ID2SYM(ID_match));
1282
+ options = rb_hash_aref(hash, ID2SYM(ID_options));
1283
+ }
1284
+
1285
+ if (NIL_P(message))
1286
+ {
1287
+ message = rb_funcall(wmq_message, ID_new, 0);
1288
+ rb_hash_aset(hash, ID2SYM(ID_message), message);
1289
+ }
1290
+
1291
+ if (NIL_P(match))
1292
+ {
1293
+ rb_hash_aset(hash, ID2SYM(ID_match), LONG2NUM(MQMO_NONE));
1294
+ }
1295
+
1296
+ /* If queue is open for browse, set Borwse first indicator */
1297
+ if(pq->open_options & MQOO_BROWSE)
1298
+ {
1299
+ MQLONG get_options;
1300
+ if(NIL_P(options))
1301
+ {
1302
+ get_options = MQGMO_BROWSE_FIRST;
1303
+ }
1304
+ else
1305
+ {
1306
+ get_options = NUM2LONG(options) | MQGMO_BROWSE_FIRST;
1307
+ }
1308
+ rb_hash_aset(hash, ID2SYM(ID_options), LONG2NUM(get_options));
1309
+
1310
+ if(pq->trace_level>1) printf("WMQ::Queue#each MQGMO_BROWSE_FIRST set, get options:%ld\n", (long)get_options);
1311
+ browse = 1;
1312
+ }
1313
+
1314
+ while(Queue_get(self, hash))
1315
+ {
1316
+ result = Qtrue;
1317
+
1318
+ /* Call code block passing in message */
1319
+ rb_funcall( proc, ID_call, 1, message );
1320
+
1321
+ if(browse)
1322
+ {
1323
+ MQLONG get_options;
1324
+ if(NIL_P(options))
1325
+ {
1326
+ get_options = MQGMO_BROWSE_NEXT;
1327
+ }
1328
+ else
1329
+ {
1330
+ get_options = (NUM2LONG(options) - MQGMO_BROWSE_FIRST) | MQGMO_BROWSE_NEXT;
1331
+ }
1332
+ rb_hash_aset(hash, ID2SYM(ID_options), LONG2NUM(get_options));
1333
+
1334
+ if(pq->trace_level>1) printf("WMQ::Queue#each MQGMO_BROWSE_NEXT set, get options:%ld\n", (long)get_options);
1335
+ }
1336
+ }
1337
+
1338
+ return result;
1339
+ }
1340
+
1341
+ /*
1342
+ * Return the completion code for the last MQ operation on this queue instance
1343
+ *
1344
+ * Returns => FixNum
1345
+ * * WMQ::MQCC_OK 0
1346
+ * * WMQ::MQCC_WARNING 1
1347
+ * * WMQ::MQCC_FAILED 2
1348
+ * * WMQ::MQCC_UNKNOWN -1
1349
+ *
1350
+ */
1351
+ VALUE Queue_comp_code(VALUE self)
1352
+ {
1353
+ PQUEUE pq;
1354
+ Data_Get_Struct(self, QUEUE, pq);
1355
+ return LONG2NUM(pq->comp_code);
1356
+ }
1357
+
1358
+ /*
1359
+ * Return the reason code for the last MQ operation on this queue instance
1360
+ *
1361
+ * Returns => FixNum
1362
+ * * For a complete list of reason codes, please see WMQ Constants or
1363
+ * the WebSphere MQ documentation for Reason Codes
1364
+ *
1365
+ * Note
1366
+ * * The list of Reason Codes varies depending on the version of WebSphere MQ
1367
+ * and the operating system on which Ruby WMQ was compiled
1368
+ */
1369
+ VALUE Queue_reason_code(VALUE self)
1370
+ {
1371
+ PQUEUE pq;
1372
+ Data_Get_Struct(self, QUEUE, pq);
1373
+ return LONG2NUM(pq->reason_code);
1374
+ }
1375
+
1376
+ /*
1377
+ * Returns a textual representation of the reason_code for the last MQ operation on this queue instance
1378
+ *
1379
+ * Returns => String
1380
+ * * For a complete list of reasons, please see WMQ Constants or
1381
+ * the WebSphere MQ documentation for Reason Codes
1382
+ *
1383
+ * Note
1384
+ * * The list of Reason Codes varies depending on the version of WebSphere MQ
1385
+ * and the operating system on which Ruby WMQ was compiled
1386
+ */
1387
+ VALUE Queue_reason(VALUE self)
1388
+ {
1389
+ PQUEUE pq;
1390
+ Data_Get_Struct(self, QUEUE, pq);
1391
+ return rb_str_new2(wmq_reason(pq->reason_code));
1392
+ }
1393
+
1394
+ /*
1395
+ * Returns whether this queue is currently open
1396
+ *
1397
+ * Returns:
1398
+ * * true : The queue is open
1399
+ * * false: The queue is _not_ open
1400
+ */
1401
+ VALUE Queue_open_q(VALUE self)
1402
+ {
1403
+ PQUEUE pq;
1404
+ Data_Get_Struct(self, QUEUE, pq);
1405
+ if (pq->hobj)
1406
+ {
1407
+ return Qtrue;
1408
+ }
1409
+ return Qfalse;
1410
+ }
1411
+