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

Last change on this file since 862 was 790, checked in by djay, 8 years ago

Add support for nested inputs and outputs.

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