rubywmq 0.3.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.
- data/.document +8 -0
- data/LICENSE +13 -0
- data/README +73 -0
- data/examples/each_a.rb +31 -0
- data/examples/each_b.rb +40 -0
- data/examples/each_header.rb +37 -0
- data/examples/files_to_q.cfg +24 -0
- data/examples/files_to_q.rb +47 -0
- data/examples/get_a.rb +34 -0
- data/examples/get_client.rb +50 -0
- data/examples/put1_a.rb +24 -0
- data/examples/put1_b.rb +32 -0
- data/examples/put1_c.rb +31 -0
- data/examples/put_a.rb +34 -0
- data/examples/put_b.rb +42 -0
- data/examples/put_dlh.rb +40 -0
- data/examples/put_dynamic_q.rb +37 -0
- data/examples/put_rfh.rb +66 -0
- data/examples/put_rfh2_a.rb +42 -0
- data/examples/put_rfh2_b.rb +42 -0
- data/examples/put_xmit_q.rb +32 -0
- data/examples/request.rb +59 -0
- data/examples/server.rb +96 -0
- data/ext/build.bat +3 -0
- data/ext/build.sh +5 -0
- data/ext/decode_rfh.c +348 -0
- data/ext/decode_rfh.h +45 -0
- data/ext/extconf.rb +44 -0
- data/ext/extconf_client.rb +40 -0
- data/ext/generate/generate_const.rb +167 -0
- data/ext/generate/generate_reason.rb +240 -0
- data/ext/generate/generate_structs.rb +99 -0
- data/ext/generate/wmq_structs.erb +388 -0
- data/ext/lib/wmq_temp.rb +197 -0
- data/ext/wmq.c +93 -0
- data/ext/wmq.h +324 -0
- data/ext/wmq_message.c +686 -0
- data/ext/wmq_mq_load.c +213 -0
- data/ext/wmq_queue.c +1410 -0
- data/ext/wmq_queue_manager.c +1592 -0
- data/lib/wmq.rb +25 -0
- data/tests/test.rb +299 -0
- metadata +87 -0
data/ext/wmq.c
ADDED
@@ -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
|
+
}
|
data/ext/wmq.h
ADDED
@@ -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
|
+
|
data/ext/wmq_message.c
ADDED
@@ -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
|
+
|