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_message.c ADDED
@@ -0,0 +1,671 @@
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
+ #include "decode_rfh.h"
19
+
20
+ /* --------------------------------------------------
21
+ * Initialize Ruby ID's for Message Class
22
+ *
23
+ * This function is called when the library is loaded
24
+ * by ruby
25
+ * --------------------------------------------------*/
26
+
27
+ static ID ID_data;
28
+ static ID ID_message;
29
+ static ID ID_descriptor;
30
+ static ID ID_headers;
31
+ static ID ID_data_set;
32
+ static ID ID_size;
33
+ static ID ID_name_value;
34
+ static ID ID_xml;
35
+ static ID ID_gsub;
36
+ static ID ID_header_type;
37
+
38
+ void Message_id_init()
39
+ {
40
+ ID_data = rb_intern("data");
41
+ ID_size = rb_intern("size");
42
+ ID_data_set = rb_intern("data=");
43
+ ID_descriptor = rb_intern("descriptor");
44
+ ID_headers = rb_intern("headers");
45
+ ID_message = rb_intern("message");
46
+ ID_name_value = rb_intern("name_value");
47
+ ID_xml = rb_intern("xml");
48
+ ID_gsub = rb_intern("gsub");
49
+ ID_header_type = rb_intern("header_type");
50
+ }
51
+
52
+ void Message_build(PMQBYTE* pq_pp_buffer, PMQLONG pq_p_buffer_size, MQLONG trace_level,
53
+ VALUE parms, PPMQVOID pp_buffer, PMQLONG p_total_length, PMQMD pmqmd)
54
+ {
55
+ VALUE data;
56
+ VALUE descriptor;
57
+ VALUE headers;
58
+ VALUE message;
59
+
60
+ /* :data is an optional parameter, that if supplied overrides message.data */
61
+ data = rb_hash_aref(parms, ID2SYM(ID_data));
62
+ if(!NIL_P(data))
63
+ {
64
+ Check_Type(data, T_STRING);
65
+ *p_total_length = RSTRING_LEN(data);
66
+ *pp_buffer = RSTRING_PTR(data);
67
+ }
68
+
69
+ /* :message is optional */
70
+ message = rb_hash_aref(parms, ID2SYM(ID_message));
71
+ if(!NIL_P(message))
72
+ {
73
+ if (NIL_P(data)) /* Obtain data from message.data */
74
+ {
75
+ data = rb_funcall(message, ID_data, 0);
76
+ }
77
+
78
+ descriptor = rb_funcall(message, ID_descriptor, 0);
79
+ Check_Type(descriptor, T_HASH);
80
+ Message_to_mqmd(descriptor, pmqmd);
81
+
82
+ headers = rb_funcall(message, ID_headers, 0);
83
+ Check_Type(headers, T_ARRAY);
84
+
85
+ Check_Type(data, T_STRING);
86
+
87
+ /* Headers present? */
88
+ if ( !NIL_P(headers) &&
89
+ (NUM2LONG(rb_funcall(headers, ID_size, 0))>0) )
90
+ {
91
+ MQLONG data_offset = 0;
92
+ struct Message_build_header_arg arg;
93
+ VALUE next_header;
94
+ VALUE first_header;
95
+ VALUE header_type;
96
+ VALUE ind_val;
97
+ size_t index;
98
+
99
+ if(trace_level>2)
100
+ printf ("WMQ::Queue#put %ld Header(s) supplied\n", NUM2LONG(rb_funcall(headers, ID_size, 0)));
101
+
102
+ /* First sanity check: Do we even have enough space for the data being written and a small header */
103
+ if(RSTRING_LEN(data) + 128 >= *pq_p_buffer_size)
104
+ {
105
+ MQLONG new_size = RSTRING_LEN(data) + 512; /* Add space for data and a header */
106
+ if(trace_level>2)
107
+ printf ("WMQ::Queue#reallocate Resizing buffer from %ld to %ld bytes\n", *pq_p_buffer_size, (long)new_size);
108
+
109
+ *pq_p_buffer_size = new_size;
110
+ free(*pq_pp_buffer);
111
+ *pq_pp_buffer = ALLOC_N(unsigned char, new_size);
112
+ }
113
+
114
+ arg.pp_buffer = pq_pp_buffer;
115
+ arg.p_buffer_size = pq_p_buffer_size;
116
+ arg.data_length = RSTRING_LEN(data);
117
+ arg.p_data_offset = &data_offset;
118
+ arg.trace_level = trace_level;
119
+ arg.next_header_id = 0;
120
+ arg.data_format = pmqmd->Format;
121
+
122
+ if(trace_level>2)
123
+ printf ("WMQ::Queue#put Building %ld headers.\n", RARRAY_LEN(headers));
124
+
125
+ for(index = 0; index < RARRAY_LEN(headers); index++)
126
+ {
127
+ /*
128
+ * Look at the next Header so that this header can set it's format
129
+ * to that required for the next header.
130
+ */
131
+ ind_val = LONG2FIX(index+1);
132
+ next_header = rb_ary_aref(1, &ind_val, headers);
133
+
134
+ if(NIL_P(next_header))
135
+ {
136
+ arg.next_header_id = 0;
137
+ }
138
+ else
139
+ {
140
+ header_type = rb_hash_aref(next_header, ID2SYM(ID_header_type));
141
+ if (!NIL_P(header_type))
142
+ {
143
+ arg.next_header_id = rb_to_id(header_type);
144
+ }
145
+ }
146
+
147
+ ind_val = LONG2FIX(index);
148
+ Message_build_header(rb_ary_aref(1, &ind_val, headers), &arg);
149
+ }
150
+
151
+ /* Obtain Format of first header and copy in MQMD.Format */
152
+ ind_val = LONG2FIX(0);
153
+ first_header = rb_ary_aref(1, &ind_val, headers);
154
+ header_type = rb_hash_aref(first_header, ID2SYM(ID_header_type));
155
+ if (!NIL_P(header_type))
156
+ {
157
+ Message_build_set_format(rb_to_id(header_type), pmqmd->Format);
158
+ }
159
+
160
+ if(trace_level>2)
161
+ printf ("WMQ::Queue#put done building headers. Offset is now %ld\n", *arg.p_data_offset);
162
+
163
+ memcpy((*pq_pp_buffer) + data_offset, RSTRING_PTR(data), RSTRING_LEN(data));
164
+ *p_total_length = data_offset + RSTRING_LEN(data);
165
+ *pp_buffer = *pq_pp_buffer;
166
+ }
167
+ else
168
+ {
169
+ *p_total_length = RSTRING_LEN(data);
170
+ *pp_buffer = RSTRING_PTR(data);
171
+ }
172
+ }
173
+ /* If :message is not supplied, then :data must be supplied in the parameter list */
174
+ else if(NIL_P(data))
175
+ {
176
+ rb_raise(rb_eArgError, "At least one of :message or :data is required.");
177
+ }
178
+ return;
179
+ }
180
+
181
+ /*
182
+ * Extract MQMD from descriptor hash
183
+ */
184
+ void Message_build_mqmd(VALUE self, PMQMD pmqmd)
185
+ {
186
+ Message_to_mqmd(rb_funcall(self, ID_descriptor, 0), pmqmd);
187
+ }
188
+
189
+ /*
190
+ * call-seq:
191
+ * new(...)
192
+ *
193
+ * Optional Named Parameters (as a single hash):
194
+ * * :data
195
+ * * Data to be written, or was read from the queue
196
+ * * :descriptor
197
+ * * Desciptor
198
+ *
199
+ * Example:
200
+ * message = WMQ::Message.new
201
+ *
202
+ * Example:
203
+ * message = WMQ::Message.new(:data=>'Hello World',
204
+ * :descriptor=> {
205
+ * :format => WMQ::MQFMT_STRING
206
+ * })
207
+ */
208
+ VALUE Message_initialize(int argc, VALUE *argv, VALUE self)
209
+ {
210
+ VALUE parms = Qnil;
211
+
212
+ /* Extract optional parameter */
213
+ rb_scan_args(argc, argv, "01", &parms);
214
+
215
+ if (NIL_P(parms))
216
+ {
217
+ rb_iv_set(self, "@data", Qnil);
218
+ rb_iv_set(self, "@headers", rb_ary_new());
219
+ rb_iv_set(self, "@descriptor", rb_hash_new());
220
+ }
221
+ else
222
+ {
223
+ VALUE val;
224
+ Check_Type(parms, T_HASH);
225
+
226
+ rb_iv_set(self, "@data", rb_hash_aref(parms, ID2SYM(ID_data)));
227
+
228
+ val = rb_hash_aref(parms, ID2SYM(ID_headers));
229
+ if (NIL_P(val))
230
+ {
231
+ rb_iv_set(self, "@headers", rb_ary_new());
232
+ }
233
+ else
234
+ {
235
+ rb_iv_set(self, "@headers", val);
236
+ }
237
+
238
+ val = rb_hash_aref(parms, ID2SYM(ID_descriptor));
239
+ if (NIL_P(val))
240
+ {
241
+ rb_iv_set(self, "@headers", rb_hash_new());
242
+ }
243
+ else
244
+ {
245
+ rb_iv_set(self, "@descriptor", val);
246
+ }
247
+ }
248
+
249
+ return Qnil;
250
+ }
251
+
252
+ /*
253
+ * Clear out the message data and headers
254
+ *
255
+ * Note:
256
+ * * The descriptor is not affected in any way
257
+ */
258
+ VALUE Message_clear(VALUE self)
259
+ {
260
+ rb_iv_set(self, "@data", Qnil);
261
+ rb_iv_set(self, "@headers", rb_ary_new());
262
+
263
+ return self;
264
+ }
265
+
266
+ /*
267
+ * Automatically grow size of buffer to meet required size
268
+ * Existing data upto offset is preserved by copying to the new buffer
269
+ *
270
+ * additional_size: Size of any additional data to be written to this buffer
271
+ * EXCLUDING current offset and size of data to be written
272
+ *
273
+ * Returns pointer to new buffer incremented by offset within buffer
274
+ */
275
+ PMQBYTE Message_autogrow_data_buffer(struct Message_build_header_arg* parg, MQLONG additional_size)
276
+ {
277
+ MQLONG size = *(parg->p_data_offset) + parg->data_length + additional_size;
278
+ /* Is buffer large enough for headers */
279
+ if(size >= *(parg->p_buffer_size))
280
+ {
281
+ PMQBYTE old_buffer = *(parg->pp_buffer);
282
+ size += 512; /* Additional space for subsequent headers */
283
+
284
+ if(parg->trace_level>2)
285
+ printf ("WMQ::Message Reallocating buffer from %ld to %ld\n", *(parg->p_buffer_size), (long)size);
286
+
287
+ *(parg->p_buffer_size) = size;
288
+ *(parg->pp_buffer) = ALLOC_N(char, size);
289
+ memcpy(*(parg->pp_buffer), old_buffer, *(parg->p_data_offset));
290
+ free(old_buffer);
291
+ }
292
+ return *(parg->pp_buffer) + *(parg->p_data_offset);
293
+ }
294
+
295
+ /*
296
+ * Concatenate the passed name or value element to the existing string
297
+ */
298
+ static void Message_name_value_concat(VALUE string, VALUE element)
299
+ {
300
+ VALUE str = StringValue(element);
301
+ if (RSTRING_LEN(str) == 0) /* Empty String: "" */
302
+ {
303
+ rb_str_concat(string, rb_str_new2("\"\""));
304
+ }
305
+ else
306
+ {
307
+ void* contains_spaces = memchr(RSTRING_PTR(str),' ',RSTRING_LEN(str));
308
+ void* contains_dbl_quotes = memchr(RSTRING_PTR(str),'"',RSTRING_LEN(str));
309
+
310
+ if(contains_spaces == NULL && contains_dbl_quotes == NULL)
311
+ {
312
+ rb_str_concat(string, str);
313
+ }
314
+ else
315
+ {
316
+ VALUE quote = rb_str_new2("\"");
317
+ rb_str_concat(string, quote);
318
+ if(contains_dbl_quotes)
319
+ {
320
+ rb_str_concat(string, rb_funcall(str, ID_gsub, 2, quote, rb_str_new2("\"\"")));
321
+ }
322
+ else
323
+ {
324
+ rb_str_concat(string, str);
325
+ }
326
+ rb_str_concat(string, quote);
327
+ }
328
+ }
329
+ }
330
+
331
+ struct Message_build_rf_header_each_value_arg {
332
+ VALUE string;
333
+ VALUE key;
334
+ VALUE space;
335
+ };
336
+
337
+ static VALUE Message_build_rf_header_each_value(VALUE value, struct Message_build_rf_header_each_value_arg* parg)
338
+ {
339
+ Message_name_value_concat(parg->string, parg->key);
340
+ rb_str_concat(parg->string, parg->space);
341
+ Message_name_value_concat(parg->string, value);
342
+ rb_str_concat(parg->string, parg->space);
343
+
344
+ return Qnil;
345
+ }
346
+
347
+ static int Message_build_rf_header_each (VALUE key, VALUE value, VALUE string)
348
+ {
349
+ VALUE space = rb_str_new2(" ");
350
+ /* If Value is an Array, need to repeat name for each value */
351
+ if (TYPE(value) == T_ARRAY)
352
+ {
353
+ struct Message_build_rf_header_each_value_arg arg;
354
+ arg.key = key;
355
+ arg.string = string;
356
+ arg.space = space;
357
+ rb_iterate (rb_each, value, Message_build_rf_header_each_value, (VALUE)&arg);
358
+ }
359
+ else
360
+ {
361
+ Message_name_value_concat(string, key);
362
+ rb_str_concat(string, space);
363
+ Message_name_value_concat(string, value);
364
+ rb_str_concat(string, space);
365
+ }
366
+ return 0;
367
+ }
368
+
369
+ void Message_build_rf_header (VALUE hash, struct Message_build_header_arg* parg)
370
+ {
371
+ PMQBYTE p_data = *(parg->pp_buffer) + *(parg->p_data_offset);
372
+
373
+ static MQRFH MQRFH_DEF = {MQRFH_DEFAULT};
374
+ MQLONG name_value_len = 0;
375
+ PMQCHAR p_name_value = 0;
376
+ VALUE name_value = rb_hash_aref(hash, ID2SYM(ID_name_value));
377
+
378
+ MQRFH_DEF.CodedCharSetId = MQCCSI_INHERIT;
379
+
380
+ if(parg->trace_level>2)
381
+ printf ("WMQ::Message#build_rf_header Found rf_header\n");
382
+
383
+ if (!NIL_P(name_value))
384
+ {
385
+ if (TYPE(name_value) == T_HASH)
386
+ {
387
+ VALUE name_value_str = rb_str_buf_new(512); /* Allocate 512 char buffer, will grow as needed */
388
+ rb_hash_foreach(name_value, Message_build_rf_header_each, name_value_str);
389
+ name_value = name_value_str;
390
+ }
391
+ else if(TYPE(name_value) != T_STRING)
392
+ {
393
+ rb_raise(rb_eArgError, ":name_value supplied in rf_header to WMQ::Message#headers must be either a String or a Hash");
394
+ }
395
+
396
+ name_value_len = RSTRING_LEN(name_value);
397
+ if (name_value_len % 4) /* Not on 4 byte boundary ? */
398
+ {
399
+ rb_str_concat(name_value, rb_str_new(" ", 4 - (name_value_len % 4)));
400
+ name_value_len = RSTRING_LEN(name_value);
401
+ }
402
+ p_name_value = RSTRING_PTR(name_value);
403
+ }
404
+
405
+ p_data = Message_autogrow_data_buffer(parg, sizeof(MQRFH)+name_value_len);
406
+
407
+ memcpy(p_data, &MQRFH_DEF, sizeof(MQRFH));
408
+ Message_to_mqrfh(hash, (PMQRFH)p_data);
409
+ ((PMQRFH)p_data)->StrucLength = sizeof(MQRFH) + name_value_len;
410
+ if(parg->next_header_id)
411
+ {
412
+ Message_build_set_format(parg->next_header_id, ((PMQRFH)p_data)->Format);
413
+ }
414
+ else
415
+ {
416
+ strncpy(((PMQRFH)p_data)->Format, parg->data_format, MQ_FORMAT_LENGTH);
417
+ }
418
+
419
+ *(parg->p_data_offset) += sizeof(MQRFH);
420
+ p_data += sizeof(MQRFH);
421
+
422
+ if(name_value_len)
423
+ {
424
+ memcpy(p_data, p_name_value, name_value_len);
425
+ *(parg->p_data_offset) += name_value_len;
426
+ }
427
+
428
+ if(parg->trace_level>3)
429
+ printf ("WMQ::Message#build_rf_header Sizeof namevalue string:%ld\n", (long)name_value_len);
430
+
431
+ if(parg->trace_level>2)
432
+ printf ("WMQ::Message#build_rf_header data offset:%ld\n", *(parg->p_data_offset));
433
+ }
434
+
435
+ static void Message_deblock_rf_header_each_pair(const char *p_name, const char *p_value, void* p_name_value_hash)
436
+ {
437
+ VALUE name_value_hash = (VALUE)p_name_value_hash;
438
+ VALUE key = rb_str_new2(p_name);
439
+ VALUE value = rb_str_new2(p_value);
440
+
441
+ /*
442
+ * If multiple values arrive for the same name (key) need to put values in an array
443
+ */
444
+ VALUE existing = rb_hash_aref(name_value_hash, key);
445
+ if(NIL_P(existing))
446
+ {
447
+ rb_hash_aset(name_value_hash, key, value);
448
+ }
449
+ else
450
+ {
451
+ if(TYPE(existing) == T_ARRAY) /* Add to existing Array */
452
+ {
453
+ rb_ary_push(existing, value);
454
+ }
455
+ else /* Convert existing entry into an array */
456
+ {
457
+ VALUE array = rb_ary_new();
458
+ rb_ary_push(array, existing);
459
+ rb_ary_push(array, value);
460
+ rb_hash_aset(name_value_hash, key, array);
461
+ }
462
+ }
463
+ }
464
+
465
+ /*
466
+ * Deblock Any custom data following RF Header
467
+ * The RF Header has already been deblocked into hash
468
+ * p_data points to the beginning of the RF Header structure
469
+ *
470
+ * msg_len is the length of the remainder of the message from this header onwards
471
+ * It includes the length of any data that may follow
472
+ *
473
+ * Returns the length of RF Header plus the size of any custom data following it
474
+ */
475
+ MQLONG Message_deblock_rf_header (VALUE hash, PMQBYTE p_data, MQLONG data_len)
476
+ {
477
+ MQLONG size = ((PMQRFH)p_data)->StrucLength;
478
+ VALUE name_value_hash = rb_hash_new();
479
+
480
+ rfh_toktype_t toktype;
481
+
482
+ if(size < 0 || size > data_len) /* Poison Message */
483
+ {
484
+ printf("WMQ::Message_deblock_rf_header StrucLength supplied in MQRFH exceeds total message length\n");
485
+ return 0;
486
+ }
487
+
488
+ toktype = rfh_decode_name_val_str(p_data + sizeof(MQRFH),
489
+ size - sizeof(MQRFH),
490
+ Message_deblock_rf_header_each_pair,
491
+ (void*)name_value_hash);
492
+
493
+ if (toktype != TT_END)
494
+ {
495
+ printf("Could not parse rfh name value string, reason %s\n",rfh_toktype_to_s(toktype));
496
+ }
497
+
498
+ rb_hash_aset(hash, ID2SYM(ID_name_value), name_value_hash);
499
+
500
+ return size;
501
+ }
502
+
503
+ /*
504
+ * RFH2 Header can contain multiple XML-like strings
505
+ * Message consists of:
506
+ * MQRFH2
507
+ * xml-string1-length (MQLONG)
508
+ * xml-string1 (Padded with spaces to match 4 byte boundary)
509
+ * xml-string2-length (MQLONG)
510
+ * xml-string2 (Padded with spaces to match 4 byte boundary)
511
+ * ....
512
+ */
513
+ MQLONG Message_deblock_rf_header_2 (VALUE hash, PMQBYTE p_buffer, MQLONG data_len)
514
+ {
515
+ MQLONG size = ((PMQRFH2)p_buffer)->StrucLength;
516
+ PMQBYTE p_data = p_buffer + sizeof(MQRFH2);
517
+ PMQBYTE p_end = p_buffer + size; /* Points to byte after last character */
518
+ MQLONG xml_len = 0;
519
+ VALUE xml_ary = rb_ary_new();
520
+
521
+ PMQBYTE pChar;
522
+ size_t length;
523
+ size_t i;
524
+
525
+ if(size < 0 || size > data_len) /* Poison Message */
526
+ {
527
+ printf("WMQ::Message_deblock_rf_header_2 StrucLength supplied in MQRFH exceeds total message length\n");
528
+ return 0;
529
+ }
530
+
531
+ rb_hash_aset(hash, ID2SYM(ID_xml), xml_ary);
532
+
533
+ while(p_data < p_end)
534
+ {
535
+ xml_len = *(PMQLONG)p_data;
536
+ p_data += sizeof(MQLONG);
537
+
538
+ if (xml_len < 0 || p_data+xml_len > p_end)
539
+ {
540
+ printf("WMQ::Message#deblock_rf_header_2 Poison Message received, stopping further processing\n");
541
+ p_data = p_end;
542
+ }
543
+ else
544
+ {
545
+ /*
546
+ * Strip trailing spaces added as pad characters during put
547
+ */
548
+ length = 0;
549
+ pChar = p_data + xml_len - 1;
550
+ for (i = xml_len; i > 0; i--)
551
+ {
552
+ if (*pChar != ' ')
553
+ {
554
+ length = i;
555
+ break;
556
+ }
557
+ pChar--;
558
+ }
559
+ rb_ary_push(xml_ary, rb_str_new(p_data, length));
560
+ p_data += xml_len;
561
+ }
562
+ }
563
+
564
+ return size;
565
+ }
566
+
567
+ static VALUE Message_build_rf_header_2_each(VALUE element, struct Message_build_header_arg* parg)
568
+ {
569
+ VALUE str = StringValue(element);
570
+ MQLONG length = RSTRING_LEN(str);
571
+
572
+ if (length % 4) /* Not on 4 byte boundary ? */
573
+ {
574
+ static const char * blanks = " ";
575
+ MQLONG pad = 4 - (length % 4);
576
+ MQLONG len = length + pad;
577
+ PMQBYTE p_data = Message_autogrow_data_buffer(parg, sizeof(len) + length + pad);
578
+
579
+ memcpy(p_data, (void*) &len, sizeof(len)); /* Start with MQLONG length indicator */
580
+ p_data += sizeof(len);
581
+ memcpy(p_data, RSTRING_PTR(str), length);
582
+ p_data += length;
583
+ memcpy(p_data, blanks, pad);
584
+ p_data += pad;
585
+ *(parg->p_data_offset) += sizeof(len) + length + pad;
586
+ }
587
+ else
588
+ {
589
+ PMQBYTE p_data = Message_autogrow_data_buffer(parg, sizeof(length) + length);
590
+
591
+ memcpy(p_data, (void*) &length, sizeof(length)); /* Start with MQLONG length indicator */
592
+ p_data += sizeof(length);
593
+ memcpy(p_data, RSTRING_PTR(str), length);
594
+ p_data += length;
595
+ *(parg->p_data_offset) += sizeof(length) + length;
596
+ }
597
+
598
+ return Qnil;
599
+ }
600
+
601
+ /*
602
+ * RFH2 Header can contain multiple XML-like strings
603
+ * Message consists of:
604
+ * MQRFH2
605
+ * xml-string1-length (MQLONG)
606
+ * xml-string1 (Padded with spaces to match 4 byte boundary)
607
+ * xml-string2-length (MQLONG)
608
+ * xml-string2 (Padded with spaces to match 4 byte boundary)
609
+ * ....
610
+ *
611
+ * The input data is either a String or an Array of Strings
612
+ * E.g.
613
+ *
614
+ * {
615
+ * :header_type =>:rf_header_2,
616
+ * :xml => '<hello>to the world</hello>'
617
+ * }
618
+ * OR
619
+ * {
620
+ * :header_type =>:rf_header_2,
621
+ * :xml => ['<hello>to the world</hello>', '<another>xml like string</another>'],
622
+ * }
623
+ *
624
+ */
625
+ void Message_build_rf_header_2(VALUE hash, struct Message_build_header_arg* parg)
626
+ {
627
+ static MQRFH2 MQRFH2_DEF = {MQRFH2_DEFAULT};
628
+ MQLONG rfh2_offset = *(parg->p_data_offset);
629
+ PMQBYTE p_data;
630
+ VALUE xml = rb_hash_aref(hash, ID2SYM(ID_xml));
631
+
632
+ if(parg->trace_level>2)
633
+ printf ("WMQ::Message#build_rf_header_2 Found rf_header_2\n");
634
+
635
+ /* Add MQRFH2 Header, ensure that their is enough space */
636
+ p_data = Message_autogrow_data_buffer(parg, sizeof(MQRFH2));
637
+ memcpy(p_data, &MQRFH2_DEF, sizeof(MQRFH2));
638
+ Message_to_mqrfh2(hash, (PMQRFH2)p_data);
639
+ if(parg->next_header_id)
640
+ {
641
+ Message_build_set_format(parg->next_header_id, ((PMQRFH2)p_data)->Format);
642
+ }
643
+ else
644
+ {
645
+ memcpy(((PMQRFH2)p_data)->Format, parg->data_format, MQ_FORMAT_LENGTH);
646
+ }
647
+ *(parg->p_data_offset) += sizeof(MQRFH2);
648
+
649
+ if (!NIL_P(xml))
650
+ {
651
+ if(TYPE(xml) == T_ARRAY)
652
+ {
653
+ rb_iterate (rb_each, xml, Message_build_rf_header_2_each, (VALUE)parg);
654
+ }
655
+ else if(TYPE(xml) != T_STRING)
656
+ {
657
+ Message_build_rf_header_2_each(xml, parg);
658
+ }
659
+ else
660
+ {
661
+ rb_raise(rb_eArgError, ":name_value supplied in rf_header_2 to WMQ::Message#headers must be a String or an Array");
662
+ }
663
+ }
664
+
665
+ /* Set total length in MQRFH2 */
666
+ ((PMQRFH)(*(parg->pp_buffer) + rfh2_offset))->StrucLength = *(parg->p_data_offset) - rfh2_offset;
667
+
668
+ if(parg->trace_level>2)
669
+ printf ("WMQ::Message#build_rf_header_2 data offset:%ld\n", *(parg->p_data_offset));
670
+ }
671
+