diff options
| -rw-r--r-- | server/src/Makefile.am | 12 | ||||
| -rw-r--r-- | server/src/luaquerymapper.cc | 121 | ||||
| -rw-r--r-- | server/src/luaquerymapper.h | 36 | ||||
| -rw-r--r-- | server/src/queryparser.cc | 72 | ||||
| -rw-r--r-- | server/src/queryparser.h | 32 | 
5 files changed, 203 insertions, 70 deletions
| diff --git a/server/src/Makefile.am b/server/src/Makefile.am index 3fa0aad..dbc481a 100644 --- a/server/src/Makefile.am +++ b/server/src/Makefile.am @@ -130,7 +130,7 @@ test: $(TESTFILES)  	@echo "All tests done."  test_clean: -	rm -f $(TESTFILES) +	rm -f $(TESTFILES) $(TESTLOGS)  TEST_VERSIONSTR_FILES = \  	versionstr.cc \ @@ -168,8 +168,6 @@ test_queryhandlerpracro: $(TEST_QUERYHANDLERPRACRO_FILES)  TEST_QUERYPARSER_FILES = \  	queryparser.cc \ -	queryhandlerpentominos.cc \ -	tcpsocket.cc \  	$(BASICFILES) \  	$(PARSERFILES)  test_queryparser: $(TEST_QUERYPARSER_FILES) @@ -177,13 +175,9 @@ test_queryparser: $(TEST_QUERYPARSER_FILES)  TEST_LUAQUERMAPPER_FILES = \  	luaquerymapper.cc \ -	queryparser.cc \ -	queryhandlerpentominos.cc \ -	tcpsocket.cc \ -	$(BASICFILES) \ -	$(PARSERFILES) +	$(BASICFILES)  test_luaquerymapper: $(TEST_LUAQUERMAPPER_FILES) -	@../../tools/test $(TEST_LUAQUERMAPPER_FILES) $(LUA_LIBS) $(BASICFLAGS) $(PARSERFLAGS) +	@../../tools/test $(TEST_LUAQUERMAPPER_FILES) $(LUA_LIBS) $(BASICFLAGS)  TEST_TEMPLATEPARSER_FILES = \  	templateparser.cc \ diff --git a/server/src/luaquerymapper.cc b/server/src/luaquerymapper.cc index d6ec531..7ee43ff 100644 --- a/server/src/luaquerymapper.cc +++ b/server/src/luaquerymapper.cc @@ -28,8 +28,6 @@  #include <sstream> -#include "exception.h" -  static std::string loadresultstring(QueryResult &res, std::string group = "")  {    std::string s; @@ -46,7 +44,7 @@ static std::string loadresultstring(QueryResult &res, std::string group = "")    std::map< std::string, QueryResult >::iterator g = res.groups.begin();    while(g != res.groups.end()) { -    if(group + (*g).first != "") s += group + (*g).first + " = {}\n"; +    s += group + (*g).first + " = {}\n";      s += loadresultstring((*g).second, group + (*g).first + ".");      g++;    } @@ -54,8 +52,10 @@ static std::string loadresultstring(QueryResult &res, std::string group = "")    return s;  } -LUAQueryMapper::LUAQueryMapper() +LUAQueryMapper::LUAQueryMapper() throw(Exception)  { +  clean_top = -1; +    L = luaL_newstate();    if(L == NULL) {      error("Could not create LUA state."); @@ -69,16 +69,15 @@ LUAQueryMapper::LUAQueryMapper()  LUAQueryMapper::~LUAQueryMapper()  { -  lua_close(L); +  if(L) lua_close(L);  } -void LUAQueryMapper::addQueryResult(QueryResult &result) +void LUAQueryMapper::addQueryResult(QueryResult &result) throw(Exception)  {    std::string preload = loadresultstring(result);    PRACRO_DEBUG(querymapper, "Preload:\n%s\n", preload.c_str()); -    if(luaL_loadbuffer(L, preload.c_str(), preload.size(), "preload")) {      error(lua_tostring(L, lua_gettop(L)));      return; @@ -93,7 +92,7 @@ void LUAQueryMapper::addQueryResult(QueryResult &result)    clean_top = lua_gettop(L);  } -Value LUAQueryMapper::map(const std::string &mapper) +Value LUAQueryMapper::map(const std::string &mapper) throw(Exception)  {    Value v; @@ -123,17 +122,13 @@ Value LUAQueryMapper::map(const std::string &mapper)    // Check if app messed up the stack.    if(lua_gettop(L) != clean_top + 3) { -    error("Wrong number of return values in " + mapper); -    lua_pop(L, lua_gettop(L) - clean_top); +    error("Wrong number of return values (should be value, timestamp, source) in " + mapper);      return v;    } -  // value, timestamp, source -    // Check if the types are right    if(lua_isstring(L, lua_gettop(L)) == false) {      error("Source is not a string in " + mapper); -    lua_pop(L, 3);      return v;    }    v.source = lua_tostring(L, lua_gettop(L)); @@ -142,7 +137,6 @@ Value LUAQueryMapper::map(const std::string &mapper)    // Check if the types are right    if(lua_isnumber(L, lua_gettop(L)) == false) {      error("Timestamp is not an integer in " + mapper); -    lua_pop(L, 2);      return v;    }    v.timestamp = lua_tointeger(L, lua_gettop(L)); @@ -151,7 +145,6 @@ Value LUAQueryMapper::map(const std::string &mapper)    // Check if the types are right    if(lua_isstring(L, lua_gettop(L)) == false) {      error("Value is not a string in " + mapper); -    lua_pop(L, 1);      return v;    }    v.value = lua_tostring(L, lua_gettop(L)); @@ -163,42 +156,106 @@ Value LUAQueryMapper::map(const std::string &mapper)    return v;  } -void LUAQueryMapper::error(std::string message) +void LUAQueryMapper::error(std::string message) throw(Exception)  { +  if(clean_top != -1) lua_pop(L, lua_gettop(L) - clean_top); // Clean up stack    throw Exception("ERROR in LUAQueryMapper: " + message);  }  #ifdef TEST_LUAQUERYMAPPER -#include "queryhandlerpentominos.h" -#include "queryparser.h" -#include "tcpsocket.h" -  int main()  { -  TCPSocket s; -  s.connect("localhost", 11108); - -  QueryHandlerPentominos qh(&s, "2003791613"); +  QueryResult res; -  Query q1; -  q1.attributes["device_id"] = "lensmeter"; -  q1.attributes["device_type"] = "lensmeter"; +  time_t now = time(NULL); -  QueryResult res = qh.exec(q1); +  res.groups["test"].timestamp = now; +  res.groups["test"].source = "test app"; +  res.groups["test"].values["somevalue"] = "hello world"; +  res.groups["test"].values["pi"] = "3.1416";    printf("%s\n", loadresultstring(res).c_str());    LUAQueryMapper mapper;    mapper.addQueryResult(res); -  std::string luamap = "return status.errstr.value, 0, 0"; +  // Test simple value forwarding +  std::string luamap = "return test.somevalue.value, test.somevalue.timestamp, test.somevalue.source";    Value value = mapper.map(luamap); -  printf("%s : %s, %lu\n", luamap.c_str(), value.value.c_str(), value.timestamp); +  printf("%s =>\n %s, %lu, %s\n", luamap.c_str(), value.value.c_str(), value.timestamp, value.source.c_str()); +  if(value.value != "hello world" ||  value.timestamp != now || value.source != "test app") +    return 1; + +  // Do some calculations +  luamap = "return 2 * tonumber(test.pi.value), test.pi.timestamp, test.pi.source"; +  value = mapper.map(luamap); +  printf("%s =>\n %s, %lu, %s\n", luamap.c_str(), value.value.c_str(), value.timestamp, value.source.c_str()); +  if(value.value != "6.2832" ||  value.timestamp != now || value.source != "test app") +    return 1; + +  // Attempt to access nonexisting value (should throw an exception) +  try { +    luamap = "return test.somevalue2.value, test.somevalue2.timestamp, test.somevalue2.source"; +    value = mapper.map(luamap); +    printf("%s =>\n %s, %lu, %s\n", luamap.c_str(), value.value.c_str(), value.timestamp, value.source.c_str()); +    if(value.value != "hello world" ||  value.timestamp != now || value.source != "test app") +      return 1; +  } catch(Exception &e) { +    printf("ERROR: %s\n", e.what()); +    goto onandon; +  } +  return 1; + onandon: + +  // Attempt to access nonexisting group (should throw an exception) +  try { +    luamap = "return test2.somevalue.value, test2.somevalue.timestamp, test2.somevalue.source"; +    value = mapper.map(luamap); +    printf("%s =>\n %s, %lu, %s\n", luamap.c_str(), value.value.c_str(), value.timestamp, value.source.c_str()); +    if(value.value != "hello world" ||  value.timestamp != now || value.source != "test app") +      return 1; +  } catch(Exception &e) { +    printf("ERROR: %s\n", e.what()); +    goto stillonandon; +  } +  return 1; + stillonandon: +   +  // Switch order of return vars (should throw an exception) +  try { +    luamap = "return test.somevalue.source, test.somevalue.value, test.somevalue.timestamp"; +    value = mapper.map(luamap); +    printf("%s =>\n %s, %lu, %s\n", luamap.c_str(), value.value.c_str(), value.timestamp, value.source.c_str()); +    if(value.value != "hello world" ||  value.timestamp != now || value.source != "test app") +      return 1; +  } catch(Exception &e) { +    printf("ERROR: %s\n", e.what()); +    goto onandonagain; +  } +  return 1; + onandonagain: + +  // Syntax error (should throw an exception) +  try { +    luamap = "this(is{] not() - a != legal lua program!]"; +    value = mapper.map(luamap); +    printf("%s =>\n %s, %lu, %s\n", luamap.c_str(), value.value.c_str(), value.timestamp, value.source.c_str()); +    if(value.value != "hello world" ||  value.timestamp != now || value.source != "test app") +      return 1; +  } catch(Exception &e) { +    printf("ERROR: %s\n", e.what()); +    goto stillonandonagain; +  } +  return 1; + stillonandonagain: -  luamap = "return math.sin(status.errstr.timestamp) * 2, 0, 0"; +  // And finally test if we haven't broken enything while being hostile to the lua engine... +  luamap = "return test.somevalue.value, test.somevalue.timestamp, test.somevalue.source";    value = mapper.map(luamap); -  printf("%s : %s, %lu\n", luamap.c_str(), value.value.c_str(), value.timestamp); +  printf("%s =>\n %s, %lu, %s\n", luamap.c_str(), value.value.c_str(), value.timestamp, value.source.c_str()); +  if(value.value != "hello world" ||  value.timestamp != now || value.source != "test app") +    return 1;    return 0;  } diff --git a/server/src/luaquerymapper.h b/server/src/luaquerymapper.h index 4648606..e8a25f8 100644 --- a/server/src/luaquerymapper.h +++ b/server/src/luaquerymapper.h @@ -33,7 +33,9 @@  #include <lauxlib.h>  // For class Value -#include "database.h" +#include "dbtypes.h" + +#include "exception.h"  /**   * The LUAQueryMapper class takes the result of an external data query and @@ -41,23 +43,39 @@   */  class LUAQueryMapper {  public: -  LUAQueryMapper(); +  /** +   * Constructor. +   * Initialises the LUA library, and opens a LUA instance. +   * Throws an exception if any of these things fail. +   */ +  LUAQueryMapper() throw(Exception);    ~LUAQueryMapper();    /**     * Applies the mapping program to the result-namespace, and returns the -   * resulting value. +   * resulting value. The map must return 3 values; value (a string), timestamp +   * (an integer) and the source (string). +   * If the program fails to load, fails to run, returns wrong number of values, +   * or the wrong types, it will thorw an exception. +   * @param mapper A std::string containing the mapping program. +   * @return A Value object containing the three result values.     */ -  Value map(const std::string &mapper); - -  void error(std::string message); - -  void addQueryResult(QueryResult &result); +  Value map(const std::string &mapper) throw(Exception); +  +  /** +   * Add a result to be included in the mapper namespace. +   * If in some way the generated values fail (illegal names for example) an exception +   * will be thrown. +   * @param result The QueryResult object containing the values to add to the +   * namespace. +   */ +  void addQueryResult(QueryResult &result) throw(Exception);  private: +  void error(std::string message) throw(Exception); +    lua_State *L;    int clean_top; -  }; diff --git a/server/src/queryparser.cc b/server/src/queryparser.cc index 2b0dbdc..602ba80 100644 --- a/server/src/queryparser.cc +++ b/server/src/queryparser.cc @@ -76,12 +76,50 @@ void QueryParser::parseError(char *buf, size_t len, std::string error, int linen    if(fwrite(buf, len, 1, stderr) != len) {}    fprintf(stderr, "]\n");    fflush(stderr); + +  char *slineno; +  if(asprintf(&slineno, " at line %d\n", lineno) != -1) { +    throw Exception(error + slineno); +    free(slineno); +  } +  }  #ifdef TEST_QUERYPARSER -#include "queryhandlerpentominos.h" -#include "tcpsocket.h" +#include <string.h> + +static char xml[] = +  "<?xml version='1.0' encoding='UTF-8'?>\n" +  "<results>\n" +  "  <result class=\"testclass\" timestamp=\"1234567890\">\n" +  "    <group name=\"testgroup\">\n" +  "      <value name=\"testvalue\" value=\"42\"/>\n" +  "      <value name=\"anothertestvalue\" value=\"42\"/>\n" +  "    </group>\n" +  "    <group name=\"anothertestgroup\">\n" +  "      <value name=\"testvalue\" value=\"42\"/>\n" +  "      <value name=\"anothertestvalue\" value=\"42\"/>\n" +  "    </group>\n" +  "  </result>\n" +  "  <result class=\"anothertestclass\" timestamp=\"1234567890\">\n" +  "    <group name=\"testgroup\">\n" +  "      <value name=\"testvalue\" value=\"42\"/>\n" +  "      <value name=\"anothertestvalue\" value=\"42\"/>\n" +  "    </group>\n" +  "    <group name=\"anothertestgroup\">\n" +  "      <value name=\"testvalue\" value=\"42\"/>\n" +  "      <value name=\"anothertestvalue\" value=\"42\"/>\n" +  "    </group>\n" +  "  </result>\n" +  "</results>\n" +; + +static char badxml[] = +  "<?xml version='1.0' encoding='UTF-8'?>\n" +  "<results>\n" +  "</sulrets>\n" +;  static std::string loadresultstring(QueryResult &res, std::string group = "")  { @@ -105,20 +143,26 @@ static std::string loadresultstring(QueryResult &res, std::string group = "")  int main()  { -  TCPSocket s; -  s.connect("localhost", 11108); - -  QueryHandlerPentominos qh(&s, "2003791613"); - -  Query q1; -  q1.attributes["device_id"] = "lensmeter"; -  q1.attributes["device_type"] = "lensmeter"; +  // Parse something +  { +    QueryParser parser; +    parser.parse(xml, strlen(xml)); +    printf("%s\n", loadresultstring(parser.result).c_str()); +  } -  QueryResult res = qh.exec(q1); -   -  printf("%s\n", loadresultstring(res).c_str()); +  // Parse something, and fail! +  try { +    QueryParser parser; +    parser.parse(badxml, strlen(badxml)); +    printf("%s\n", loadresultstring(parser.result).c_str()); +  } catch(Exception &e) { +    printf("ERROR: %s\n", e.what()); +    goto weitergehen; +  } +  return 1; + weitergehen:    return 0;  } -#endif +#endif/*TEST_QUERYPARSER*/ diff --git a/server/src/queryparser.h b/server/src/queryparser.h index 947767c..3a73aa2 100644 --- a/server/src/queryparser.h +++ b/server/src/queryparser.h @@ -27,27 +27,47 @@  #ifndef __PRACRO_QUERYPARSER_H__  #define __PRACRO_QUERYPARSER_H__ -#include <time.h> - -#include "queryresult.h"  #include "saxparser.h" +#include <time.h>  #include <vector> +#include "queryresult.h"  #include "utf8.h" +#include "exception.h"  /** - * This class parses xml entyties into a QueryResult structure. + * This class parses xml entities into a QueryResult structure. + * Call the parent (SAXParser) method parse in order to actually parse something. + * If the parser fails (syntax error) it will throw an Exception. + * @see QueryResult result, in order to get the parsed data.   */  class QueryParser : public SAXParser {  public: +  /** +   * Constructor. +   */    QueryParser(); +  /** +   * The object will contain the result when the parsing is done. +   */ +  QueryResult result; + +  /** +   * Private parser callback. +   */    void startTag(std::string name, std::map< std::string, std::string> attributes); + +  /** +   * Private parser callback. +   */    void endTag(std::string name); -  void parseError(char *buf, size_t len, std::string error, int lineno); -  QueryResult result; +  /** +   * Private parser callback. +   */ +  void parseError(char *buf, size_t len, std::string error, int lineno);  private:    // For read | 
