initial move of the REST gateway out to Open-ILS
authormiker <miker@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Fri, 26 Aug 2005 22:00:47 +0000 (22:00 +0000)
committermiker <miker@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Fri, 26 Aug 2005 22:00:47 +0000 (22:00 +0000)
git-svn-id: svn://svn.open-ils.org/ILS/trunk@1755 dcc99617-32d9-48b4-a31d-7c20da2025e4

Open-ILS/src/apachemods/fieldmapper_lookup-gen.pl [new file with mode: 0755]
Open-ILS/src/apachemods/fieldmapper_lookup.h [new file with mode: 0644]
Open-ILS/src/apachemods/json_xml.c [new file with mode: 0644]
Open-ILS/src/apachemods/json_xml.h [new file with mode: 0644]
Open-ILS/src/apachemods/mod_rest_gateway.c [new file with mode: 0644]
Open-ILS/src/apachemods/mod_rest_gateway.h [new file with mode: 0644]

diff --git a/Open-ILS/src/apachemods/fieldmapper_lookup-gen.pl b/Open-ILS/src/apachemods/fieldmapper_lookup-gen.pl
new file mode 100755 (executable)
index 0000000..04a8480
--- /dev/null
@@ -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 <source_file>\n";
+       exit;
+}
+
+warn "Generating fieldmapper-c code...\n";
+
+
+print $ARGV[0] . "\n";
+
+open(SOURCE, ">$ARGV[0]");
+
+print SOURCE <<C;
+#include "fieldmapper_lookup.h"
+
+
+char * fm_pton(char * class, int pos) {
+       if (class == NULL) return NULL;
+C
+
+
+for my $object (keys %$map) {
+       my $short_name= $map->{$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 <<C;
+       return strdup("datum");
+}
+
+int isFieldmapper(char* class) {
+       if (class == NULL) return NULL;
+C
+
+for my $object (keys %$map) {
+       my $short_name= $map->{$object}->{hint};
+       print SOURCE "  else if (!strcmp(class, \"$short_name\")) return 1;";
+}
+print SOURCE <<C;
+       return NULL;
+}
+
+int fm_ntop(char* class, char* field);
+       if (class == NULL) return NULL;
+C
+
+
+for my $object (keys %$map) {
+       my $short_name= $map->{$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 (file)
index 0000000..2e4d9c8
--- /dev/null
@@ -0,0 +1,13 @@
+
+#include <string.h>
+#include <stdio.h>
+
+/* 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 (file)
index 0000000..e34f89f
--- /dev/null
@@ -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, "<response>");
+
+       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, "</response>");
+
+       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,"&amp;");
+               else if (text[i] == '<')
+                       buffer_add(b,"&lt;");
+               else if (text[i] == '>')
+                       buffer_add(b,"&gt;");
+               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><Object class_hint=\\\"%s\\\"/></%s>", tag, obj->classname, tag);
+                       return;
+               } else {
+                       buffer_fadd(buf,"<%s><Object class_hint=\\\"%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</%s>",tag,tag);
+                
+       } else if(obj->is_bool && ! obj->bool_value) {
+               if (notag)
+                       buffer_add(buf, "false");
+               else
+                       buffer_fadd(buf, "<%s>false</%s>",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</%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</%s>",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</%s>",tag,obj->double_value,tag);
+
+
+       } else if (obj->is_array) {
+               if (!notag) {
+                       if(!isFieldmapper(obj_class))
+                               buffer_add(buf,"<array>");
+                       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,"</array>");
+                       else
+                                       buffer_fadd(buf,"</%s>",tag);
+               }
+
+        } else if (obj->is_hash) {
+
+               if (!notag) {
+                       if(!obj_class)
+                               buffer_add(buf,"<hash>");
+                       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,"<pair>");
+                               buffer_fadd(buf,"<key>%s</key><value>",tmp->key);
+                       }
+
+                        _rest_xml_output(buf, tmp->item, NULL,0,notag);
+
+                       if (notag) {
+                               buffer_fadd(buf,"</%s>",tmp->key);
+                       } else {
+                               buffer_add(buf,"</value></pair>");
+                       }
+                }
+                free_iterator(itr);
+
+               if (!notag) {
+                       if(!obj_class)
+                               buffer_add(buf,"</hash>");
+                       else
+                                       buffer_fadd(buf,"</%s>",tag);
+               }
+
+       }
+
+       if (obj->classname)
+                buffer_fadd(buf,"</Object></%s>",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 (file)
index 0000000..2752fd0
--- /dev/null
@@ -0,0 +1,10 @@
+
+#include <string.h>
+#include <stdio.h>
+
+/* 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 (file)
index 0000000..ec4a547
--- /dev/null
@@ -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 (file)
index 0000000..a60f431
--- /dev/null
@@ -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;
+