From d6056e3b7f807deddc89077ac28143eec02999f5 Mon Sep 17 00:00:00 2001 From: miker Date: Fri, 26 Aug 2005 22:00:47 +0000 Subject: [PATCH] initial move of the REST gateway out to Open-ILS git-svn-id: svn://svn.open-ils.org/ILS/trunk@1755 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- .../src/apachemods/fieldmapper_lookup-gen.pl | 86 ++++++ Open-ILS/src/apachemods/fieldmapper_lookup.h | 13 + Open-ILS/src/apachemods/json_xml.c | 191 +++++++++++++ Open-ILS/src/apachemods/json_xml.h | 10 + Open-ILS/src/apachemods/mod_rest_gateway.c | 254 ++++++++++++++++++ Open-ILS/src/apachemods/mod_rest_gateway.h | 31 +++ 6 files changed, 585 insertions(+) create mode 100755 Open-ILS/src/apachemods/fieldmapper_lookup-gen.pl create mode 100644 Open-ILS/src/apachemods/fieldmapper_lookup.h create mode 100644 Open-ILS/src/apachemods/json_xml.c create mode 100644 Open-ILS/src/apachemods/json_xml.h create mode 100644 Open-ILS/src/apachemods/mod_rest_gateway.c create mode 100644 Open-ILS/src/apachemods/mod_rest_gateway.h diff --git a/Open-ILS/src/apachemods/fieldmapper_lookup-gen.pl b/Open-ILS/src/apachemods/fieldmapper_lookup-gen.pl new file mode 100755 index 0000000000..04a8480b7f --- /dev/null +++ b/Open-ILS/src/apachemods/fieldmapper_lookup-gen.pl @@ -0,0 +1,86 @@ +#!/usr/bin/perl +use strict; +use lib '../perlmods/'; + +my $map = {}; +eval " + use lib '../perlmods/'; + use OpenILS::Utils::Fieldmapper; +"; +$map = $Fieldmapper::fieldmap unless ($@); + +die $@ if ($@); + + +if(!$ARGV[0]) { + print "usage: $0 \n"; + exit; +} + +warn "Generating fieldmapper-c code...\n"; + + +print $ARGV[0] . "\n"; + +open(SOURCE, ">$ARGV[0]"); + +print SOURCE <{$object}->{hint}; + print SOURCE <<" C"; + else if (!strcmp(class, "$short_name")) { + switch (pos) { + C + for my $field (keys %{$map->{$object}->{fields}}) { + my $position = $map->{$object}->{fields}->{$field}->{position}; + print SOURCE <<" C"; + case $position: + return strdup("$field"); + break; + C + } + print SOURCE " }\n"; + print SOURCE " }\n"; +} +print SOURCE <{$object}->{hint}; + print SOURCE " else if (!strcmp(class, \"$short_name\")) return 1;"; +} +print SOURCE <{$object}->{hint}; + print SOURCE " else if (!strcmp(class, \"$short_name\")) {\n"; + for my $field (keys %{$map->{$object}->{fields}}) { + my $position = $map->{$object}->{fields}->{$field}->{position}; + print SOURCE " if (!strcmp(field,\"$field\")) return $position;\n"; + } + print SOURCE " }\n" +} + +print SOURCE " return -1;\n}"; +warn "done\n"; + diff --git a/Open-ILS/src/apachemods/fieldmapper_lookup.h b/Open-ILS/src/apachemods/fieldmapper_lookup.h new file mode 100644 index 0000000000..2e4d9c8526 --- /dev/null +++ b/Open-ILS/src/apachemods/fieldmapper_lookup.h @@ -0,0 +1,13 @@ + +#include +#include + +/* the JSON parser, so we can read the response we're XMLizing */ +#include "objson/object.h" +#include "objson/json_parser.h" +#include "utils.h" + +int isFieldmapper(char*); +int fm_ntop(char*,char*); +char* fm_pton(char*,int); + diff --git a/Open-ILS/src/apachemods/json_xml.c b/Open-ILS/src/apachemods/json_xml.c new file mode 100644 index 0000000000..e34f89fee9 --- /dev/null +++ b/Open-ILS/src/apachemods/json_xml.c @@ -0,0 +1,191 @@ +#include "json_xml.h" +#include "fieldmapper_lookup.h" + +void _rest_xml_output(growing_buffer*, object*, char*, int, int); +char* _escape_xml (char*); + +char* json_string_to_xml(char* content) { + object * obj; + growing_buffer * res_xml; + char * output; + int i; + + obj = json_parse_string( content ); + res_xml = buffer_init(1024); + + if (!obj) + return NULL; + + buffer_add(res_xml, ""); + + if(obj->is_array) { + for( i = 0; i!= obj->size; i++ ) { + _rest_xml_output(res_xml, obj->get_index(obj,i), NULL, 0,0); + } + } else { + _rest_xml_output(res_xml, obj, NULL, 0,0); + } + + buffer_add(res_xml, ""); + + output = buffer_data(res_xml); + buffer_free(res_xml); + free_object(obj); + + return output; +} + +char* _escape_xml (char* text) { + char* out; + growing_buffer* b = buffer_init(256); + int len = strlen(text); + int i; + for (i = 0; i < len; i++) { + if (text[i] == '&') + buffer_add(b,"&"); + else if (text[i] == '<') + buffer_add(b,"<"); + else if (text[i] == '>') + buffer_add(b,">"); + else + buffer_add_char(b,text[i]); + } + out = buffer_data(b); + buffer_free(b); + return out; +} + +void _rest_xml_output(growing_buffer* buf, object* obj, char * obj_class, int arr_index, int notag) { + char * tag; + int i; + + if(!obj) return; + + if (obj->classname) + notag = 1; + + if(isFieldmapper(obj_class)) { + tag = fm_pton(obj_class,arr_index); + } else if(obj_class) { + tag = strdup(obj_class); + } else { + tag = strdup("datum"); + } + + + /* add class hints if we have a class name */ + if(obj->classname) { + if(obj->is_null) { + buffer_fadd(buf,"<%s>", tag, obj->classname, tag); + return; + } else { + buffer_fadd(buf,"<%s>", tag, obj->classname); + } + } + + + /* now add the data */ + if(obj->is_null) { + if (!notag) + buffer_fadd(buf, "<%s/>",tag); + } else if(obj->is_bool && obj->bool_value) { + if (notag) + buffer_add(buf, "true"); + else + buffer_fadd(buf, "<%s>true",tag,tag); + + } else if(obj->is_bool && ! obj->bool_value) { + if (notag) + buffer_add(buf, "false"); + else + buffer_fadd(buf, "<%s>false",tag,tag); + + } else if (obj->is_string) { + if (notag) { + char * t = _escape_xml(obj->string_data); + buffer_add(buf,t); + free(t); + } else { + char * t = _escape_xml(obj->string_data); + buffer_fadd(buf,"<%s>%s",tag,t,tag); + free(t); + } + + } else if(obj->is_number) { + + if (notag) + buffer_fadd(buf,"%ld",obj->num_value); + else + buffer_fadd(buf,"<%s>%ld",tag,obj->num_value,tag); + + + } else if(obj->is_double) { + if (notag) + buffer_fadd(buf,"%lf",tag,obj->double_value,tag); + else + buffer_fadd(buf,"<%s>%lf",tag,obj->double_value,tag); + + + } else if (obj->is_array) { + if (!notag) { + if(!isFieldmapper(obj_class)) + buffer_add(buf,""); + else + buffer_fadd(buf,"<%s>",tag); + } + + for( i = 0; i!= obj->size; i++ ) { + _rest_xml_output(buf, obj->get_index(obj,i), obj->classname, i,0); + } + + if (!notag) { + if(!isFieldmapper(obj_class)) + buffer_add(buf,""); + else + buffer_fadd(buf,"",tag); + } + + } else if (obj->is_hash) { + + if (!notag) { + if(!obj_class) + buffer_add(buf,""); + else + buffer_fadd(buf,"<%s>",tag); + } + + object_iterator* itr = new_iterator(obj); + object_node* tmp; + while( (tmp = itr->next(itr)) ) { + if (notag) { + buffer_fadd(buf,"<%s>",tmp->key); + } else { + buffer_add(buf,""); + buffer_fadd(buf,"%s",tmp->key); + } + + _rest_xml_output(buf, tmp->item, NULL,0,notag); + + if (notag) { + buffer_fadd(buf,"",tmp->key); + } else { + buffer_add(buf,""); + } + } + free_iterator(itr); + + if (!notag) { + if(!obj_class) + buffer_add(buf,""); + else + buffer_fadd(buf,"",tag); + } + + } + + if (obj->classname) + buffer_fadd(buf,"",tag); + + free(tag); +} + diff --git a/Open-ILS/src/apachemods/json_xml.h b/Open-ILS/src/apachemods/json_xml.h new file mode 100644 index 0000000000..2752fd0492 --- /dev/null +++ b/Open-ILS/src/apachemods/json_xml.h @@ -0,0 +1,10 @@ + +#include +#include + +/* the JSON parser, so we can read the response we're XMLizing */ +#include "objson/object.h" +#include "objson/json_parser.h" +#include "utils.h" + +char* json_string_to_xml(char*); diff --git a/Open-ILS/src/apachemods/mod_rest_gateway.c b/Open-ILS/src/apachemods/mod_rest_gateway.c new file mode 100644 index 0000000000..ec4a547143 --- /dev/null +++ b/Open-ILS/src/apachemods/mod_rest_gateway.c @@ -0,0 +1,254 @@ +#include "mod_ils_gateway.h" + +char* ils_rest_gateway_config_file; + +static const char* ils_gateway_set_config(cmd_parms *parms, void *config, const char *arg) { + ils_gateway_config *cfg; + + cfg = ap_get_module_config(parms->server->module_config, &ils_rest_gateway_module); + + cfg->configfile = (char*) arg; + ils_rest_gateway_config_file = (char*) arg; + + return NULL; +} + +/* tell apache about our commands */ +static const command_rec ils_gateway_cmds[] = { + AP_INIT_TAKE1( GATEWAY_CONFIG, ils_gateway_set_config, NULL, RSRC_CONF, "gateway config file"), + {NULL} +}; + +/* build the config object */ +static void* ils_gateway_create_config( apr_pool_t* p, server_rec* s) { + ils_gateway_config* cfg = (ils_gateway_config*) apr_palloc(p, sizeof(ils_gateway_config)); + cfg->configfile = GATEWAY_DEFAULT_CONFIG; + return (void*) cfg; +} + + +static void mod_ils_gateway_child_init(apr_pool_t *p, server_rec *s) { + + char* cfg = ils_rest_gateway_config_file; + + if( ! osrf_system_bootstrap_client( cfg, CONFIG_CONTEXT) ) + fatal_handler("Unable to load gateway config file..."); + fprintf(stderr, "Bootstrapping %d\n", getpid() ); + fflush(stderr); +} + +static int mod_ils_gateway_method_handler (request_rec *r) { + + /* make sure we're needed first thing*/ + if (strcmp(r->handler, MODULE_NAME )) + return DECLINED; + + apr_pool_t *p = r->pool; /* memory pool */ + char* arg = r->args; /* url query string */ + + char* service = NULL; /* service to connect to */ + char* method = NULL; /* method to perform */ + + //json* exception = NULL; /* returned in error conditions */ + object* exception = NULL; /* returned in error conditions */ + string_array* sarray = init_string_array(12); /* method parameters */ + + growing_buffer* buffer = NULL; /* POST data */ + growing_buffer* tmp_buf = NULL; /* temp buffer */ + + char* key = NULL; /* query item name */ + char* val = NULL; /* query item value */ + + + + /* verify we are connected */ + if(!osrf_system_get_transport_client()) { + fatal_handler("Bootstrap Failed, no transport client"); + return HTTP_INTERNAL_SERVER_ERROR; + } + + + + /* gather the post args and append them to the url query string */ + if( !strcmp(r->method,"POST") ) { + + ap_setup_client_block(r,REQUEST_CHUNKED_DECHUNK); + + if(! ap_should_client_block(r)) { + warning_handler("No Post Body"); + } + + char body[1025]; + memset(body,0,1025); + buffer = buffer_init(1025); + + while(ap_get_client_block(r, body, 1024)) { + debug_handler("Apache read POST block data: %s\n", body); + buffer_add( buffer, body ); + memset(body,0,1025); + } + + if(arg && arg[0]) { + tmp_buf = buffer_init(1024); + buffer_add(tmp_buf,arg); + buffer_add(tmp_buf,buffer->buf); + arg = (char*) apr_pstrdup(p, tmp_buf->buf); + buffer_free(tmp_buf); + } else { + arg = (char*) apr_pstrdup(p, buffer->buf); + } + buffer_free(buffer); + + } + + debug_handler("params args are %s", arg); + + + if( ! arg || !arg[0] ) { /* we received no request */ + warning_handler("No Args"); + return OK; + } + + r->allowed |= (AP_METHOD_BIT << M_GET); + r->allowed |= (AP_METHOD_BIT << M_POST); + + + while( arg && (val = ap_getword(p, (const char**) &arg, '&'))) { + + key = ap_getword(r->pool, (const char**) &val, '='); + if(!key || !key[0]) + break; + + ap_unescape_url((char*)key); + ap_unescape_url((char*)val); + + if(!strcmp(key,"service")) + service = val; + + if(!strcmp(key,"method")) + method = val; + + if(!strcmp(key,"param")) + string_array_add(sarray, val); + + } + + info_handler("Performing(%d): service %s | method %s | \n", + getpid(), service, method ); + + int k; + for( k = 0; k!= sarray->size; k++ ) { + info_handler( "param %s", string_array_get_string(sarray,k)); + } + + osrf_app_session* session = osrf_app_client_session_init(service); + + debug_handler("MOD session service: %s", session->remote_service ); + + int req_id = osrf_app_session_make_req( session, NULL, method, 1, sarray ); + string_array_destroy(sarray); + + osrf_message* omsg = NULL; + + growing_buffer* result_data = buffer_init(256); + buffer_add(result_data, "["); + + /* gather result data */ + while((omsg = osrf_app_session_request_recv( session, req_id, 60 ))) { + + if( omsg->_result_content ) { + char* content = object_to_json(omsg->_result_content); + buffer_add(result_data, content); + buffer_add( result_data, ","); + free(content); + + } else { + + + /* build the exception information */ + growing_buffer* exc_buffer = buffer_init(256); + buffer_add( exc_buffer, "\nReceived Exception:\nName: " ); + buffer_add( exc_buffer, omsg->status_name ); + buffer_add( exc_buffer, "\nStatus: " ); + buffer_add( exc_buffer, omsg->status_text ); + buffer_add( exc_buffer, "\nStatus: " ); + + char code[16]; + memset(code, 0, 16); + sprintf( code, "%d", omsg->status_code ); + buffer_add( exc_buffer, code ); + + exception = json_parse_string("{}"); + exception->add_key(exception, "is_err", json_parse_string("1")); + exception->add_key(exception, "err_msg", new_object(exc_buffer->buf) ); + + warning_handler("*** Looks like we got a " + "server exception\n%s", exc_buffer->buf ); + + buffer_free(exc_buffer); + osrf_message_free(omsg); + break; + } + + osrf_message_free(omsg); + omsg = NULL; + } + + /* remove trailing comma */ + if( result_data->buf[strlen(result_data->buf)-1] == ',') { + result_data->buf[strlen(result_data->buf)-1] = '\0'; + result_data->n_used--; + } + + buffer_add(result_data,"]"); + + char* content = NULL; + + if(exception) { + content = exception->to_json(exception); + free_object(exception); + } + + /* set content type to text/xml for passing around XML objects */ + ap_set_content_type(r, "text/xml"); + if(content) { /* exception... */ + char* tmp = content; + content = json_string_to_xml( tmp ); + free(tmp); + } else { + content = json_string_to_xml( result_data->buf ); + } + + buffer_free(result_data); + + if(content) { + debug_handler( "APACHE writing data to web client: %s", content ); + ap_rputs(content,r); + free(content); + } + + osrf_app_session_request_finish( session, req_id ); + debug_handler("gateway process message successfully"); + + + osrf_app_session_destroy(session); + return OK; + +} + +static void mod_ils_gateway_register_hooks (apr_pool_t *p) { + ap_hook_handler(mod_ils_gateway_method_handler, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_child_init(mod_ils_gateway_child_init,NULL,NULL,APR_HOOK_MIDDLE); +} + + +module AP_MODULE_DECLARE_DATA ils_rest_gateway_module = { + STANDARD20_MODULE_STUFF, + NULL, + NULL, + ils_gateway_create_config, + NULL, + ils_gateway_cmds, + mod_ils_gateway_register_hooks, +}; + diff --git a/Open-ILS/src/apachemods/mod_rest_gateway.h b/Open-ILS/src/apachemods/mod_rest_gateway.h new file mode 100644 index 0000000000..a60f4311aa --- /dev/null +++ b/Open-ILS/src/apachemods/mod_rest_gateway.h @@ -0,0 +1,31 @@ +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_protocol.h" +#include "apr_compat.h" +#include "apr_strings.h" + +/* our stuff */ +#include "opensrf/transport_client.h" +#include "opensrf/osrf_message.h" +#include "opensrf/osrf_app_session.h" +#include "string_array.h" +#include "md5.h" +#include "objson/object.h" +#include "objson/json_parser.h" + +#include "rest_xml.h" +#define GATEWAY_CONFIG "ILSRestGatewayConfig" +#define MODULE_NAME "ils_rest_gateway_module" +#define CONFIG_CONTEXT "rest_gateway" + +#define GATEWAY_DEFAULT_CONFIG "/openils/conf/opensrf_core.xml" + + +/* our config structure */ +typedef struct { + char* configfile; /* our bootstrap config file */ +} ils_gateway_config; + +module AP_MODULE_DECLARE_DATA ils_rest_gateway_module; + -- 2.43.2