ferocia-rubywmq 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/.document +8 -0
  2. data/LICENSE +13 -0
  3. data/Manifest.txt +20 -0
  4. data/README +73 -0
  5. data/examples/each_a.rb +32 -0
  6. data/examples/each_b.rb +41 -0
  7. data/examples/each_header.rb +38 -0
  8. data/examples/files_to_q.cfg +24 -0
  9. data/examples/files_to_q.rb +47 -0
  10. data/examples/get_a.rb +35 -0
  11. data/examples/get_client.rb +51 -0
  12. data/examples/put1_a.rb +25 -0
  13. data/examples/put1_b.rb +33 -0
  14. data/examples/put1_c.rb +32 -0
  15. data/examples/put_a.rb +35 -0
  16. data/examples/put_b.rb +43 -0
  17. data/examples/put_dlh.rb +41 -0
  18. data/examples/put_dynamic_q.rb +38 -0
  19. data/examples/put_group_a.rb +51 -0
  20. data/examples/put_group_b.rb +53 -0
  21. data/examples/put_rfh.rb +67 -0
  22. data/examples/put_rfh2_a.rb +43 -0
  23. data/examples/put_rfh2_b.rb +43 -0
  24. data/examples/put_xmit_q.rb +33 -0
  25. data/examples/q_to_files.cfg +17 -0
  26. data/examples/q_to_files.rb +48 -0
  27. data/examples/request.rb +60 -0
  28. data/examples/server.rb +97 -0
  29. data/ext/build.bat +3 -0
  30. data/ext/build.sh +6 -0
  31. data/ext/decode_rfh.c +348 -0
  32. data/ext/decode_rfh.h +45 -0
  33. data/ext/extconf.rb +44 -0
  34. data/ext/extconf_client.rb +40 -0
  35. data/ext/generate/generate_const.rb +167 -0
  36. data/ext/generate/generate_reason.rb +246 -0
  37. data/ext/generate/generate_structs.rb +97 -0
  38. data/ext/generate/wmq_structs.erb +371 -0
  39. data/ext/lib/wmq_temp.rb +197 -0
  40. data/ext/wmq.c +93 -0
  41. data/ext/wmq.h +367 -0
  42. data/ext/wmq_message.c +671 -0
  43. data/ext/wmq_mq_load.c +217 -0
  44. data/ext/wmq_queue.c +1411 -0
  45. data/ext/wmq_queue_manager.c +1587 -0
  46. data/lib/wmq.rb +25 -0
  47. data/rubywmq.binary.gemspec +32 -0
  48. data/rubywmq.gemspec +33 -0
  49. data/tests/test.rb +328 -0
  50. metadata +124 -0
data/ext/wmq_message.c ADDED
@@ -0,0 +1,671 @@
1
+ /* --------------------------------------------------------------------------
2
+ * Copyright 2006 J. Reid Morrison. Dimension Solutions, Inc.
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
+