1 #include <opensrf/osrf_message.h>
5 @brief Implementation of osrfMessage.
8 static jsonObject* osrfMessageToJSON( const osrfMessage* msg );
10 static char default_locale[17] = "en-US\0\0\0\0\0\0\0\0\0\0\0\0";
11 static char* current_locale = NULL;
14 @brief Allocate and initialize an osrfMessage.
15 @param type One of CONNECT, REQUEST, RESULT, STATUS, or DISCONNECT.
16 @param thread_trace Thread trace.
17 @param protocol Protocol.
18 @return Pointer to the newly allocated osrfMessage.
20 The calling code is responsible for freeing the osrfMessage by calling osrfMessageFree().
22 osrfMessage* osrf_message_init( enum M_TYPE type, int thread_trace, int protocol ) {
24 osrfMessage* msg = (osrfMessage*) safe_malloc(sizeof(osrfMessage));
26 msg->thread_trace = thread_trace;
27 msg->protocol = protocol;
28 msg->status_name = NULL;
29 msg->status_text = NULL;
32 msg->is_exception = 0;
34 msg->_result_content = NULL;
35 msg->result_string = NULL;
36 msg->method_name = NULL;
37 msg->sender_locale = NULL;
38 msg->sender_tz_offset = 0;
45 @brief Return the previous locale.
46 @return A pointer to the locale specified by the last message to have been deserialized,
49 A JSON message may specify a locale string, which is saved as the current locale. If
50 the message does not specify a locale string, then the current locale becomes NULL.
52 This function returns a pointer to an internal buffer. Since both the address and the
53 contents of this buffer may change from one call to the next, the calling code should
54 either use the result immediately or make a copy of the string for later use.
56 const char* osrf_message_get_last_locale() {
57 return current_locale;
61 @brief Set the locale for a specified osrfMessage.
62 @param msg Pointer to the osrfMessage.
63 @param locale Pointer to the locale string to be installed in the osrfMessage.
64 @return Pointer to the new locale string for the osrfMessage, or NULL if either
67 If no locale is specified for an osrfMessage, we use the default locale.
69 Used for a REQUEST message.
71 const char* osrf_message_set_locale( osrfMessage* msg, const char* locale ) {
72 if( msg == NULL || locale == NULL )
74 if( msg->sender_locale )
75 free( msg->sender_locale );
76 return msg->sender_locale = strdup( locale );
80 @brief Change the default locale.
81 @param locale The new default locale.
82 @return A pointer to the new default locale if successful, or NULL if not.
84 The function returns NULL if the parameter is NULL, or if the proposed new locale is
85 longer than 16 characters.
87 At this writing, nothing calls this function.
89 const char* osrf_message_set_default_locale( const char* locale ) {
90 if( locale == NULL ) return NULL;
91 if( strlen(locale) > sizeof(default_locale) - 1 ) return NULL;
93 strcpy( default_locale, locale );
94 return default_locale;
98 @brief Populate the method_name member of an osrfMessage.
99 @param msg Pointer to the osrfMessage.
100 @param method_name The method name.
102 Used for a REQUEST message to specify what method to invoke.
104 void osrf_message_set_method( osrfMessage* msg, const char* method_name ) {
105 if( msg == NULL || method_name == NULL )
107 if( msg->method_name )
108 free( msg->method_name );
109 msg->method_name = strdup( method_name );
114 @brief Add a copy of a jsonObject to an osrfMessage as a parameter for a method call.
115 @param msg Pointer to the osrfMessage.
116 @param o Pointer to the jsonObject of which a copy is to be stored.
118 Make a copy of the input jsonObject, with all classnames encoded with JSON_CLASS_KEY and
119 JSON_DATA_KEY. Append it to a JSON_ARRAY stored at msg->_params.
121 If there is nothing at msg->_params, create a new JSON_ARRAY for it and add the new object
122 as the first element.
124 If the jsonObject is raw (i.e. the class information has not been decoded into classnames),
125 decode it. If the class information has already been decoded, discard it.
127 See also osrf_message_add_param().
129 At this writing, nothing calls this function.
131 void osrf_message_add_object_param( osrfMessage* msg, const jsonObject* o ) {
132 if(!msg|| !o) return;
134 msg->_params = jsonNewObjectType( JSON_ARRAY );
135 jsonObjectPush(msg->_params, jsonObjectDecodeClass( o ));
139 @brief Populate the params member of an osrfMessage.
140 @param msg Pointer to the osrfMessage.
141 @param o Pointer to a jsonObject representing the parameter(s) to a method.
143 Make a copy of a jsonObject and install it as the parameter list for a method call.
145 If the osrfMessage already has any parameters, discard them.
147 The @a o parameter should point to a jsonObject of type JSON_ARRAY, with each element
148 of the array being a parameter. If @a o points to any other type of jsonObject, create
149 a JSON_ARRAY as a wrapper for it, and install a copy of @a o as its only element.
151 Used for a REQUEST message, to pass parameters to a method. The alternative is to call
152 osrf_message_add_param() or osrf_message_add_object_param() repeatedly as needed to add
153 one parameter at a time.
155 void osrf_message_set_params( osrfMessage* msg, const jsonObject* o ) {
156 if(!msg || !o) return;
159 jsonObjectFree(msg->_params);
161 if(o->type == JSON_ARRAY) {
162 msg->_params = jsonObjectClone(o);
164 osrfLogDebug( OSRF_LOG_MARK, "passing non-array to osrf_message_set_params(), fixing...");
165 jsonObject* clone = jsonObjectClone(o);
166 msg->_params = jsonNewObjectType( JSON_ARRAY );
167 jsonObjectPush(msg->_params, clone);
174 @brief Add a JSON string to an osrfMessage as a parameter for a method call.
175 @param msg Pointer to the osrfMessage.
176 @param param_string A JSON string encoding the parameter to be added.
178 Translate the JSON string into a jsonObject, and append it to the parameter list.
179 If the parameter list doesn't already exist, create an empty one and add the new
182 Decode any class information in the raw JSON into classnames.
184 If the JSON string is not valid JSON, append a new jsonObject of type JSON_NULL.
186 Used for a REQUEST message, to pass a parameter to a method, The alternative is to
187 call osrf_message_set_params() to provide all the parameters at once. See also
188 osrf_message_add_object_param().
190 void osrf_message_add_param( osrfMessage* msg, const char* param_string ) {
191 if(msg == NULL || param_string == NULL) return;
192 if(!msg->_params) msg->_params = jsonNewObjectType( JSON_ARRAY );
193 jsonObjectPush(msg->_params, jsonParseString(param_string));
198 @brief Set the status_name, status_text, and status_code members of an osrfMessage.
199 @param msg Pointer to the osrfMessage to be populated.
200 @param status_name Status name (may be NULL).
201 @param status_text Status text (may be NULL).
202 @param status_code Status code.
204 If the @a status_name or @a status_text parameter is NULL, the corresponding member
207 Used for a RESULT or STATUS message.
209 void osrf_message_set_status_info( osrfMessage* msg,
210 const char* status_name, const char* status_text, int status_code ) {
213 if( status_name != NULL ) {
214 if( msg->status_name )
215 free( msg->status_name );
216 msg->status_name = strdup( status_name );
219 if( status_text != NULL ) {
220 if( msg->status_text )
221 free( msg->status_text );
222 msg->status_text = strdup( status_text );
225 msg->status_code = status_code;
230 @brief Populate the result_string and _result_content members of an osrfMessage.
231 @param msg Pointer to the osrfMessage to be populated.
232 @param json_string A JSON string encoding a result set.
234 Used for a RESULT message to return the results of a request, such as a database lookup.
236 void osrf_message_set_result_content( osrfMessage* msg, const char* json_string ) {
237 if( msg == NULL || json_string == NULL) return;
238 if( msg->result_string )
239 free( msg->result_string );
240 if( msg->_result_content )
241 jsonObjectFree( msg->_result_content );
243 msg->result_string = strdup(json_string);
244 msg->_result_content = jsonParseString(json_string);
249 @brief Free an osrfMessage and everything it owns.
250 @param msg Pointer to the osrfMessage to be freed.
252 void osrfMessageFree( osrfMessage* msg ) {
256 if( msg->status_name != NULL )
257 free(msg->status_name);
259 if( msg->status_text != NULL )
260 free(msg->status_text);
262 if( msg->_result_content != NULL )
263 jsonObjectFree( msg->_result_content );
265 if( msg->result_string != NULL )
266 free( msg->result_string);
268 if( msg->method_name != NULL )
269 free(msg->method_name);
271 if( msg->sender_locale != NULL )
272 free(msg->sender_locale);
274 if( msg->_params != NULL )
275 jsonObjectFree(msg->_params);
282 @brief Turn a collection of osrfMessages into one big JSON string.
283 @param msgs Pointer to an array of osrfMessages.
284 @param count Maximum number of messages to serialize.
285 @return Pointer to the JSON string.
287 Traverse the array, adding each osrfMessage in turn to a JSON_ARRAY. Stop when you have added the
288 maximum number of messages, or when you encounter a NULL pointer in the array. Then translate the
289 JSON_ARRAY into a JSON string.
291 The calling code is responsible for freeing the returned string.
293 char* osrfMessageSerializeBatch( osrfMessage* msgs [], int count ) {
294 if( !msgs ) return NULL;
296 jsonObject* wrapper = jsonNewObjectType(JSON_ARRAY);
299 while( (i < count) && msgs[i] ) {
300 jsonObjectPush(wrapper, osrfMessageToJSON( msgs[i] ));
304 char* j = jsonObjectToJSON(wrapper);
305 jsonObjectFree(wrapper);
312 @brief Turn a single osrfMessage into a JSON string.
313 @param msg Pointer to the osrfMessage to be serialized.
314 @return Pointer to the resulting JSON string.
316 Translate the osrfMessage into JSON, wrapped in a JSON array.
318 This function is equivalent to osrfMessageSerializeBatch() for an array of one pointer.
320 The calling code is responsible for freeing the returned string.
322 char* osrf_message_serialize(const osrfMessage* msg) {
324 if( msg == NULL ) return NULL;
327 jsonObject* json = osrfMessageToJSON( msg );
330 jsonObject* wrapper = jsonNewObjectType(JSON_ARRAY);
331 jsonObjectPush(wrapper, json);
332 j = jsonObjectToJSON(wrapper);
333 jsonObjectFree(wrapper);
341 @brief Translate an osrfMessage into a jsonObject.
342 @param msg Pointer to the osrfMessage to be translated.
343 @return Pointer to a newly created jsonObject.
345 The resulting jsonObject is a JSON_HASH with a classname of "osrfMessage", and the following keys:
349 - "payload" (only for STATUS, REQUEST, and RESULT messages)
351 The data for "payload" is also a JSON_HASH, whose structure depends on the message type:
353 For a STATUS message, the payload's classname is msg->status_name. The keys are "status" (carrying
354 msg->status_text) and "statusCode" (carrying the status code as a string).
356 For a REQUEST message, the payload's classname is "osrfMethod". The keys are "method" (carrying
357 msg->method_name) and "params" (carrying a jsonObject to pass any parameters to the method call).
359 For a RESULT message, the payload's classname is "osrfResult". The keys are "status" (carrying
360 msg->status_text), "statusCode" (carrying the status code as a string), and "content" (carrying a jsonObject
361 to return any results from the method call).
363 The calling code is responsible for freeing the returned jsonObject.
365 static jsonObject* osrfMessageToJSON( const osrfMessage* msg ) {
367 jsonObject* json = jsonNewObjectType(JSON_HASH);
368 jsonObjectSetClass(json, "osrfMessage");
371 osrf_clearbuf(sc, sizeof(sc));
373 INT_TO_STRING(msg->thread_trace);
374 jsonObjectSetKey(json, "threadTrace", jsonNewObject(INTSTR));
376 if (msg->sender_locale != NULL) {
377 jsonObjectSetKey(json, "locale", jsonNewObject(msg->sender_locale));
378 } else if (current_locale != NULL) {
379 jsonObjectSetKey(json, "locale", jsonNewObject(current_locale));
381 jsonObjectSetKey(json, "locale", jsonNewObject(default_locale));
384 switch(msg->m_type) {
387 jsonObjectSetKey(json, "type", jsonNewObject("CONNECT"));
391 jsonObjectSetKey(json, "type", jsonNewObject("DISCONNECT"));
395 jsonObjectSetKey(json, "type", jsonNewObject("STATUS"));
396 payload = jsonNewObject(NULL);
397 jsonObjectSetClass(payload, msg->status_name);
398 jsonObjectSetKey(payload, "status", jsonNewObject(msg->status_text));
399 snprintf(sc, sizeof(sc), "%d", msg->status_code);
400 jsonObjectSetKey(payload, "statusCode", jsonNewObject(sc));
401 jsonObjectSetKey(json, "payload", payload);
405 jsonObjectSetKey(json, "type", jsonNewObject("REQUEST"));
406 payload = jsonNewObject(NULL);
407 jsonObjectSetClass(payload, "osrfMethod");
408 jsonObjectSetKey(payload, "method", jsonNewObject(msg->method_name));
409 jsonObjectSetKey( payload, "params", jsonObjectDecodeClass( msg->_params ) );
410 jsonObjectSetKey(json, "payload", payload);
415 jsonObjectSetKey(json, "type", jsonNewObject("RESULT"));
416 payload = jsonNewObject(NULL);
417 jsonObjectSetClass(payload,"osrfResult");
418 jsonObjectSetKey(payload, "status", jsonNewObject(msg->status_text));
419 snprintf(sc, sizeof(sc), "%d", msg->status_code);
420 jsonObjectSetKey(payload, "statusCode", jsonNewObject(sc));
421 jsonObjectSetKey(payload, "content", jsonObjectDecodeClass( msg->_result_content ));
422 jsonObjectSetKey(json, "payload", payload);
431 @brief Translate a JSON array into an array of osrfMessages.
432 @param string The JSON string to be translated.
433 @param msgs Pointer to an array of pointers to osrfMessage, to receive the results.
434 @param count How many slots are available in the @a msgs array.
435 @return The number of osrfMessages created.
437 The JSON string is expected to be a JSON array, with each element encoding an osrfMessage.
439 If there are too many messages in the JSON array to fit into the pointer array, we
440 silently ignore the excess.
442 int osrf_message_deserialize(const char* string, osrfMessage* msgs[], int count) {
444 if(!string || !msgs || count <= 0) return 0;
448 jsonObject* json = jsonParseString(string);
451 osrfLogWarning( OSRF_LOG_MARK,
452 "osrf_message_deserialize() unable to parse data: \n%s\n", string);
456 // Traverse the JSON_ARRAY, turning each element into an osrfMessage
458 for( x = 0; x < json->size && x < count; x++ ) {
460 const jsonObject* message = jsonObjectGetIndex(json, x);
462 if(message && message->type != JSON_NULL &&
463 message->classname && !strcmp(message->classname, "osrfMessage")) {
465 osrfMessage* new_msg = safe_malloc(sizeof(osrfMessage));
467 new_msg->thread_trace = 0;
468 new_msg->protocol = 0;
469 new_msg->status_name = NULL;
470 new_msg->status_text = NULL;
471 new_msg->status_code = 0;
472 new_msg->is_exception = 0;
473 new_msg->_result_content = NULL;
474 new_msg->result_string = NULL;
475 new_msg->method_name = NULL;
476 new_msg->_params = NULL;
477 new_msg->next = NULL;
478 new_msg->sender_locale = NULL;
479 new_msg->sender_tz_offset = 0;
481 // Get the message type. If not specified, default to CONNECT.
482 const jsonObject* tmp = jsonObjectGetKeyConst(message, "type");
485 if( ( t = jsonObjectGetString(tmp)) ) {
487 if(!strcmp(t, "CONNECT")) new_msg->m_type = CONNECT;
488 else if(!strcmp(t, "DISCONNECT")) new_msg->m_type = DISCONNECT;
489 else if(!strcmp(t, "STATUS")) new_msg->m_type = STATUS;
490 else if(!strcmp(t, "REQUEST")) new_msg->m_type = REQUEST;
491 else if(!strcmp(t, "RESULT")) new_msg->m_type = RESULT;
494 // Get the thread trace, defaulting to zero.
495 tmp = jsonObjectGetKeyConst(message, "threadTrace");
497 const char* tt = jsonObjectGetString(tmp);
499 new_msg->thread_trace = atoi(tt);
503 // Get the sender's locale, or leave it NULL if not specified.
505 free( current_locale );
507 tmp = jsonObjectGetKeyConst(message, "locale");
509 if(tmp && (new_msg->sender_locale = jsonObjectToSimpleString(tmp))) {
510 current_locale = strdup( new_msg->sender_locale );
512 current_locale = NULL;
515 // Get the protocol, defaulting to zero.
516 tmp = jsonObjectGetKeyConst(message, "protocol");
519 const char* proto = jsonObjectGetString(tmp);
521 new_msg->protocol = atoi(proto);
525 tmp = jsonObjectGetKeyConst(message, "payload");
527 // Get method name and parameters for a REQUEST
528 const jsonObject* tmp0 = jsonObjectGetKeyConst(tmp,"method");
529 const char* tmp_str = jsonObjectGetString(tmp0);
531 new_msg->method_name = strdup(tmp_str);
533 tmp0 = jsonObjectGetKeyConst(tmp,"params");
535 // Note that we use jsonObjectDecodeClass() instead of
536 // jsonObjectClone(). The classnames are already decoded,
537 // but jsonObjectDecodeClass removes the decoded classnames.
538 new_msg->_params = jsonObjectDecodeClass( tmp0 );
539 if(new_msg->_params && new_msg->_params->type == JSON_NULL)
540 new_msg->_params->type = JSON_ARRAY;
543 // Get status fields for a RESULT or STATUS
545 new_msg->status_name = strdup(tmp->classname);
547 tmp0 = jsonObjectGetKeyConst(tmp,"status");
548 tmp_str = jsonObjectGetString(tmp0);
550 new_msg->status_text = strdup(tmp_str);
552 tmp0 = jsonObjectGetKeyConst(tmp,"statusCode");
554 tmp_str = jsonObjectGetString(tmp0);
556 new_msg->status_code = atoi(tmp_str);
557 if(tmp0->type == JSON_NUMBER)
558 new_msg->status_code = (int) jsonObjectGetNumber(tmp0);
561 // Get the content for a RESULT
562 tmp0 = jsonObjectGetKeyConst(tmp,"content");
564 // Note that we use jsonObjectDecodeClass() instead of
565 // jsonObjectClone(). The classnames are already decoded,
566 // but jsonObjectDecodeClass removes the decoded classnames.
567 new_msg->_result_content = jsonObjectDecodeClass( tmp0 );
571 msgs[numparsed++] = new_msg;
575 jsonObjectFree(json);
582 @brief Return a pointer to the result content of an osrfMessage.
583 @param msg Pointer to the osrfMessage whose result content is to be returned.
584 @return Pointer to the result content (or NULL if there is no such content, or if @a msg is NULL).
586 The returned pointer points into the innards of the osrfMessage. The calling code should @em not call
587 jsonObjectFree() on it, because the osrfMessage still owns it.
589 jsonObject* osrfMessageGetResult( osrfMessage* msg ) {
590 if(msg) return msg->_result_content;