rubywmq 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,93 @@
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
+
19
+ VALUE wmq_queue;
20
+ VALUE wmq_queue_manager;
21
+ VALUE wmq_message;
22
+ VALUE wmq_exception;
23
+
24
+ void Init_wmq() {
25
+ VALUE qmgr, wmq;
26
+
27
+ wmq = rb_define_module("WMQ");
28
+
29
+ wmq_queue_manager = rb_define_class_under(wmq, "QueueManager", rb_cObject);
30
+ rb_define_alloc_func(wmq_queue_manager, QUEUE_MANAGER_alloc);
31
+ rb_define_singleton_method(wmq_queue_manager, "connect", QueueManager_singleton_connect, -1); /* in wmq_queue_manager.c */
32
+ rb_define_method(wmq_queue_manager, "initialize", QueueManager_initialize, 1); /* in wmq_queue_manager.c */
33
+ rb_define_method(wmq_queue_manager, "connect", QueueManager_connect, 0); /* in wmq_queue_manager.c */
34
+ rb_define_method(wmq_queue_manager, "disconnect", QueueManager_disconnect, 0); /* in wmq_queue_manager.c */
35
+ rb_define_method(wmq_queue_manager, "open_queue", QueueManager_open_queue, -1); /* in wmq_queue_manager.c */
36
+ rb_define_method(wmq_queue_manager, "access_queue", QueueManager_open_queue, -1); /* in wmq_queue_manager.c */
37
+ rb_define_method(wmq_queue_manager, "begin", QueueManager_begin, 0); /* in wmq_queue_manager.c */
38
+ rb_define_method(wmq_queue_manager, "commit", QueueManager_commit, 0); /* in wmq_queue_manager.c */
39
+ rb_define_method(wmq_queue_manager, "backout", QueueManager_backout, 0); /* in wmq_queue_manager.c */
40
+ rb_define_method(wmq_queue_manager, "put", QueueManager_put, 1); /* in wmq_queue_manager.c */
41
+ rb_define_method(wmq_queue_manager, "comp_code", QueueManager_comp_code, 0); /* in wmq_queue_manager.c */
42
+ rb_define_method(wmq_queue_manager, "reason_code", QueueManager_reason_code, 0); /* in wmq_queue_manager.c */
43
+ rb_define_method(wmq_queue_manager, "reason", QueueManager_reason, 0); /* in wmq_queue_manager.c */
44
+ rb_define_method(wmq_queue_manager, "exception_on_error", QueueManager_exception_on_error, 0); /* in wmq_queue_manager.c */
45
+ rb_define_method(wmq_queue_manager, "connected?", QueueManager_connected_q, 0); /* in wmq_queue_manager.c */
46
+ rb_define_method(wmq_queue_manager, "name", QueueManager_name, 0); /* in wmq_queue_manager.c */
47
+ rb_define_method(wmq_queue_manager, "execute", QueueManager_execute, 1); /* in wmq_queue_manager.c */
48
+
49
+ wmq_queue = rb_define_class_under(wmq, "Queue", rb_cObject);
50
+ rb_define_alloc_func(wmq_queue, QUEUE_alloc);
51
+ rb_define_singleton_method(wmq_queue, "open", Queue_singleton_open, -1); /* in wmq_queue.c */
52
+ rb_define_method(wmq_queue, "initialize", Queue_initialize, 1); /* in wmq_queue.c */
53
+ rb_define_method(wmq_queue, "open", Queue_open, 0); /* in wmq_queue.c */
54
+ rb_define_method(wmq_queue, "close", Queue_close, 0); /* in wmq_queue.c */
55
+ rb_define_method(wmq_queue, "put", Queue_put, 1); /* in wmq_queue.c */
56
+ rb_define_method(wmq_queue, "get", Queue_get, 1); /* in wmq_queue.c */
57
+ rb_define_method(wmq_queue, "each", Queue_each, -1); /* in wmq_queue.c */
58
+ rb_define_method(wmq_queue, "name", Queue_name, 0); /* in wmq_queue.c */
59
+ rb_define_method(wmq_queue, "comp_code", Queue_comp_code, 0); /* in wmq_queue.c */
60
+ rb_define_method(wmq_queue, "reason_code", Queue_reason_code, 0); /* in wmq_queue.c */
61
+ rb_define_method(wmq_queue, "reason", Queue_reason, 0); /* in wmq_queue.c */
62
+ rb_define_method(wmq_queue, "open?", Queue_open_q, 0); /* in wmq_queue.c */
63
+
64
+ wmq_message = rb_define_class_under(wmq, "Message", rb_cObject);
65
+ rb_define_method(wmq_message, "initialize", Message_initialize, -1); /* in wmq_message.c */
66
+ rb_define_method(wmq_message, "clear", Message_clear, 0); /* in wmq_message.c */
67
+
68
+ /*
69
+ * WMQException is thrown whenever an MQ operation fails and
70
+ * exception_on_error is true
71
+ */
72
+ wmq_exception = rb_define_class_under(wmq, "WMQException", rb_eRuntimeError);
73
+
74
+ /*
75
+ * Initialize id fields
76
+ */
77
+ Message_id_init();
78
+ Queue_id_init();
79
+ QueueManager_id_init();
80
+ QueueManager_selector_id_init();
81
+ QueueManager_command_id_init();
82
+ wmq_structs_id_init();
83
+
84
+ rb_require("wmq/wmq_temp");
85
+ rb_require("wmq/wmq_const");
86
+ }
87
+
88
+ /*
89
+ * For client build when dynamic loading is not being used E.g. Not Windows or Solaris ...
90
+ */
91
+ void Init_wmq_client() {
92
+ Init_wmq();
93
+ }
@@ -0,0 +1,324 @@
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 <ruby.h>
18
+ #include <version.h>
19
+ #include <cmqc.h>
20
+ #include <cmqxc.h>
21
+
22
+ /* Todo: Add a #ifdef here to exclude the following includes when applicable */
23
+ #include <cmqcfc.h> /* PCF */
24
+ #include <cmqbc.h> /* MQAI */
25
+
26
+ #ifdef _WIN32
27
+ #define WMQ_EXPORT __declspec(dllexport)
28
+ #else
29
+ #define WMQ_EXPORT
30
+ #endif
31
+
32
+ void QueueManager_id_init();
33
+ void QueueManager_selector_id_init();
34
+ void QueueManager_command_id_init();
35
+ VALUE QUEUE_MANAGER_alloc(VALUE klass);
36
+ VALUE QueueManager_singleton_connect(int argc, VALUE *argv, VALUE self);
37
+ VALUE QueueManager_open_queue(int argc, VALUE *argv, VALUE self);
38
+ VALUE QueueManager_initialize(VALUE self, VALUE parms);
39
+ VALUE QueueManager_connect(VALUE self);
40
+ VALUE QueueManager_disconnect(VALUE self);
41
+ VALUE QueueManager_begin(VALUE self);
42
+ VALUE QueueManager_commit(VALUE self);
43
+ VALUE QueueManager_backout(VALUE self);
44
+ VALUE QueueManager_put(VALUE self, VALUE parms);
45
+ VALUE QueueManager_reason_code(VALUE self);
46
+ VALUE QueueManager_comp_code(VALUE self);
47
+ VALUE QueueManager_reason(VALUE self);
48
+ VALUE QueueManager_exception_on_error(VALUE self);
49
+ VALUE QueueManager_connected_q(VALUE self);
50
+ VALUE QueueManager_name(VALUE self);
51
+ VALUE QueueManager_execute(VALUE self, VALUE hash);
52
+
53
+ void Queue_id_init();
54
+ VALUE QUEUE_alloc(VALUE klass);
55
+ VALUE Queue_initialize(VALUE self, VALUE parms);
56
+ VALUE Queue_singleton_open(int argc, VALUE *argv, VALUE self);
57
+ VALUE Queue_open(VALUE self);
58
+ VALUE Queue_close(VALUE self);
59
+ VALUE Queue_put(VALUE self, VALUE parms);
60
+ VALUE Queue_get(VALUE self, VALUE parms);
61
+ VALUE Queue_each(int argc, VALUE *argv, VALUE self);
62
+ VALUE Queue_name(VALUE self);
63
+ VALUE Queue_reason_code(VALUE self);
64
+ VALUE Queue_comp_code(VALUE self);
65
+ VALUE Queue_reason(VALUE self);
66
+ VALUE Queue_open_q(VALUE self);
67
+
68
+ void Queue_extract_put_message_options(VALUE hash, PMQPMO ppmo);
69
+
70
+ extern VALUE wmq_queue;
71
+ extern VALUE wmq_queue_manager;
72
+ extern VALUE wmq_message;
73
+ extern VALUE wmq_exception;
74
+
75
+ #define WMQ_EXEC_STRING_INQ_BUFFER_SIZE 32768 /* Todo: Should we make the mqai string return buffer dynamic? */
76
+
77
+ /* Internal C Structures for holding MQ data types */
78
+ typedef struct tagQUEUE_MANAGER QUEUE_MANAGER;
79
+ typedef QUEUE_MANAGER MQPOINTER PQUEUE_MANAGER;
80
+
81
+ struct tagQUEUE_MANAGER {
82
+ MQHCONN hcon; /* connection handle */
83
+ MQLONG comp_code; /* completion code */
84
+ MQLONG reason_code; /* reason code for MQCONN */
85
+ MQLONG exception_on_error; /* Non-Zero means throw exception*/
86
+ MQLONG already_connected; /* Already connected means don't disconnect */
87
+ MQLONG trace_level; /* Trace level. 0==None, 1==Info 2==Debug ..*/
88
+ MQCNO connect_options; /* MQCONNX Connection Options */
89
+ #ifdef MQCNO_VERSION_2
90
+ MQCD client_conn; /* Client Connection */
91
+ #endif
92
+ #ifdef MQCNO_VERSION_4
93
+ MQSCO ssl_config_opts; /* Security options */
94
+ #endif
95
+ #ifdef MQCD_VERSION_6
96
+ MQPTR long_remote_user_id_ptr;
97
+ #endif
98
+ #ifdef MQCD_VERSION_7
99
+ MQPTR ssl_peer_name_ptr;
100
+ #endif
101
+ #ifdef MQHB_UNUSABLE_HBAG
102
+ MQHBAG admin_bag;
103
+ MQHBAG reply_bag;
104
+ #endif
105
+ PMQBYTE p_buffer; /* message buffer */
106
+ MQLONG buffer_size; /* Allocated size of buffer */
107
+
108
+ MQLONG is_client_conn; /* Is this a Client Connection? */
109
+ void* mq_lib_handle; /* Handle to MQ library */
110
+
111
+ void(*MQCONNX)(PMQCHAR,PMQCNO,PMQHCONN,PMQLONG,PMQLONG);
112
+ void(*MQCONN) (PMQCHAR,PMQHCONN,PMQLONG,PMQLONG);
113
+ void(*MQDISC) (PMQHCONN,PMQLONG,PMQLONG);
114
+ void(*MQBEGIN)(MQHCONN,PMQVOID,PMQLONG,PMQLONG);
115
+ void(*MQBACK) (MQHCONN,PMQLONG,PMQLONG);
116
+ void(*MQCMIT) (MQHCONN,PMQLONG,PMQLONG);
117
+ void(*MQPUT1) (MQHCONN,PMQVOID,PMQVOID,PMQVOID,MQLONG,PMQVOID,PMQLONG,PMQLONG);
118
+
119
+ void(*MQOPEN) (MQHCONN,PMQVOID,MQLONG,PMQHOBJ,PMQLONG,PMQLONG);
120
+ void(*MQCLOSE)(MQHCONN,PMQHOBJ,MQLONG,PMQLONG,PMQLONG);
121
+ void(*MQGET) (MQHCONN,MQHOBJ,PMQVOID,PMQVOID,MQLONG,PMQVOID,PMQLONG,PMQLONG,PMQLONG);
122
+ void(*MQPUT) (MQHCONN,MQHOBJ,PMQVOID,PMQVOID,MQLONG,PMQVOID,PMQLONG,PMQLONG);
123
+
124
+ void(*MQINQ) (MQHCONN,MQHOBJ,MQLONG,PMQLONG,MQLONG,PMQLONG,MQLONG,PMQCHAR,PMQLONG,PMQLONG);
125
+ void(*MQSET) (MQHCONN,MQHOBJ,MQLONG,PMQLONG,MQLONG,PMQLONG,MQLONG,PMQCHAR,PMQLONG,PMQLONG);
126
+
127
+ void(*mqCreateBag)(MQLONG,PMQHBAG,PMQLONG,PMQLONG);
128
+ void(*mqDeleteBag)(PMQHBAG,PMQLONG,PMQLONG);
129
+ void(*mqClearBag)(MQHBAG,PMQLONG,PMQLONG);
130
+ void(*mqExecute)(MQHCONN,MQLONG,MQHBAG,MQHBAG,MQHBAG,MQHOBJ,MQHOBJ,PMQLONG,PMQLONG);
131
+ void(*mqCountItems)(MQHBAG,MQLONG,PMQLONG,PMQLONG,PMQLONG);
132
+ void(*mqInquireBag)(MQHBAG,MQLONG,MQLONG,PMQHBAG,PMQLONG,PMQLONG);
133
+ void(*mqInquireItemInfo)(MQHBAG,MQLONG,MQLONG,PMQLONG,PMQLONG,PMQLONG,PMQLONG);
134
+ void(*mqInquireInteger)(MQHBAG,MQLONG,MQLONG,PMQLONG,PMQLONG,PMQLONG);
135
+ void(*mqInquireString)(MQHBAG,MQLONG,MQLONG,MQLONG,PMQCHAR,PMQLONG,PMQLONG,PMQLONG,PMQLONG);
136
+ void(*mqAddInquiry)(MQHBAG,MQLONG,PMQLONG,PMQLONG);
137
+ void(*mqAddInteger)(MQHBAG,MQLONG,MQLONG,PMQLONG,PMQLONG);
138
+ void(*mqAddString)(MQHBAG,MQLONG,MQLONG,PMQCHAR,PMQLONG,PMQLONG);
139
+ };
140
+
141
+ void Queue_manager_mq_load(PQUEUE_MANAGER pqm);
142
+ void Queue_manager_mq_free(PQUEUE_MANAGER pqm);
143
+
144
+
145
+ /*
146
+ * Message
147
+ */
148
+ struct Message_build_header_arg {
149
+ PMQBYTE* pp_buffer; /* Autosize: Pointer to start of total buffer */
150
+ PMQLONG p_buffer_size; /* Autosize: Size of total buffer */
151
+ MQLONG data_length; /* Autosize: Length of the data being written */
152
+ PMQLONG p_data_offset; /* Current offset of data portion in total buffer */
153
+ MQLONG trace_level; /* Trace level. 0==None, 1==Info 2==Debug ..*/
154
+ ID next_header_id; /* Used for setting MQ Format to next header */
155
+ PMQBYTE data_format; /* Format of data. Used when next_header_id == 0 */
156
+ };
157
+
158
+ void Message_id_init();
159
+ VALUE Message_initialize(int argc, VALUE *argv, VALUE self);
160
+ VALUE Message_clear(VALUE self);
161
+ PMQBYTE Message_autogrow_data_buffer(struct Message_build_header_arg* parg, MQLONG additional_size);
162
+ void Message_build_rf_header (VALUE hash, struct Message_build_header_arg* parg);
163
+ MQLONG Message_deblock_rf_header (VALUE hash, PMQBYTE p_data, MQLONG data_len);
164
+ void Message_build_rf_header_2 (VALUE hash, struct Message_build_header_arg* parg);
165
+ MQLONG Message_deblock_rf_header_2 (VALUE hash, PMQBYTE p_data, MQLONG data_len);
166
+
167
+ void Message_build_set_format(ID header_type, PMQBYTE p_format);
168
+ void Message_build(PMQBYTE* pq_pp_buffer, PMQLONG pq_p_buffer_size, MQLONG trace_level,
169
+ VALUE parms, PPMQVOID pp_buffer, PMQLONG p_total_length, PMQMD pmqmd);
170
+ void Message_build_mqmd(VALUE self, PMQMD pmqmd);
171
+ void Message_deblock(VALUE message, PMQMD pmqmd, PMQBYTE p_buffer, MQLONG total_length, MQLONG trace_level);
172
+
173
+ int Message_build_header(VALUE hash, struct Message_build_header_arg* parg);
174
+
175
+ /* Utility methods */
176
+
177
+ /* --------------------------------------------------
178
+ * Set internal variable based on value passed in from
179
+ * a hash
180
+ * --------------------------------------------------*/
181
+ void setFromHashString(VALUE self, VALUE hash, char* pKey, char* pAttribute, char* pDefault);
182
+ void setFromHashValue(VALUE self, VALUE hash, char* pKey, char* pAttribute, VALUE valDefault);
183
+
184
+ void to_mqmd(VALUE hash, MQMD* pmqmd);
185
+ void from_mqmd(VALUE hash, MQMD* pmqmd);
186
+
187
+ void to_mqdlh(VALUE hash, MQDLH* pmqdlh);
188
+ void from_mqdlh(VALUE hash, MQDLH* pmqdlh);
189
+
190
+ void wmq_structs_id_init();
191
+
192
+ char* wmq_reason(MQLONG reason_code);
193
+ ID wmq_selector_id(MQLONG selector);
194
+ MQLONG wmq_command_lookup(ID command_id);
195
+ void wmq_selector(ID selector_id, PMQLONG selector_type, PMQLONG selector);
196
+
197
+ /* --------------------------------------------------
198
+ * MACROS for moving data between Ruby and MQ
199
+ * --------------------------------------------------*/
200
+ #define WMQ_STR2MQLONG(STR,ELEMENT) \
201
+ ELEMENT = NUM2LONG(STR);
202
+
203
+ #define WMQ_STR2MQCHAR(STR,ELEMENT) \
204
+ ELEMENT = NUM2LONG(STR);
205
+
206
+ #define WMQ_STR2MQCHARS(STR,ELEMENT) \
207
+ str = StringValue(STR); \
208
+ length = RSTRING(STR)->len; \
209
+ size = sizeof(ELEMENT); \
210
+ strncpy(ELEMENT, RSTRING(STR)->ptr, length > size ? size : length);
211
+
212
+ #define WMQ_STR2MQBYTES(STR,ELEMENT) \
213
+ str = StringValue(STR); \
214
+ length = RSTRING(str)->len; \
215
+ size = sizeof(ELEMENT); \
216
+ if (length >= size) \
217
+ { \
218
+ memcpy(ELEMENT, RSTRING(str)->ptr, size); \
219
+ } \
220
+ else \
221
+ { \
222
+ memcpy(ELEMENT, RSTRING(str)->ptr, length); \
223
+ memset(ELEMENT+length, 0, size-length); \
224
+ }
225
+
226
+ #define WMQ_HASH2MQLONG(HASH,KEY,ELEMENT) \
227
+ val = rb_hash_aref(HASH, ID2SYM(ID_##KEY)); \
228
+ if (!NIL_P(val)) { WMQ_STR2MQLONG(val,ELEMENT) }
229
+
230
+ #define WMQ_HASH2MQCHARS(HASH,KEY,ELEMENT) \
231
+ val = rb_hash_aref(HASH, ID2SYM(ID_##KEY)); \
232
+ if (!NIL_P(val)) \
233
+ { \
234
+ WMQ_STR2MQCHARS(val,ELEMENT) \
235
+ }
236
+
237
+ #define WMQ_HASH2MQCHAR(HASH,KEY,ELEMENT) \
238
+ val = rb_hash_aref(HASH, ID2SYM(ID_##KEY)); \
239
+ if (!NIL_P(val)) { WMQ_STR2MQCHAR(val,ELEMENT); } \
240
+
241
+ #define WMQ_HASH2MQBYTES(HASH,KEY,ELEMENT) \
242
+ val = rb_hash_aref(HASH, ID2SYM(ID_##KEY)); \
243
+ if (!NIL_P(val)) \
244
+ { \
245
+ WMQ_STR2MQBYTES(val,ELEMENT)(val); \
246
+ }
247
+
248
+ #define WMQ_HASH2BOOL(HASH,KEY,ELEMENT) \
249
+ val = rb_hash_aref(hash, ID2SYM(ID_##KEY)); \
250
+ if (!NIL_P(val)) \
251
+ { \
252
+ if(TYPE(val) == T_TRUE) ELEMENT = 1; \
253
+ else if(TYPE(val) == T_FALSE) ELEMENT = 0; \
254
+ else \
255
+ rb_raise(rb_eTypeError, ":" #KEY \
256
+ " must be true or false"); \
257
+ }
258
+
259
+ #define IF_TRUE(KEY,DEFAULT) \
260
+ val = rb_hash_aref(hash, ID2SYM(ID_##KEY)); \
261
+ if (NIL_P(val)) \
262
+ { \
263
+ flag = DEFAULT; \
264
+ } \
265
+ else \
266
+ { \
267
+ if(TYPE(val) == T_TRUE) flag = 1; \
268
+ else if(TYPE(val) == T_FALSE) flag = 0; \
269
+ else \
270
+ rb_raise(rb_eTypeError, ":" #KEY \
271
+ " must be true or false"); \
272
+ } \
273
+ if (flag)
274
+
275
+ /* --------------------------------------------------
276
+ * Strip trailing nulls and spaces
277
+ * --------------------------------------------------*/
278
+ #define WMQ_MQCHARS2STR(ELEMENT, TARGET) \
279
+ size = sizeof(ELEMENT); \
280
+ length = 0; \
281
+ pChar = ELEMENT + size-1; \
282
+ for (i = size; i > 0; i--) \
283
+ { \
284
+ if (*pChar != ' ' && *pChar != 0) \
285
+ { \
286
+ length = i; \
287
+ break; \
288
+ } \
289
+ pChar--; \
290
+ } \
291
+ TARGET = rb_str_new(ELEMENT,length);
292
+
293
+ #define WMQ_MQCHARS2HASH(HASH,KEY,ELEMENT) \
294
+ WMQ_MQCHARS2STR(ELEMENT, str) \
295
+ rb_hash_aset(HASH, ID2SYM(ID_##KEY), str);
296
+
297
+ #define WMQ_MQLONG2HASH(HASH,KEY,ELEMENT) \
298
+ rb_hash_aset(HASH, ID2SYM(ID_##KEY), LONG2NUM(ELEMENT));
299
+
300
+ #define WMQ_MQCHAR2HASH(HASH,KEY,ELEMENT) \
301
+ rb_hash_aset(HASH, ID2SYM(ID_##KEY), LONG2NUM(ELEMENT));
302
+
303
+ /* --------------------------------------------------
304
+ * Trailing Spaces are important with binary fields
305
+ * --------------------------------------------------*/
306
+ #define WMQ_MQBYTES2STR(ELEMENT,TARGET) \
307
+ size = sizeof(ELEMENT); \
308
+ length = 0; \
309
+ pChar = ELEMENT + size-1; \
310
+ for (i = size; i > 0; i--) \
311
+ { \
312
+ if (*pChar != 0) \
313
+ { \
314
+ length = i; \
315
+ break; \
316
+ } \
317
+ pChar--; \
318
+ } \
319
+ TARGET = rb_str_new(ELEMENT,length);
320
+
321
+ #define WMQ_MQBYTES2HASH(HASH,KEY,ELEMENT) \
322
+ WMQ_MQBYTES2STR(ELEMENT, str) \
323
+ rb_hash_aset(HASH, ID2SYM(ID_##KEY), str);
324
+
@@ -0,0 +1,686 @@
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(data)->len;
66
+ *pp_buffer = RSTRING(data)->ptr;
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
+ PMQBYTE p_data = 0;
92
+ MQLONG data_offset = 0;
93
+ struct Message_build_header_arg arg;
94
+ VALUE next_header;
95
+ VALUE first_header;
96
+ VALUE header_type;
97
+ VALUE ind_val;
98
+ size_t index;
99
+ ID first_header_id;
100
+
101
+ if(trace_level>2)
102
+ printf ("WMQ::Queue#put %ld Header(s) supplied\n", NUM2LONG(rb_funcall(headers, ID_size, 0)));
103
+
104
+ /* First sanity check: Do we even have enough space for the data being written and a small header */
105
+ if(RSTRING(data)->len + 128 >= *pq_p_buffer_size)
106
+ {
107
+ MQLONG new_size = RSTRING(data)->len + 512; /* Add space for data and a header */
108
+ if(trace_level>2)
109
+ printf ("WMQ::Queue#reallocate Resizing buffer from %ld to %ld bytes\n", *pq_p_buffer_size, new_size);
110
+
111
+ *pq_p_buffer_size = new_size;
112
+ free(*pq_pp_buffer);
113
+ *pq_pp_buffer = ALLOC_N(char, new_size);
114
+ }
115
+
116
+ arg.pp_buffer = pq_pp_buffer;
117
+ arg.p_buffer_size = pq_p_buffer_size;
118
+ arg.data_length = RSTRING(data)->len;
119
+ arg.p_data_offset = &data_offset;
120
+ arg.trace_level = trace_level;
121
+ arg.next_header_id = 0;
122
+ arg.data_format = pmqmd->Format;
123
+
124
+ if(trace_level>2)
125
+ printf ("WMQ::Queue#put Building %ld headers.\n", RARRAY(headers)->len);
126
+
127
+ for(index = 0; index < RARRAY(headers)->len; index++)
128
+ {
129
+ /*
130
+ * Look at the next Header so that this header can set it's format
131
+ * to that required for the next header.
132
+ */
133
+ ind_val = LONG2FIX(index+1);
134
+ next_header = rb_ary_aref(1, &ind_val, headers);
135
+
136
+ if(NIL_P(next_header))
137
+ {
138
+ arg.next_header_id = 0;
139
+ }
140
+ else
141
+ {
142
+ header_type = rb_hash_aref(next_header, ID2SYM(ID_header_type));
143
+ if (!NIL_P(header_type))
144
+ {
145
+ arg.next_header_id = rb_to_id(header_type);
146
+ }
147
+ }
148
+
149
+ ind_val = LONG2FIX(index);
150
+ Message_build_header(rb_ary_aref(1, &ind_val, headers), &arg);
151
+ }
152
+
153
+ /* Obtain Format of first header and copy in MQMD.Format */
154
+ ind_val = LONG2FIX(0);
155
+ first_header = rb_ary_aref(1, &ind_val, headers);
156
+ header_type = rb_hash_aref(first_header, ID2SYM(ID_header_type));
157
+ if (!NIL_P(header_type))
158
+ {
159
+ Message_build_set_format(rb_to_id(header_type), pmqmd->Format);
160
+ }
161
+
162
+ if(trace_level>2)
163
+ printf ("WMQ::Queue#put done building headers. Offset is now %ld\n", arg.p_data_offset);
164
+
165
+ memcpy((*pq_pp_buffer) + data_offset, RSTRING(data)->ptr, RSTRING(data)->len);
166
+ *p_total_length = data_offset + RSTRING(data)->len;
167
+ *pp_buffer = *pq_pp_buffer;
168
+ }
169
+ else
170
+ {
171
+ *p_total_length = RSTRING(data)->len;
172
+ *pp_buffer = RSTRING(data)->ptr;
173
+ }
174
+ }
175
+ /* If :message is not supplied, then :data must be supplied in the parameter list */
176
+ else if(NIL_P(data))
177
+ {
178
+ rb_raise(rb_eArgError, "At least one of :message or :data is required.");
179
+ }
180
+ return;
181
+ }
182
+
183
+ /*
184
+ * Extract MQMD from descriptor hash
185
+ */
186
+ void Message_build_mqmd(VALUE self, PMQMD pmqmd)
187
+ {
188
+ Message_to_mqmd(rb_funcall(self, ID_descriptor, 0), pmqmd);
189
+ }
190
+
191
+ /*
192
+ * call-seq:
193
+ * new(...)
194
+ *
195
+ * Optional Named Parameters (as a single hash):
196
+ * * :data
197
+ * * Data to be written, or was read from the queue
198
+ * * :descriptor
199
+ * * Desciptor
200
+ *
201
+ * Example:
202
+ * message = WMQ::Message.new
203
+ *
204
+ * Example:
205
+ * message = WMQ::Message.new(:data=>'Hello World',
206
+ * :descriptor=> {
207
+ * :format => WMQ::MQFMT_STRING
208
+ * })
209
+ */
210
+ VALUE Message_initialize(int argc, VALUE *argv, VALUE self)
211
+ {
212
+ VALUE parms = Qnil;
213
+ VALUE proc = Qnil;
214
+
215
+ /* Extract optional parameter */
216
+ rb_scan_args(argc, argv, "01", &parms);
217
+
218
+ if (NIL_P(parms))
219
+ {
220
+ rb_iv_set(self, "@data", Qnil);
221
+ rb_iv_set(self, "@headers", rb_ary_new());
222
+ rb_iv_set(self, "@descriptor", rb_hash_new());
223
+ }
224
+ else
225
+ {
226
+ VALUE val;
227
+ Check_Type(parms, T_HASH);
228
+
229
+ rb_iv_set(self, "@data", rb_hash_aref(parms, ID2SYM(ID_data)));
230
+
231
+ val = rb_hash_aref(parms, ID2SYM(ID_headers));
232
+ if (NIL_P(val))
233
+ {
234
+ rb_iv_set(self, "@headers", rb_ary_new());
235
+ }
236
+ else
237
+ {
238
+ rb_iv_set(self, "@headers", val);
239
+ }
240
+
241
+ val = rb_hash_aref(parms, ID2SYM(ID_descriptor));
242
+ if (NIL_P(val))
243
+ {
244
+ rb_iv_set(self, "@headers", rb_hash_new());
245
+ }
246
+ else
247
+ {
248
+ rb_iv_set(self, "@descriptor", val);
249
+ }
250
+ }
251
+
252
+ return Qnil;
253
+ }
254
+
255
+ /*
256
+ * Clear out the message data and headers
257
+ *
258
+ * Note:
259
+ * * The descriptor is not affected in any way
260
+ */
261
+ VALUE Message_clear(VALUE self)
262
+ {
263
+ rb_iv_set(self, "@data", Qnil);
264
+ rb_iv_set(self, "@headers", rb_ary_new());
265
+
266
+ return self;
267
+ }
268
+
269
+ /*
270
+ * Automatically grow size of buffer to meet required size
271
+ * Existing data upto offset is preserved by copying to the new buffer
272
+ *
273
+ * additional_size: Size of any additional data to be written to this buffer
274
+ * EXCLUDING current offset and size of data to be written
275
+ *
276
+ * Returns pointer to new buffer incremented by offset within buffer
277
+ */
278
+ PMQBYTE Message_autogrow_data_buffer(struct Message_build_header_arg* parg, MQLONG additional_size)
279
+ {
280
+ MQLONG size = *(parg->p_data_offset) + parg->data_length + additional_size;
281
+ /* Is buffer large enough for headers */
282
+ if(size >= *(parg->p_buffer_size))
283
+ {
284
+ PMQBYTE old_buffer = *(parg->pp_buffer);
285
+ size += 512; /* Additional space for subsequent headers */
286
+
287
+ if(parg->trace_level>2)
288
+ printf ("WMQ::Message Reallocating buffer from %ld to %ld\n", *(parg->p_buffer_size), size);
289
+
290
+ *(parg->p_buffer_size) = size;
291
+ *(parg->pp_buffer) = ALLOC_N(char, size);
292
+ memcpy(*(parg->pp_buffer), old_buffer, *(parg->p_data_offset));
293
+ free(old_buffer);
294
+ }
295
+ return *(parg->pp_buffer) + *(parg->p_data_offset);
296
+ }
297
+
298
+ /*
299
+ * Concatenate the passed name or value element to the existing string
300
+ */
301
+ static void Message_name_value_concat(VALUE string, VALUE element)
302
+ {
303
+ VALUE str = StringValue(element);
304
+ if (RSTRING(str)->len == 0) /* Empty String: "" */
305
+ {
306
+ rb_str_concat(string, rb_str_new2("\"\""));
307
+ }
308
+ else
309
+ {
310
+ void* contains_spaces = memchr(RSTRING(str)->ptr,' ',RSTRING(str)->len);
311
+ void* contains_dbl_quotes = memchr(RSTRING(str)->ptr,'"',RSTRING(str)->len);
312
+
313
+ if(contains_spaces == NULL && contains_dbl_quotes == NULL)
314
+ {
315
+ rb_str_concat(string, str);
316
+ }
317
+ else
318
+ {
319
+ VALUE quote = rb_str_new2("\"");
320
+ rb_str_concat(string, quote);
321
+ if(contains_dbl_quotes)
322
+ {
323
+ rb_str_concat(string, rb_funcall(str, ID_gsub, 2, quote, rb_str_new2("\"\"")));
324
+ }
325
+ else
326
+ {
327
+ rb_str_concat(string, str);
328
+ }
329
+ rb_str_concat(string, quote);
330
+ }
331
+ }
332
+ }
333
+
334
+ struct Message_build_rf_header_each_value_arg {
335
+ VALUE string;
336
+ VALUE key;
337
+ VALUE space;
338
+ };
339
+
340
+ static VALUE Message_build_rf_header_each_value(VALUE value, struct Message_build_rf_header_each_value_arg* parg)
341
+ {
342
+ Message_name_value_concat(parg->string, parg->key);
343
+ rb_str_concat(parg->string, parg->space);
344
+ Message_name_value_concat(parg->string, value);
345
+ rb_str_concat(parg->string, parg->space);
346
+
347
+ return Qnil;
348
+ }
349
+
350
+ #if RUBY_VERSION_CODE > 183
351
+ static int Message_build_rf_header_each (VALUE key, VALUE value, VALUE string)
352
+ {
353
+ #else
354
+ static int Message_build_rf_header_each (VALUE array, VALUE string)
355
+ {
356
+ VALUE key = rb_ary_shift(array);
357
+ VALUE value = rb_ary_shift(array);
358
+ #endif
359
+ VALUE space = rb_str_new2(" ");
360
+ /* If Value is an Array, need to repeat name for each value */
361
+ if (TYPE(value) == T_ARRAY)
362
+ {
363
+ struct Message_build_rf_header_each_value_arg arg;
364
+ arg.key = key;
365
+ arg.string = string;
366
+ arg.space = space;
367
+ rb_iterate (rb_each, value, Message_build_rf_header_each_value, (VALUE)&arg);
368
+ }
369
+ else
370
+ {
371
+ Message_name_value_concat(string, key);
372
+ rb_str_concat(string, space);
373
+ Message_name_value_concat(string, value);
374
+ rb_str_concat(string, space);
375
+ }
376
+ return 0;
377
+ }
378
+
379
+ void Message_build_rf_header (VALUE hash, struct Message_build_header_arg* parg)
380
+ {
381
+ PMQBYTE p_data = *(parg->pp_buffer) + *(parg->p_data_offset);
382
+
383
+ static MQRFH MQRFH_DEF = {MQRFH_DEFAULT};
384
+ MQLONG name_value_len = 0;
385
+ PMQCHAR p_name_value = 0;
386
+ VALUE name_value = rb_hash_aref(hash, ID2SYM(ID_name_value));
387
+ MQLONG pad = 0;
388
+
389
+ MQRFH_DEF.CodedCharSetId = MQCCSI_INHERIT;
390
+
391
+ if(parg->trace_level>2)
392
+ printf ("WMQ::Message#build_rf_header Found rf_header\n");
393
+
394
+ if (!NIL_P(name_value))
395
+ {
396
+ if (TYPE(name_value) == T_HASH)
397
+ {
398
+ VALUE name_value_str = rb_str_buf_new(512); /* Allocate 512 char buffer, will grow as needed */
399
+ #if RUBY_VERSION_CODE > 183
400
+ rb_hash_foreach(name_value, Message_build_rf_header_each, name_value_str);
401
+ #else
402
+ rb_iterate (rb_each, name_value, Message_build_rf_header_each, name_value_str);
403
+ #endif
404
+ name_value = name_value_str;
405
+ }
406
+ else if(TYPE(name_value) != T_STRING)
407
+ {
408
+ rb_raise(rb_eArgError, ":name_value supplied in rf_header to WMQ::Message#headers must be either a String or a Hash");
409
+ }
410
+
411
+ name_value_len = RSTRING(name_value)->len;
412
+ if (name_value_len % 4) /* Not on 4 byte boundary ? */
413
+ {
414
+ rb_str_concat(name_value, rb_str_new(" ", 4 - (name_value_len % 4)));
415
+ name_value_len = RSTRING(name_value)->len;
416
+ }
417
+ p_name_value = RSTRING(name_value)->ptr;
418
+ }
419
+
420
+ p_data = Message_autogrow_data_buffer(parg, sizeof(MQRFH)+name_value_len);
421
+
422
+ memcpy(p_data, &MQRFH_DEF, sizeof(MQRFH));
423
+ Message_to_mqrfh(hash, (PMQRFH)p_data);
424
+ ((PMQRFH)p_data)->StrucLength = sizeof(MQRFH) + name_value_len;
425
+ if(parg->next_header_id)
426
+ {
427
+ Message_build_set_format(parg->next_header_id, ((PMQRFH)p_data)->Format);
428
+ }
429
+ else
430
+ {
431
+ strncpy(((PMQRFH)p_data)->Format, parg->data_format, MQ_FORMAT_LENGTH);
432
+ }
433
+
434
+ *(parg->p_data_offset) += sizeof(MQRFH);
435
+ p_data += sizeof(MQRFH);
436
+
437
+ if(name_value_len)
438
+ {
439
+ memcpy(p_data, p_name_value, name_value_len);
440
+ *(parg->p_data_offset) += name_value_len;
441
+ }
442
+
443
+ if(parg->trace_level>3)
444
+ printf ("WMQ::Message#build_rf_header Sizeof namevalue string:%ld\n", name_value_len);
445
+
446
+ if(parg->trace_level>2)
447
+ printf ("WMQ::Message#build_rf_header data offset:%ld\n", *(parg->p_data_offset));
448
+ }
449
+
450
+ static void Message_deblock_rf_header_each_pair(const char *p_name, const char *p_value, void* p_name_value_hash)
451
+ {
452
+ VALUE name_value_hash = (VALUE)p_name_value_hash;
453
+ VALUE key = rb_str_new2(p_name);
454
+ VALUE value = rb_str_new2(p_value);
455
+
456
+ /*
457
+ * If multiple values arrive for the same name (key) need to put values in an array
458
+ */
459
+ VALUE existing = rb_hash_aref(name_value_hash, key);
460
+ if(NIL_P(existing))
461
+ {
462
+ rb_hash_aset(name_value_hash, key, value);
463
+ }
464
+ else
465
+ {
466
+ if(TYPE(existing) == T_ARRAY) /* Add to existing Array */
467
+ {
468
+ rb_ary_push(existing, value);
469
+ }
470
+ else /* Convert existing entry into an array */
471
+ {
472
+ VALUE array = rb_ary_new();
473
+ rb_ary_push(array, existing);
474
+ rb_ary_push(array, value);
475
+ rb_hash_aset(name_value_hash, key, array);
476
+ }
477
+ }
478
+ }
479
+
480
+ /*
481
+ * Deblock Any custom data following RF Header
482
+ * The RF Header has already been deblocked into hash
483
+ * p_data points to the beginning of the RF Header structure
484
+ *
485
+ * msg_len is the length of the remainder of the message from this header onwards
486
+ * It includes the length of any data that may follow
487
+ *
488
+ * Returns the length of RF Header plus the size of any custom data following it
489
+ */
490
+ MQLONG Message_deblock_rf_header (VALUE hash, PMQBYTE p_data, MQLONG data_len)
491
+ {
492
+ MQLONG size = ((PMQRFH)p_data)->StrucLength;
493
+ VALUE name_value_hash = rb_hash_new();
494
+
495
+ rfh_toktype_t toktype;
496
+
497
+ if(size > data_len) /* Poison Message */
498
+ {
499
+ printf("WMQ::Message_deblock_rf_header StrucLength supplied in MQRFH exceeds total message length\n");
500
+ return 0;
501
+ }
502
+
503
+ toktype = rfh_decode_name_val_str(p_data + sizeof(MQRFH),
504
+ size - sizeof(MQRFH),
505
+ Message_deblock_rf_header_each_pair,
506
+ (void*)name_value_hash);
507
+
508
+ if (toktype != TT_END)
509
+ {
510
+ printf("Could not parse rfh name value string, reason %s\n",rfh_toktype_to_s(toktype));
511
+ }
512
+
513
+ rb_hash_aset(hash, ID2SYM(ID_name_value), name_value_hash);
514
+
515
+ return size;
516
+ }
517
+
518
+ /*
519
+ * RFH2 Header can contain multiple XML-like strings
520
+ * Message consists of:
521
+ * MQRFH2
522
+ * xml-string1-length (MQLONG)
523
+ * xml-string1 (Padded with spaces to match 4 byte boundary)
524
+ * xml-string2-length (MQLONG)
525
+ * xml-string2 (Padded with spaces to match 4 byte boundary)
526
+ * ....
527
+ */
528
+ MQLONG Message_deblock_rf_header_2 (VALUE hash, PMQBYTE p_buffer, MQLONG data_len)
529
+ {
530
+ MQLONG size = ((PMQRFH2)p_buffer)->StrucLength;
531
+ PMQBYTE p_data = p_buffer + sizeof(MQRFH2);
532
+ PMQBYTE p_end = p_buffer + size; /* Points to byte after last character */
533
+ MQLONG xml_len = 0;
534
+ VALUE xml_ary = rb_ary_new();
535
+
536
+ PMQBYTE pChar;
537
+ size_t length;
538
+ size_t i;
539
+
540
+ if(size > data_len) /* Poison Message */
541
+ {
542
+ printf("WMQ::Message_deblock_rf_header_2 StrucLength supplied in MQRFH exceeds total message length\n");
543
+ return 0;
544
+ }
545
+
546
+ rb_hash_aset(hash, ID2SYM(ID_xml), xml_ary);
547
+
548
+ while(p_data < p_end)
549
+ {
550
+ xml_len = *(PMQLONG)p_data;
551
+ p_data += sizeof(MQLONG);
552
+
553
+ if (p_data+xml_len > p_end)
554
+ {
555
+ printf("WMQ::Message#deblock_rf_header_2 Poison Message received, stopping further processing\n");
556
+ p_data = p_end;
557
+ }
558
+ else
559
+ {
560
+ /*
561
+ * Strip trailing spaces added as pad characters during put
562
+ */
563
+ length = 0;
564
+ pChar = p_data + xml_len - 1;
565
+ for (i = xml_len; i > 0; i--)
566
+ {
567
+ if (*pChar != ' ')
568
+ {
569
+ length = i;
570
+ break;
571
+ }
572
+ pChar--;
573
+ }
574
+ rb_ary_push(xml_ary, rb_str_new(p_data, length));
575
+ p_data += xml_len;
576
+ }
577
+ }
578
+
579
+ return size;
580
+ }
581
+
582
+ static VALUE Message_build_rf_header_2_each(VALUE element, struct Message_build_header_arg* parg)
583
+ {
584
+ VALUE str = StringValue(element);
585
+ MQLONG length = RSTRING(str)->len;
586
+
587
+ if (length % 4) /* Not on 4 byte boundary ? */
588
+ {
589
+ static const char * blanks = " ";
590
+ MQLONG pad = 4 - (length % 4);
591
+ MQLONG len = length + pad;
592
+ PMQBYTE p_data = Message_autogrow_data_buffer(parg, sizeof(len) + length + pad);
593
+
594
+ memcpy(p_data, (void*) &len, sizeof(len)); /* Start with MQLONG length indicator */
595
+ p_data += sizeof(len);
596
+ memcpy(p_data, RSTRING(str)->ptr, length);
597
+ p_data += length;
598
+ memcpy(p_data, blanks, pad);
599
+ p_data += pad;
600
+ *(parg->p_data_offset) += sizeof(len) + length + pad;
601
+ }
602
+ else
603
+ {
604
+ PMQBYTE p_data = Message_autogrow_data_buffer(parg, sizeof(length) + length);
605
+
606
+ memcpy(p_data, (void*) &length, sizeof(length)); /* Start with MQLONG length indicator */
607
+ p_data += sizeof(length);
608
+ memcpy(p_data, RSTRING(str)->ptr, length);
609
+ p_data += length;
610
+ *(parg->p_data_offset) += sizeof(length) + length;
611
+ }
612
+
613
+ return Qnil;
614
+ }
615
+
616
+ /*
617
+ * RFH2 Header can contain multiple XML-like strings
618
+ * Message consists of:
619
+ * MQRFH2
620
+ * xml-string1-length (MQLONG)
621
+ * xml-string1 (Padded with spaces to match 4 byte boundary)
622
+ * xml-string2-length (MQLONG)
623
+ * xml-string2 (Padded with spaces to match 4 byte boundary)
624
+ * ....
625
+ *
626
+ * The input data is either a String or an Array of Strings
627
+ * E.g.
628
+ *
629
+ * {
630
+ * :header_type =>:rf_header_2,
631
+ * :xml => '<hello>to the world</hello>'
632
+ * }
633
+ * OR
634
+ * {
635
+ * :header_type =>:rf_header_2,
636
+ * :xml => ['<hello>to the world</hello>', '<another>xml like string</another>'],
637
+ * }
638
+ *
639
+ */
640
+ void Message_build_rf_header_2(VALUE hash, struct Message_build_header_arg* parg)
641
+ {
642
+ static MQRFH2 MQRFH2_DEF = {MQRFH2_DEFAULT};
643
+ MQLONG rfh2_offset = *(parg->p_data_offset);
644
+ PMQBYTE p_data;
645
+ VALUE xml = rb_hash_aref(hash, ID2SYM(ID_xml));
646
+
647
+ if(parg->trace_level>2)
648
+ printf ("WMQ::Message#build_rf_header_2 Found rf_header_2\n");
649
+
650
+ /* Add MQRFH2 Header, ensure that their is enough space */
651
+ p_data = Message_autogrow_data_buffer(parg, sizeof(MQRFH2));
652
+ memcpy(p_data, &MQRFH2_DEF, sizeof(MQRFH2));
653
+ Message_to_mqrfh2(hash, (PMQRFH2)p_data);
654
+ if(parg->next_header_id)
655
+ {
656
+ Message_build_set_format(parg->next_header_id, ((PMQRFH2)p_data)->Format);
657
+ }
658
+ else
659
+ {
660
+ memcpy(((PMQRFH2)p_data)->Format, parg->data_format, MQ_FORMAT_LENGTH);
661
+ }
662
+ *(parg->p_data_offset) += sizeof(MQRFH2);
663
+
664
+ if (!NIL_P(xml))
665
+ {
666
+ if(TYPE(xml) == T_ARRAY)
667
+ {
668
+ rb_iterate (rb_each, xml, Message_build_rf_header_2_each, (VALUE)parg);
669
+ }
670
+ else if(TYPE(xml) != T_STRING)
671
+ {
672
+ Message_build_rf_header_2_each(xml, parg);
673
+ }
674
+ else
675
+ {
676
+ rb_raise(rb_eArgError, ":name_value supplied in rf_header_2 to WMQ::Message#headers must be a String or an Array");
677
+ }
678
+ }
679
+
680
+ /* Set total length in MQRFH2 */
681
+ ((PMQRFH)(*(parg->pp_buffer) + rfh2_offset))->StrucLength = *(parg->p_data_offset) - rfh2_offset;
682
+
683
+ if(parg->trace_level>2)
684
+ printf ("WMQ::Message#build_rf_header_2 data offset:%ld\n", *(parg->p_data_offset));
685
+ }
686
+