source: trunk/zoo-project/zoo-kernel/service_internal_ruby.c @ 581

Last change on this file since 581 was 581, checked in by djay, 9 years ago

Continue adding initial doxygen comments.

File size: 13.3 KB
Line 
1/*
2 * Author : Gérald FENOY
3 *
4 * Copyright (c) 2014 GeoLabs SARL
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24
25#include "service_internal_ruby.h"
26
27#if RUBY_API_VERSION_MAJOR >= 2 || RUBY_API_VERSION_MINOR == 9
28int argc=0;
29char **argv=NULL;
30#endif
31
32/**
33 * Load a Ruby file then run the function corresponding to the service by
34 * passing the conf, inputs and outputs parameters by value as JavaScript
35 * Objects.
36 *
37 * @param main_conf the conf maps containing the main.cfg settings
38 * @param request the map containing the HTTP request
39 * @param s the service structure
40 * @param real_inputs the maps containing the inputs
41 * @param real_outputs the maps containing the outputs
42 * @return SERVICE_SUCCEEDED or SERVICE_FAILED if the service run, -1
43 *  if the service failed to load or throw error at runtime.
44 */
45int zoo_ruby_support(maps** main_conf,map* request,service* s,maps **real_inputs,maps **real_outputs){
46#if RUBY_API_VERSION_MAJOR >= 2 || RUBY_API_VERSION_MINOR == 9
47  ruby_sysinit(&argc,&argv);
48  RUBY_INIT_STACK;
49#endif
50  ruby_init();
51  maps* m=*main_conf;
52  maps* inputs=*real_inputs;
53  maps* outputs=*real_outputs;
54  map* tmp0=getMapFromMaps(*main_conf,"lenv","cwd");
55  char *ntmp=tmp0->value;
56  map* tmp=NULL;
57  ruby_init_loadpath();
58  ruby_script("ZOO_EMBEDDED_ENV");
59 
60  VALUE klass=rb_define_module("Zoo");
61  rb_define_const(klass,"SERVICE_SUCCEEDED",INT2FIX(3));
62  rb_define_const(klass,"SERVICE_FAILED",INT2FIX(4));
63  typedef VALUE (*HOOK)(...);
64  rb_define_module_function(klass,"Translate",reinterpret_cast<HOOK>(RubyTranslate),-1);
65  rb_define_module_function(klass,"UpdateStatus",reinterpret_cast<HOOK>(RubyUpdateStatus),-1);
66
67  int error = 0;
68               
69  ID rFunc=Qnil;
70  tmp=getMap(s->content,"serviceProvider");
71  if(tmp!=NULL){
72#if RUBY_VERSION_MINOR == 8
73    const char* script = ruby_sourcefile = rb_source_filename(tmp->value);
74    rb_protect(LoadWrap, reinterpret_cast<VALUE>(script), &error);
75#else
76    rb_load_protect(rb_str_new2(tmp->value), 0, &error);
77#endif
78    if(error) {
79      ruby_trace_error(m);
80      return -1;
81    }
82#if RUBY_VERSION_MINOR == 8
83    ruby_exec();
84#else
85    ruby_exec_node(NULL);
86#endif
87  }
88  else{
89    map* err=createMap("text","Unable to parse serviceProvider please check your zcfg file.");
90    addToMap(err,"code","NoApplicableCode");
91    printExceptionReportResponse(m,err);
92    return -1;
93  }
94  int res=SERVICE_FAILED;
95  rFunc=rb_intern(s->name);
96  if(rFunc!=Qnil){
97    VALUE arg1=RubyHash_FromMaps(m);
98    VALUE arg2=RubyHash_FromMaps(inputs);
99    VALUE arg3=RubyHash_FromMaps(outputs);
100    VALUE rArgs[3]={arg1,arg2,arg3};
101    if (!rArgs)
102      return -1;
103    struct my_callback data;
104    data.obj=Qnil;
105    data.method_id=rFunc;
106    data.nargs=3;
107    data.args[0]=rArgs[0];
108    data.args[1]=rArgs[1];
109    data.args[2]=rArgs[2];
110    typedef VALUE (*HOOK)(VALUE);
111    VALUE tres=rb_protect(reinterpret_cast<HOOK>(FunCallWrap),(VALUE)(&data),&error);
112    if (TYPE(tres) == T_FIXNUM) {
113      res=FIX2INT(tres);
114      freeMaps(real_outputs);
115      free(*real_outputs);
116      freeMaps(main_conf);
117      free(*main_conf);
118      *main_conf=mapsFromRubyHash(arg1);
119      *real_outputs=mapsFromRubyHash(arg3);
120#ifdef DEBUG
121      dumpMaps(*main_conf);
122      dumpMaps(*real_outputs);
123#endif
124    }else{
125      ruby_trace_error(m);
126      res=-1;
127    }
128  }
129  else{
130    char tmpS[1024];
131    sprintf(tmpS, "Cannot find the %s function in the %s file.\n", s->name, tmp->value);
132    map* tmps=createMap("text",tmpS);
133    printExceptionReportResponse(m,tmps);
134    res=-1;
135  }
136  ruby_finalize();
137  return res;
138}
139
140/**
141 * Load a ruby file
142 *
143 * @arg the file to load
144 * @return Qnil
145 */
146VALUE LoadWrap(VALUE arg) {
147  const char *filename = reinterpret_cast<const char*>(arg);
148  rb_load_file(filename);
149  return Qnil;
150}
151
152/**
153 * Call a ruby function with parameters
154 *
155 * @arg the callback structure
156 * @return the value returned the called ruby function
157 */
158VALUE FunCallWrap(VALUE rdata) {
159  struct my_callback* data = (struct my_callback*) rdata;
160  return rb_funcall2(data->obj,data->method_id,data->nargs,data->args);
161}
162
163/**
164 * Print the Ruby Stack Trace in an ows:ExceptionReport XML Document
165 *
166 * @param m the conf maps containing the main.cfg settings
167 * @see printExceptionReportResponse
168 */
169void ruby_trace_error(maps* m){
170#if RUBY_VERSION_MINOR == 8
171  VALUE lasterr = rb_gv_get("$!");
172#else
173  VALUE lasterr = rb_errinfo();
174  VALUE ruby_errinfo = lasterr;
175#endif
176  VALUE message = rb_obj_as_string(lasterr);
177  VALUE lklass = rb_class_path(CLASS_OF(lasterr));
178#if RUBY_VERSION_MINOR == 8
179  char *trace=(char*)malloc((strlen(RSTRING(lklass)->ptr)+strlen(RSTRING(message)->ptr)+3)*sizeof(char));
180  sprintf(trace,"%s: %s",RSTRING_PTR(lklass),RSTRING_PTR(message));
181#else
182  char *trace=(char*)malloc((strlen(RSTRING_PTR(lklass))+strlen(RSTRING_PTR(message))+3)*sizeof(char));
183  sprintf(trace,"%s: %s",RSTRING_PTR(lklass),RSTRING_PTR(message));
184#endif
185  if(!NIL_P(ruby_errinfo))
186    {
187      VALUE ary = rb_funcall(ruby_errinfo, rb_intern("backtrace"), 0);
188      int c;
189      for (c=0; c<RARRAY_LEN(ary); c++) {
190        int len=strlen(trace);
191        char *tmp0=zStrdup(trace);
192#if RUBY_VERSION_MINOR == 8
193        trace=(char *) realloc(trace,(len+strlen(RSTRING(RARRAY(ary)->ptr[c])->ptr)+2)*sizeof(char));
194        sprintf(trace,"%s\n%s",tmp0,RSTRING(RARRAY(ary)->ptr[c])->ptr);
195#else
196        trace=(char *) realloc(trace,(len+strlen(RSTRING_PTR(RARRAY_PTR(ary)[c]))+2)*sizeof(char));
197        sprintf(trace,"%s\n%s",tmp0,RSTRING_PTR(RARRAY_PTR(ary)[c]));
198#endif
199        free(tmp0);
200      }
201    }
202  map* err=createMap("text",trace);
203  addToMap(err,"code","NoApplicableCode");
204  printExceptionReportResponse(m,err);
205}
206
207/**
208 * Convert a maps to a Ruby Hash
209 *
210 * @param t the maps to convert
211 * @return a new Ruby Hash
212 */
213VALUE RubyHash_FromMaps(maps* t){
214  VALUE res=rb_hash_new();
215  maps* tmp=t;
216  while(tmp!=NULL){
217    VALUE value=RubyHash_FromMap(tmp->content);
218    VALUE name=rb_str_new2(tmp->name);
219    rb_hash_aset(res,name,value);
220    tmp=tmp->next;
221  } 
222  return res;
223}
224
225/**
226 * Push the key on the array
227 *
228 * @param key the key to push
229 * @param value not used
230 * @param ary the Array
231 * @return ST_CONTINUE
232 */
233int
234keys_i(VALUE key,VALUE value,VALUE ary)
235{
236  if (key == Qundef) return ST_CONTINUE;
237  rb_ary_push(ary, key);
238  return ST_CONTINUE;
239}
240
241/**
242 * Return the size of a Ruby Hash
243 *
244 * @param hash the input Hash
245 */
246VALUE
247rb_hash_size(VALUE hash)
248{
249    return INT2FIX(RHASH_TBL(hash)->num_entries);
250}
251
252/**
253 * Convert a map to a Ruby Hash
254 *
255 * @param t the map to convert
256 * @return a new Ruby Hash
257 */
258VALUE RubyHash_FromMap(map* t){
259  VALUE res=rb_hash_new( );
260  map* tmp=t;
261  int hasSize=0;
262  map* isArray=getMap(tmp,"isArray");
263  map* size=getMap(tmp,"size");
264  map* tmap=getMapType(tmp);
265  while(tmp!=NULL){
266    VALUE name= rb_str_new2(tmp->name);
267    if(strcasecmp(tmp->name,"value")==0) {
268      if(isArray!=NULL){
269        map* len=getMap(tmp,"length");
270        int cnt=atoi(len->value);
271        VALUE value=rb_ary_new2(cnt);
272        VALUE mvalue=rb_ary_new2(cnt);
273        VALUE svalue=rb_ary_new2(cnt);
274
275        for(int i=0;i<cnt;i++){
276         
277          map* vMap=getMapArray(tmp,"value",i);     
278          map* sMap=getMapArray(tmp,"size",i);
279
280          if(vMap!=NULL){
281           
282            VALUE lvalue;
283            VALUE lsvalue;
284            if(sMap==NULL){
285              lvalue=rb_str_new2(vMap->value);
286              lsvalue=Qnil;
287            }
288            else{
289              lvalue=rb_str_new(vMap->value,atoi(sMap->value));
290              lsvalue=rb_str_new2(sMap->value);
291              hasSize=1;
292            }
293
294            rb_ary_push(value,lvalue);
295            rb_ary_push(svalue,lsvalue);
296          }
297         
298          map* mMap=getMapArray(tmp,tmap->name,i);
299
300          VALUE lmvalue;
301          if(mMap!=NULL){
302            lmvalue=rb_str_new2(mMap->value);
303          }else
304            lmvalue=Qnil;
305          rb_ary_push(mvalue,lmvalue);
306         
307        }
308
309        rb_hash_aset(res, name, mvalue);
310        rb_hash_aset(res, rb_str_new2(tmap->name), mvalue);
311     
312        if(hasSize>0){
313          VALUE lname=rb_str_new2("size");
314          rb_hash_aset(res, lname, value);
315        }
316      }
317      else if(size!=NULL){
318        VALUE value=rb_str_new(tmp->value,atoi(size->value));
319        rb_hash_aset(res, name, value);
320      }
321      else{
322        VALUE value=rb_str_new2(tmp->value);
323        rb_hash_aset(res, name, value);
324      }
325    }
326    else{
327      VALUE value=rb_str_new2(tmp->value);
328      rb_hash_aset(res, name, value);
329    }
330    tmp=tmp->next;
331  }
332  return res;
333}
334
335/**
336 * Convert a Ruby Hash to a maps
337 *
338 * @param t the Ruby Hash to convert
339 * @return a new maps
340 */
341maps* mapsFromRubyHash(VALUE t){
342  maps* res=NULL;
343  maps* cursor=res;
344  VALUE list;
345  list = rb_ary_new();
346  typedef int (*HOOK)(...);
347  rb_hash_foreach(t, reinterpret_cast<HOOK>(keys_i), list);
348  int nb=rb_hash_size(t);
349  int i;
350  for(i=0;i<FIX2INT(nb);i++){
351    VALUE key=rb_ary_pop(list);
352    VALUE value=rb_hash_aref(t,key);
353#ifdef DEBUG
354    fprintf(stderr,">> DEBUG VALUES : %s => %s\n",
355            StringValueCStr(key),StringValueCStr(value));
356#endif
357    cursor=(maps*)malloc(MAPS_SIZE);
358    cursor->name=StringValueCStr(key);
359    cursor->content=mapFromRubyHash(value);
360    cursor->next=NULL;
361    if(res==NULL)
362      res=dupMaps(&cursor);
363    else
364      addMapsToMaps(&res,cursor);
365    freeMap(&cursor->content);
366    free(cursor->content);
367    free(cursor);
368  }
369  return res;
370}
371
372/**
373 * Convert a Ruby Hash to a map
374 *
375 * @param t the Ruby Hash to convert
376 * @return a new map
377 */
378map* mapFromRubyHash(VALUE t){
379  map* res=NULL;
380  VALUE list;
381  list = rb_ary_new();
382  typedef int (*HOOK)(...);
383  rb_hash_foreach(t,reinterpret_cast<HOOK>(keys_i), list);
384  int nb=RHASH_TBL(t)->num_entries;
385  int i;
386  for(i=0;i<nb;i++){
387    VALUE key=rb_ary_pop(list);
388    VALUE value=rb_hash_aref(t,key);
389#ifdef DEBUG
390    fprintf(stderr,">> DEBUG VALUES : %s => %s\n",
391            StringValueCStr(key),StringValueCStr(value));
392#endif
393    if(strcmp(StringValueCStr(key),"value")==0){
394      char *buffer=NULL;
395      int size=RSTRING_LEN(value);
396      buffer=StringValueCStr(value);
397      if(res!=NULL){
398        addToMap(res,StringValueCStr(key),"");
399      }else{
400        res=createMap(StringValueCStr(key),"");
401      }
402      map* tmpR=getMap(res,"value");
403      free(tmpR->value);
404      tmpR->value=(char*)malloc((size+1)*sizeof(char));
405      memmove(tmpR->value,buffer,size*sizeof(char));
406      tmpR->value[size]=0;
407      char sin[1024];
408      sprintf(sin,"%d",size);
409      addToMap(res,"size",sin);
410    }else{
411      if(res!=NULL){
412        addToMap(res,StringValueCStr(key),StringValueCStr(value));
413      }
414      else{
415        res=
416          createMap(StringValueCStr(key),StringValueCStr(value));
417      }
418    }
419  }
420  return res;
421}
422
423/**
424 * Use the ZOO-Services messages translation function from the Ruby
425 * environment (ZOO-API)
426 *
427 * @param argc the number of parameters
428 * @param argv the parameter values given from the Ruby environment
429 * @param obj the Ruby object on which we run the method
430 * @return a new Ruby string containing the translated value
431 * @see _ss
432 */
433VALUE
434RubyTranslate(int argc, VALUE *argv, VALUE obj)
435{
436  return rb_str_new2(_ss(StringValueCStr(argv[0])));
437}
438
439/**
440 * Update the ongoing status of a running service from the Ruby environment
441 * (ZOO-API)
442 *
443 * @param argc the number of parameters
444 * @param argv the parameter values given from the Ruby environment
445 * @param obj the Ruby object on which we run the method
446 * @return a new Ruby string containing the translated value
447 * @see _updateStatus
448 */
449VALUE
450RubyUpdateStatus(int argc, VALUE *argv, VALUE obj)
451{
452  maps* conf;
453  VALUE confdict=argv[0];
454  int istatus=argv[1];
455  char* status;
456  if (istatus < 0 || istatus > 100){
457    fprintf(stderr,"Status must be a percentage.");
458    return Qnil;
459  }else{
460     char tmpStatus[4];
461     snprintf(tmpStatus, 4, "%i", istatus);
462     status = zStrdup(tmpStatus);
463  }
464  /* now update the map */
465  {
466    VALUE lenv = rb_hash_aref(confdict,rb_str_new2("lenv"));
467    if(TYPE(lenv)!=T_NIL){
468      VALUE valobj=rb_str_new2(status);
469      rb_hash_aset(lenv,rb_str_new2("status"),valobj);
470    }
471  }
472  conf = mapsFromRubyHash(confdict);
473  if (getMapFromMaps(conf,"lenv","status") != NULL){
474    fprintf(stderr,"STATUS RETURNED : %s\n",status);
475    if(status!=NULL){
476      setMapInMaps(conf,"lenv","status",status);
477      free(status);
478    }
479    else
480      setMapInMaps(conf,"lenv","status","15");
481    _updateStatus(conf);
482  }
483  freeMaps(&conf);
484  free(conf);
485  return Qnil;
486}
487
Note: See TracBrowser for help on using the repository browser.

Search

ZOO Sponsors

http://www.zoo-project.org/trac/chrome/site/img/geolabs-logo.pnghttp://www.zoo-project.org/trac/chrome/site/img/neogeo-logo.png http://www.zoo-project.org/trac/chrome/site/img/apptech-logo.png http://www.zoo-project.org/trac/chrome/site/img/3liz-logo.png http://www.zoo-project.org/trac/chrome/site/img/gateway-logo.png

Become a sponsor !

Knowledge partners

http://www.zoo-project.org/trac/chrome/site/img/ocu-logo.png http://www.zoo-project.org/trac/chrome/site/img/gucas-logo.png http://www.zoo-project.org/trac/chrome/site/img/polimi-logo.png http://www.zoo-project.org/trac/chrome/site/img/fem-logo.png http://www.zoo-project.org/trac/chrome/site/img/supsi-logo.png http://www.zoo-project.org/trac/chrome/site/img/cumtb-logo.png

Become a knowledge partner

Related links

http://zoo-project.org/img/ogclogo.png http://zoo-project.org/img/osgeologo.png