Events JSON generator.
authorFrantišek Dvořák <valtri@civ.zcu.cz>
Fri, 26 Aug 2011 13:12:02 +0000 (13:12 +0000)
committerFrantišek Dvořák <valtri@civ.zcu.cz>
Fri, 26 Aug 2011 13:12:02 +0000 (13:12 +0000)
org.glite.lb.common/Makefile
org.glite.lb.common/interface/events_json.h [new file with mode: 0644]
org.glite.lb.common/src/events_json.c.T [new file with mode: 0644]
org.glite.lb.common/test/parse.cpp.T
org.glite.lb.types/MultiStruct.pm
org.glite.lbjp-common.trio/interface/escape.h
org.glite.lbjp-common.trio/src/escape.c
org.glite.lbjp-common.trio/src/trio.c
org.glite.lbjp-common.trio/test/trio_test.cpp

index bddaa50..b23e700 100644 (file)
@@ -83,7 +83,7 @@ OBJS:=${JOBID_OBJS} ${PERF_OBJS} lb_plain_io.o events.o mini_http.o query_rec.o
        status.o xml_conversions.o xml_parse.o ulm_parse.o param.o \
        events_parse.o il_string.o il_int.o notifid.o \
        il_log.o il_msg.o log_msg.o context.o \
-       connpool.o 
+       connpool.o events_json.o
 LOBJS:=${OBJS:.o=.lo}
 
 THROBJS:=${OBJS:.o=.thr.o}
@@ -92,7 +92,8 @@ THRLOBJS:=${OBJS:.o=.thr.lo}
 HDRS:=context.h context-int.h lb_plain_io.h mini_http.h authz.h xml_parse.h \
        xml_conversions.h log_proto.h events_parse.h il_string.h il_msg.h \
        ulm_parse.h connpool.h notifid.h notif_rec.h padstruct.h \
-       query_rec.h timeouts.h LoggingExceptions.h CountRef.h ${PERF_HDRS} 
+       query_rec.h timeouts.h LoggingExceptions.h CountRef.h ${PERF_HDRS} \
+       events_json.h
 GEN_HDRS:=events.h jobstat.h common_version.h
 
 NOTHRSTATICLIB:=libglite_lb_common_${nothrflavour}.a
diff --git a/org.glite.lb.common/interface/events_json.h b/org.glite.lb.common/interface/events_json.h
new file mode 100644 (file)
index 0000000..4ccec6b
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef GLITE_LB_EVENTS_JSON_H
+#define GLITE_LB_EVENTS_JSON_H
+
+#ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+
+#ifdef BUILDING_LB_COMMON
+#include "events.h"
+#else
+#include "glite/lb/events.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** 
+ * Generate a special Notification ULM line from edg_wll_Event structure
+ * \param context IN: context to work with
+ * \param event IN: event to unparse
+ */
+extern int edg_wll_UnparseEventJSON(
+       edg_wll_Context context,
+       edg_wll_Event * event,
+       char **line
+);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* GLITE_LB_EVENTS_JSON_H */
diff --git a/org.glite.lb.common/src/events_json.c.T b/org.glite.lb.common/src/events_json.c.T
new file mode 100644 (file)
index 0000000..343d579
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+@@@AUTO
+*/
+@@@LANG: C
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+
+#include "glite/lbu/trio.h"
+#include "context-int.h"
+#include "events.h"
+#include "ulm_parse.h"
+
+
+@@@{
+       gen "#define MAX_COUNT ".(getMaxSize $event + 1)."\n"; # max fields count plus type field
+@@@}
+
+
+typedef struct json_item_s {
+       size_t len;
+       char *s;
+} json_item_t;
+
+typedef struct {
+       json_item_t items[MAX_COUNT];
+       size_t len, count;
+} json_t;
+
+
+static const struct timeval null_timeval = {0,0};
+
+
+static int json_add_raw(json_t *json, char *s);
+static int json_add_rawconst(json_t *json, const char *value);
+static int json_add_string(json_t *json, const char *key, char *value);
+
+
+int edg_wll_UnparseEventJSON(edg_wll_Context ctx, edg_wll_Event *event, char **result) {
+       char *out, *tmp_out, *str = NULL;
+       char ulm_time[ULM_DATE_STRING_LENGTH + 1];
+       json_t json;
+       size_t len, i;
+
+       memset(&json, 0, sizeof json);
+
+       if (!event) {
+               *result = NULL;
+               return 0;
+       }
+       if (event->type == EDG_WLL_EVENT_UNDEF) {
+               *result = strdup("{}");
+               if (*result) return 0;
+               else return edg_wll_SetError(ctx, ENOMEM, NULL);
+       }
+
+       //
+       // common fields
+       //
+
+       str = edg_wll_EventToString(event->type);
+       if (!str || json_add_string(&json, "type", str)) goto err;
+       free(str);
+@@@{
+       my ($t, $Tname, $tname, $tstruct, $source);
+       my ($f, $fname, $Fname);
+       my ($noNull, $indent);
+
+       for $t ('_common_', sort { $event->{order}->{$a} <=> $event->{order}->{$b} } $event->getTypes) {
+               selectType $event $t;
+               $Tname = $t eq '_common_' ? '' : $t;
+               $tname = lcfirst $t;
+               $TNAME = uc $t;
+               if ($tname =~ m/^pBS/) { $tname = ucfirst $tname; }
+               if ($tname =~ m/^cREAM/) { $tname = ucfirst $tname; }
+               if ($tname =~ m/^condor/) { $tname = ucfirst $tname; }
+               $tstruct = $t eq '_common_' ? '->any' : "->$tname";
+
+               if ($t ne '_common_') {
+                       gen "   case EDG_WLL_EVENT_$TNAME:\n";
+               }
+
+               for ($event->getFieldsOrdered) {
+                       $f = selectField $event $_;
+                       $fname = $f->{name};
+                       $Fname = ucfirst $f->{name};
+                       $source = "event$tstruct.$fname";
+
+                       $noNull = 0;
+                       $indent = $t eq '_common_' ? "" : "\t";
+                       if (not exists $f->{codes} and (
+                           $f->{type} eq 'int'
+                        or $f->{type} eq 'float'
+                        or $f->{type} eq 'double'
+                        or $f->{type} eq 'bool'
+                        or $f->{type} eq 'string'
+                       )) {
+                               $noNull = 1;
+                       } else {
+                               gen "$indent    if (";
+                               gen isNULL $f "$source";
+                               gen ") {\n";
+                               gen "$indent            if (json_add_rawconst(&json, \"$fname: null\")) goto err;\n";
+                               gen "$indent    } else {\n";
+                               $indent = "$indent\t";
+                       }
+
+                       if ($f->{codes}) {
+                               gen "$indent    str = edg_wll\_".($t eq '_common_' ? '' : $t)."${Fname}ToString($source);\n";
+                               gen "$indent    if (!str || json_add_string(&json, \"$fname\", str)) goto err;\n";
+                               gen "$indent    free(str);\n";
+                       }
+                       elsif ($f->{type} eq 'bool') {
+                               gen "$indent    str = $source ? \"true\" : \"false\";";
+                               gen "$indent    if (asprintf(&str, \"$fname: %s\", str) == -1 || json_add_raw(&json, str)) goto err;\n";
+                       }
+                       elsif ($f->{type} eq 'int'
+                           or $f->{type} eq 'float'
+                           or $f->{type} eq 'double'
+                           or $f->{type} eq 'port'
+                       ) {
+                               gen "$indent    if (asprintf(&str, \"$fname: ".(toFormatString $f)."\", $source) == -1 || json_add_raw(&json, str)) goto err;\n";
+                       }
+                       elsif ($f->{type} eq 'string') {
+                               gen "$indent    if (json_add_string(&json, \"$fname\", $source)) goto err;\n";
+                       }
+                       elsif ($f->{type} eq 'timeval') {
+                               gen "$indent    ".(toString $f "$source", "ulm_time")."\n";
+                               gen "$indent    if (json_add_string(&json, \"$fname\", ulm_time)) goto err;\n";
+                       }
+                       else {
+                               gen "$indent    ".(toString $f "$source", "str")."\n";
+                               gen "$indent    if (!str || json_add_string(&json, \"$fname\", str)) goto err;\n";
+                               gen "$indent    free(str);\n";
+                       }
+                       if (not $noNull) {
+                               $indent = $t eq '_common_' ? "" : "\t";
+                               gen "$indent    }\n";
+                       }
+               }
+               if ($t eq '_common_') {
+                       gen "\n";
+                       gen "   //\n";
+                       gen "   // event specific fields\n";
+                       gen "   //\n";
+                       gen "\n";
+                       gen "   switch (event->type) {\n";
+                       gen "\n";
+               } else {
+                       gen "           break;\n";
+                       gen "\n";
+               }
+       }
+       gen "   default:\n";
+       gen "           break;\n";
+       gen "\n";
+       gen "   }\n";
+@@@}
+
+       //
+       // generate result JSON string
+       //
+
+       len = 3               // '{', new line, '}'
+           + 4 * json.count  // space, space, PAIR, comma, new line
+           + json.len;
+
+       if ((out = malloc(len + 1)) == NULL) goto err;
+       sprintf(out, "{\n");
+       tmp_out = out + 2;
+       for (i = 0; i < json.count; i++) {
+               sprintf(tmp_out, "  %s%s\n", json.items[i].s, (i + 1 < json.count) ? "," : "");
+               tmp_out += (4 + json.items[i].len);
+               free(json.items[i].s);
+       
+       }
+       //assert((tmp_out - out) == (json.len + 4 * json.count + 2));
+       sprintf(tmp_out - 1, "}");
+       *result = out;
+
+       return 0;
+
+err:
+       free(str);
+       for (i = 0; i < json.count; i++) free(json.items[i].s);
+       *result = NULL;
+       return edg_wll_SetError(ctx, ENOMEM, NULL);
+}
+
+
+static int json_add_raw(json_t *json, char *s) {
+       json_item_t *item;
+
+       item = &json->items[json->count++];
+
+       assert(json->count <= MAX_COUNT);
+
+       item->s = s;
+       item->len = strlen(s);
+
+       json->len += item->len;
+
+       return 0;
+}
+
+
+static int json_add_rawconst(json_t *json, const char *value) {
+       char *s;
+
+       if ((s = strdup(value)) == NULL) return ENOMEM;
+       return json_add_raw(json, s);
+}
+
+static int json_add_string(json_t *json, const char *key, char *value) {
+       char *s = NULL;
+       int ret;
+
+       if (value) ret = trio_asprintf(&s, "%s: \"%|Js\"", key, value);
+       else ret = asprintf(&s, "%s: null", key);
+       return s && ret != -1 ? json_add_raw(json, s) : -1;
+}
index aa3636e..407f3cc 100644 (file)
@@ -25,8 +25,10 @@ limitations under the License.
 
 #ifdef BUILDING_LB_COMMON
 #include "events_parse.h"
+#include "events_json.h"
 #else
 #include "glite/lb/events_parse.h"
+#include "glite/lb/events_json.h"
 #endif
 
 class EventParseTest: public  CppUnit::TestFixture
@@ -146,6 +148,13 @@ void EventParseTest::$l()
                CPPUNIT_ASSERT_MESSAGE(std::string("edg_wll_CompareEvents():") + et + " " + ed, 0);
        }
 
+       free(line);
+       if (edg_wll_UnparseEventJSON(ctx, e1, &line)) {
+               CPPUNIT_ASSERT_MESSAGE(std::string("edg_wll_UnparseEventJSON() error"), 0);
+       } else {
+//             std::cerr << line << std::endl;
+       }
+
        edg_wll_FreeEvent(e1); free(e1);
        edg_wll_FreeEvent(e2); free(e2);
        edg_wll_FreeContext(ctx);
index 629660c..9e7898b 100644 (file)
@@ -226,4 +226,20 @@ sub getAllFieldsOrdered {
        } @names;
 }
 
+sub getMaxSize {
+       my $self = shift;
+       my $count = 0;
+       my @fields;
+
+       for my $t (keys %{$self->{fields}}) {
+               next if $t eq '_common_';
+               next if not exists $self->{fields}->{$t};
+               @fields = keys %{$self->{fields}->{$t}};
+               if ($#fields > $count) { $count = $#fields; }
+       }
+
+       @fields = keys %{$self->{fields}->{_common_}};
+       return $#fields + $count + 1 + 1;
+}
+
 1;
index c6fcc64..3524e7d 100644 (file)
@@ -68,9 +68,19 @@ char *glite_lbu_UnescapeXML(const char *);
  * \fn char *glite_lbu_EscapeSQL(const char *str)
  * \param str          a string to escape
  * \return             new (allocated) escaped string
- * \briefin given string (SQL) escape all unwanted characters
+ * \brief in given string (SQL) escape all unwanted characters
  */
 
 char *glite_lbu_EscapeSQL(const char *);
 
+
+/*!
+ * \fn char *glite_lbu_EscapeJSON(const char *str)
+ * \param str          a string to escape
+ * \return             new (allocated) escaped string
+ * \brief in given string (JSON) escape all unwanted characters
+ */
+
+char *glite_lbu_EscapeJSON(const char *in);
+
 #endif /* __EDG_WORKLOAD_LOGGING_COMMON_ESCAPE_H__ */
index e1d4aef..3077aa7 100644 (file)
@@ -241,3 +241,72 @@ char *glite_lbu_EscapeSQL(const char *in)
 
        return out;
 }
+
+char *glite_lbu_EscapeJSON(const char *in) {
+       const char * tmp_in;
+       char    *out = NULL;
+       int     i,j,cnt;
+
+       if (!in) return NULL;
+
+       for (cnt = 0, tmp_in = in; *tmp_in; tmp_in++) {
+               switch (*tmp_in) {
+               case '"':
+               case '\\':
+               case '/':
+               case '\b':
+               case '\f':
+               case '\n':
+               case '\r':
+               case '\t':
+                       cnt++;
+                       break;
+               default:
+                       if ((unsigned char)*tmp_in < 0x20) cnt += 6;
+                       break;
+               }
+       }
+
+       out = malloc(strlen(in)+1+cnt);
+
+       for (i=j=0; in[i]; i++) {
+               switch (in[i]) {
+               case '"':
+               case '\\':
+               case '/':
+                       out[j++] = '\\';
+                       out[j++] = in[i];
+                       break;
+               case '\b':
+                       out[j++] = '\\';
+                       out[j++] = 'b';
+                       break;
+               case '\f':
+                       out[j++] = '\\';
+                       out[j++] = 'f';
+                       break;
+               case '\n':
+                       out[j++] = '\\';
+                       out[j++] = 'n';
+                       break;
+               case '\r':
+                       out[j++] = '\\';
+                       out[j++] = 'r';
+                       break;
+               case '\t':
+                       out[j++] = '\\';
+                       out[j++] = 't';
+                       break;
+               default:
+                       if ((unsigned char)in[i] < 0x20) {
+                               snprintf(out + j, 7, "\\u%04x", in[i]);
+                               j += 6;
+                       } else
+                               out[j++] = in[i];
+                       break;
+               }
+       }
+       out[j] = 0;
+
+       return out;
+}
index 7c939cf..a83063d 100644 (file)
@@ -607,7 +607,7 @@ typedef struct {
   int base;
   int varsize;
 #ifdef QUALIFIER_ESCAPE
-  enum dg_escape { ESCAPE_NONE, ESCAPE_ULM, ESCAPE_XML, ESCAPE_SQL } escape;
+  enum dg_escape { ESCAPE_NONE, ESCAPE_ULM, ESCAPE_XML, ESCAPE_SQL, ESCAPE_JSON } escape;
 #endif
   int indexAfterSpecifier;
   union {
@@ -1330,6 +1330,7 @@ TrioPreprocess(int type,
                    case 'U': escape = ESCAPE_ULM; break;
                    case 'X': escape = ESCAPE_XML; break;
                    case 'S': escape = ESCAPE_SQL; break;
+                   case 'J': escape = ESCAPE_JSON; break;
                    default: return TRIO_ERROR_RETURN(TRIO_EINVAL,index);
                  }
                  break;
@@ -2874,6 +2875,9 @@ TrioFormatProcess(trio_T *data,
                        case ESCAPE_SQL:
                                s = glite_lbu_EscapeSQL(parameters[i].data.string);
                                break;
+                       case ESCAPE_JSON:
+                               s = glite_lbu_EscapeJSON(parameters[i].data.string);
+                               break;
                        case ESCAPE_NONE:
                                s = strdup(parameters[i].data.string ? parameters[i].data.string : empty);
                                break;
index 5dc8b53..282ff6d 100644 (file)
@@ -29,12 +29,14 @@ class TrioTest: public  CppUnit::TestFixture
        CPPUNIT_TEST(escapeULM);
        CPPUNIT_TEST(escapeXML);
        CPPUNIT_TEST(escapeSQL);
+       CPPUNIT_TEST(escapeJSON);
        CPPUNIT_TEST_SUITE_END();
 
 public:
        void escapeULM();
        void escapeXML();
        void escapeSQL();
+       void escapeJSON();
 };
 
 void TrioTest::escapeULM()
@@ -45,6 +47,8 @@ void TrioTest::escapeULM()
        std::cerr << e << std::endl;
 
        CPPUNIT_ASSERT_MESSAGE("escape ULM failed",!strcmp(e,r));
+
+       free(e);
 }
 
 void TrioTest::escapeXML()
@@ -55,6 +59,8 @@ void TrioTest::escapeXML()
        std::cerr << e << std::endl;
 
        CPPUNIT_ASSERT_MESSAGE("escape XML failed",!strcmp(e,r));
+
+       free(e);
 }
 
 void TrioTest::escapeSQL()
@@ -65,6 +71,20 @@ void TrioTest::escapeSQL()
        std::cerr << e << std::endl;
 
        CPPUNIT_ASSERT_MESSAGE("escape SQL failed",!strcmp(e,r));
+
+       free(e);
+}
+
+void TrioTest::escapeJSON() {
+       char    *e, *r = "START Jásoň doesn't like: \\\\\\n\\r\\b\\r\\t\\f and \\u001b END";
+       int     ret;
+
+       ret = trio_asprintf(&e, "START %|Js END", "Jásoň doesn't like: \\\n\r\b\r\t\f and \x1B");
+       std::cerr << e << std::endl;
+
+       CPPUNIT_ASSERT_MESSAGE("escape JSON failed",ret > 0 && strcmp(e,r) == 0);
+
+       free(e);
 }
 
 CPPUNIT_TEST_SUITE_REGISTRATION( TrioTest );