From b96d54a1d898eeeec9b4a84915281a61c6c3dbd3 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Franti=C5=A1ek=20Dvo=C5=99=C3=A1k?= Date: Fri, 26 Aug 2011 13:12:02 +0000 Subject: [PATCH] Events JSON generator. --- org.glite.lb.common/Makefile | 5 +- org.glite.lb.common/interface/events_json.h | 50 ++++++ org.glite.lb.common/src/events_json.c.T | 222 ++++++++++++++++++++++++++ org.glite.lb.common/test/parse.cpp.T | 9 ++ org.glite.lb.types/MultiStruct.pm | 16 ++ org.glite.lbjp-common.trio/interface/escape.h | 12 +- org.glite.lbjp-common.trio/src/escape.c | 69 ++++++++ org.glite.lbjp-common.trio/src/trio.c | 6 +- org.glite.lbjp-common.trio/test/trio_test.cpp | 20 +++ 9 files changed, 405 insertions(+), 4 deletions(-) create mode 100644 org.glite.lb.common/interface/events_json.h create mode 100644 org.glite.lb.common/src/events_json.c.T diff --git a/org.glite.lb.common/Makefile b/org.glite.lb.common/Makefile index bddaa50..b23e700 100644 --- a/org.glite.lb.common/Makefile +++ b/org.glite.lb.common/Makefile @@ -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 index 0000000..4ccec6b --- /dev/null +++ b/org.glite.lb.common/interface/events_json.h @@ -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 index 0000000..343d579 --- /dev/null +++ b/org.glite.lb.common/src/events_json.c.T @@ -0,0 +1,222 @@ +/* +@@@AUTO +*/ +@@@LANG: C + +#include +#include +#include + +#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; +} diff --git a/org.glite.lb.common/test/parse.cpp.T b/org.glite.lb.common/test/parse.cpp.T index aa3636e..407f3cc 100644 --- a/org.glite.lb.common/test/parse.cpp.T +++ b/org.glite.lb.common/test/parse.cpp.T @@ -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); diff --git a/org.glite.lb.types/MultiStruct.pm b/org.glite.lb.types/MultiStruct.pm index 629660c..9e7898b 100644 --- a/org.glite.lb.types/MultiStruct.pm +++ b/org.glite.lb.types/MultiStruct.pm @@ -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; diff --git a/org.glite.lbjp-common.trio/interface/escape.h b/org.glite.lbjp-common.trio/interface/escape.h index c6fcc64..3524e7d 100644 --- a/org.glite.lbjp-common.trio/interface/escape.h +++ b/org.glite.lbjp-common.trio/interface/escape.h @@ -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__ */ diff --git a/org.glite.lbjp-common.trio/src/escape.c b/org.glite.lbjp-common.trio/src/escape.c index e1d4aef..3077aa7 100644 --- a/org.glite.lbjp-common.trio/src/escape.c +++ b/org.glite.lbjp-common.trio/src/escape.c @@ -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; +} diff --git a/org.glite.lbjp-common.trio/src/trio.c b/org.glite.lbjp-common.trio/src/trio.c index 7c939cf..a83063d 100644 --- a/org.glite.lbjp-common.trio/src/trio.c +++ b/org.glite.lbjp-common.trio/src/trio.c @@ -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; diff --git a/org.glite.lbjp-common.trio/test/trio_test.cpp b/org.glite.lbjp-common.trio/test/trio_test.cpp index 5dc8b53..282ff6d 100644 --- a/org.glite.lbjp-common.trio/test/trio_test.cpp +++ b/org.glite.lbjp-common.trio/test/trio_test.cpp @@ -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 ); -- 1.8.2.3