diff options
26 files changed, 852 insertions, 254 deletions
diff --git a/server/src/Makefile.am b/server/src/Makefile.am index 167f4e4..bb172bd 100644 --- a/server/src/Makefile.am +++ b/server/src/Makefile.am @@ -36,6 +36,8 @@ pracrod_SOURCES = \ journal.cc \ journal_uploadserver.cc \ log.cc \ + luascript.cc \ + luaoncommit.cc \ luapraxisd.cc \ luaquerymapper.cc \ luaresume.cc \ @@ -51,7 +53,6 @@ pracrod_SOURCES = \ queryhandlerpentominos.cc \ queryhandlerpracro.cc \ queryparser.cc \ - resumeparser.cc \ saxparser.cc \ semaphore.cc \ server.cc \ @@ -97,6 +98,8 @@ EXTRA_DIST = \ journal.h \ journal_uploadserver.h \ log.h \ + luascript.h \ + luaoncommit.h \ luapraxisd.h \ luaquerymapper.h \ luaresume.h \ @@ -114,7 +117,6 @@ EXTRA_DIST = \ queryhandlerpracro.h \ queryparser.h \ queryresult.h \ - resumeparser.h \ saxparser.h \ semaphore.h \ server.h \ diff --git a/server/src/client_connection.cc b/server/src/client_connection.cc index b9d17b9..c3de7b0 100644 --- a/server/src/client_connection.cc +++ b/server/src/client_connection.cc @@ -108,6 +108,7 @@ void ClientConnection::nocommit(Session *session) } void ClientConnection::commit(Session *session) + throw(LUAScript::Exception, Journal::Exception) { if(docommit) { if(session->isReadonly()) { // Commit of an empty session discards it. @@ -117,7 +118,13 @@ void ClientConnection::commit(Session *session) DEBUG(connection, "Commit (%s)\n", session->id().c_str()); std::string sid = session->id(); - session->commit(); + try { + session->commit(); + } catch(LUAScript::Exception &e) { + throw e; + } catch(Journal::Exception &e) { + throw e; + } env.sessions.deleteSession(sid); sessionid = ""; docommit = false; @@ -208,7 +215,15 @@ bool ClientConnection::handle() response = handleTransaction(request, transaction, env, *session); } - commit(session); + try { + commit(session); + } catch(LUAScript::Exception &e) { + response = error_box(xml_encode(e.msg)); + return true; + } catch(Journal::Exception &e) { + response = error_box(xml_encode(e.msg)); + return true; + } nocommit(session); discard(session); diff --git a/server/src/client_connection.h b/server/src/client_connection.h index b8f7836..0bfa2e4 100644 --- a/server/src/client_connection.h +++ b/server/src/client_connection.h @@ -36,6 +36,8 @@ #include "transaction.h" #include "transactionparser.h" +#include "luascript.h" + class Session; class ClientConnection : public Connection { @@ -55,7 +57,7 @@ public: void getReply(Httpd::Reply &reply); private: - void commit(Session *session); + void commit(Session *session) throw(LUAScript::Exception, Journal::Exception); void nocommit(Session *session); void discard(Session *session); diff --git a/server/src/journal.cc b/server/src/journal.cc index fc4203c..04b1459 100644 --- a/server/src/journal.cc +++ b/server/src/journal.cc @@ -32,7 +32,8 @@ Journal::Journal() {} void Journal::addEntry(Transaction &transaction, Commit &commit, - std::string resume, Template *templ) + std::string resume, Template *templ, + LUAOnCommit *oncommit) { size_t index = 0; std::vector< Macro >::iterator i = templ->macros.begin(); @@ -70,16 +71,17 @@ void Journal::addEntry(Transaction &transaction, Commit &commit, } #endif - addEntry(resume, commit.macro, transaction.user, index); + addEntry(resume, commit.macro, transaction.user, index, oncommit); } void Journal::addEntry(std::string resume, std::string macro, - std::string user, int index) + std::string user, int index, LUAOnCommit *oncommit) { DEBUG(journal, "Add: %p %s %s - %s\n", this, macro.c_str(), user.c_str(), resume.c_str()); ResumeEntry re; + re.oncommit = oncommit; re.resume = resume; re.macro = macro; re.user = user; @@ -148,6 +150,22 @@ std::string Journal::patientID() return _patientid; } +void Journal::runOnCommitScripts() throw(LUAScript::Exception) +{ + std::map< int, ResumeEntry >::iterator i = entrylist.begin(); + while(i != entrylist.end()) { + if(i->second.oncommit != NULL) { + try { + i->second.oncommit->run(); + } catch(LUAScript::Exception &e) { + throw e; + } + } + i++; + } +} + + #ifdef TEST_JOURNAL //deps: debug.cc log.cc journal_uploadserver.cc journal_commit.cc //cflags: -I.. diff --git a/server/src/journal.h b/server/src/journal.h index 573f252..d098756 100644 --- a/server/src/journal.h +++ b/server/src/journal.h @@ -33,22 +33,31 @@ #include "transaction.h" #include "template.h" +#include "luaoncommit.h" class SessionSerialiser; class Journal { friend class SessionSerialiser; public: + class Exception { + public: + Exception(std::string m) : msg(m) {} + std::string msg; + }; + Journal(); virtual ~Journal() {} void addEntry(Transaction &transaction, Commit &commit, - std::string resume, Template *templ); + std::string resume, Template *templ, LUAOnCommit *oncommit); void addEntry(std::string resume, std::string macro, - std::string user, int index); + std::string user, int index, LUAOnCommit *oncommit); + + void runOnCommitScripts() throw(LUAScript::Exception); - virtual void commit() = 0; + virtual void commit() throw(Exception) = 0; std::string getEntry(std::string macro); void removeEntry(std::string macro); @@ -65,6 +74,7 @@ protected: std::string resume; std::string macro; std::string user; + LUAOnCommit *oncommit; bool dirty; }; diff --git a/server/src/journal_commit.cc b/server/src/journal_commit.cc index 0c1a93d..d765ebb 100644 --- a/server/src/journal_commit.cc +++ b/server/src/journal_commit.cc @@ -132,6 +132,7 @@ int journal_commit(const char *cpr, const char *user, // send body if(sock != -1 && write(sock, resume.c_str(), resume.size()) != (ssize_t)resume.size()) { ERR_LOG(journal, "write did not write all the bytes in the buffer.\n"); + return -1; } DEBUG(journal, "%s\n", buf); diff --git a/server/src/journal_uploadserver.cc b/server/src/journal_uploadserver.cc index a1299ec..eac6cd5 100644 --- a/server/src/journal_uploadserver.cc +++ b/server/src/journal_uploadserver.cc @@ -163,7 +163,10 @@ JournalUploadServer::JournalUploadServer(std::string host, } void JournalUploadServer::commit() + throw(Journal::Exception) { + int ret = 0; + #ifdef USE_MULTIPLE_USERS std::string resume; std::string olduser; @@ -177,9 +180,12 @@ void JournalUploadServer::commit() } if(i->second.user != olduser && olduser != "" && resume != "") { - journal_commit(patientID().c_str(), olduser.c_str(), - host.c_str(), port, - resume.c_str(), resume.size()); + ret = journal_commit(patientID().c_str(), olduser.c_str(), + host.c_str(), port, + resume.c_str(), resume.size()); + + if(ret == -1) throw Journal::Exception("Journal Commit error."); + // FIXME - UGLY HACK: Avoid upload server spooling in the wrong order. usleep(200000); resume = ""; @@ -195,9 +201,9 @@ void JournalUploadServer::commit() if(resume == "") return; - journal_commit(patientID().c_str(), olduser.c_str(), - host.c_str(), port, - resume.c_str(), resume.size()); + ret = journal_commit(patientID().c_str(), olduser.c_str(), + host.c_str(), port, + resume.c_str(), resume.size()); #else std::string resume; std::string user; @@ -222,10 +228,12 @@ void JournalUploadServer::commit() if(resume == "") return; // Connect to praxisuploadserver and commit all resumes in one bulk. - journal_commit(patientID().c_str(), user.c_str(), - host.c_str(), port, - resume.c_str(), resume.size()); + ret = journal_commit(patientID().c_str(), user.c_str(), + host.c_str(), port, + resume.c_str(), resume.size()); #endif/*USE_MULTIPLE_USERS*/ + + if(ret == -1) throw Journal::Exception("Journal Commit error."); } diff --git a/server/src/journal_uploadserver.h b/server/src/journal_uploadserver.h index 2393709..3e2fab3 100644 --- a/server/src/journal_uploadserver.h +++ b/server/src/journal_uploadserver.h @@ -33,7 +33,7 @@ class JournalUploadServer : public Journal { public: JournalUploadServer(std::string host, unsigned short int port); - void commit(); + void commit() throw(Journal::Exception); private: std::string host; diff --git a/server/src/luaoncommit.cc b/server/src/luaoncommit.cc new file mode 100644 index 0000000..8e96066 --- /dev/null +++ b/server/src/luaoncommit.cc @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * luaoncommit.cc + * + * Thu Jan 12 08:38:02 CET 2012 + * Copyright 2012 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "luaoncommit.h" + +#include <lua.hpp> +#include <lauxlib.h> + +#include "luautil.h" +#include "luapraxisd.h" + +#include "debug.h" + +#include <stdio.h> + +LUAOnCommit::LUAOnCommit(Transaction &t, Commit &c) : LUAScript() +{ + setEnv(LUAScript::ENV_PATIENTID, t.patientid); + setEnv(LUAScript::ENV_TEMPLATE, c.templ); + setEnv(LUAScript::ENV_MACRO, c.macro); + setEnv(LUAScript::ENV_USER, t.user); + + std::map<std::string, std::string>::iterator i = c.fields.begin(); + while(i != c.fields.end()) { + addValue(i->first, i->second); + i++; + } +} + +#ifdef TEST_LUAONCOMMIT +//Additional dependency files +//deps: +//Required cflags (autoconf vars may be used) +//cflags: +//Required link options (autoconf vars may be used) +//libs: +#include "test.h" + +TEST_BEGIN; + +// TODO: Put some testcode here (see test.h for usable macros). +TEST_TRUE(false, "No tests yet!"); + +TEST_END; + +#endif/*TEST_LUAONCOMMIT*/ diff --git a/server/src/resumeparser.h b/server/src/luaoncommit.h index 381e7c6..b64e744 100644 --- a/server/src/resumeparser.h +++ b/server/src/luaoncommit.h @@ -1,9 +1,10 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ /*************************************************************************** - * resumeparser.h + * luaoncommit.h * - * Mon Oct 1 11:17:35 CEST 2007 - * Copyright 2007 Bent Bisballe Nyeng + * Thu Jan 12 08:38:02 CET 2012 + * Copyright 2012 Bent Bisballe Nyeng * deva@aasimon.org ****************************************************************************/ @@ -24,13 +25,20 @@ * along with Pracro; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#ifndef __PRACRO_RESUMEPARSER_H__ -#define __PRACRO_RESUMEPARSER_H__ +#ifndef __PRACRO_LUAONCOMMIT_H__ +#define __PRACRO_LUAONCOMMIT_H__ + +#include "luascript.h" -#include <string> #include "transaction.h" -#include "template.h" +#include <string> + +class LUAOnCommit : public LUAScript { +public: + LUAOnCommit() {} + LUAOnCommit(Transaction &transaction, Commit &commit); -std::string resume_parser(Macro ¯o, Commit &commit); + const char *name() { return "lua on commit"; } +}; -#endif/*__PRACRO_RESUMEPARSER_H__*/ +#endif/*__PRACRO_LUAONCOMMIT_H__*/ diff --git a/server/src/luaresume.cc b/server/src/luaresume.cc index 49de8be..1db4fb3 100644 --- a/server/src/luaresume.cc +++ b/server/src/luaresume.cc @@ -26,122 +26,27 @@ */ #include "luaresume.h" +#include <lua.hpp> +#include <lauxlib.h> + #include "luautil.h" -#include "luapraxisd.h" #include "debug.h" #include <stdio.h> -#define GLOBAL_POINTER "_pracroGlobalLUAObjectPointerThisShouldBeANameThatIsNotAccidentallyOverwritten" - -static int _value(lua_State *L) -{ - Pracro::checkParameters(L, - Pracro::T_STRING, - Pracro::T_END); - - std::string name = lua_tostring(L, lua_gettop(L)); - - lua_getglobal(L, GLOBAL_POINTER); - LUAResume *lua = (LUAResume*)lua_touserdata(L, lua_gettop(L)); - - if(!lua) { - lua_pushstring(L, "No LUA pointer!"); - lua_error(L); - return 1; - } - - std::string value = lua->value(name); - lua_pushstring(L, value.c_str()); - - return 1; -} - -LUAResume::LUAResume(Commit &c) - : commit(c) -{ - L = luaL_newstate(); - if(L == NULL) { - ERR(luaresume, "Could not create LUA state.\n"); - return; - } - - luaL_openlibs(L); - - lua_pushlightuserdata(L, this); // Push the pointer to 'this' instance - lua_setglobal(L, GLOBAL_POINTER); // Assign it to a global lua var. - - lua_register(L, "value", _value); - - register_praxisd(L); -} - -LUAResume::~LUAResume() -{ - lua_close(L); -} - -std::string LUAResume::value(std::string name) +LUAResume::LUAResume(Transaction &t, Commit &c) : LUAScript() { - if(commit.fields.find(name) == commit.fields.end()) { - ERR(luaresume, "LUAResume: No such field '%s'\n", name.c_str()); - return ""; + setEnv(LUAScript::ENV_PATIENTID, t.patientid); + setEnv(LUAScript::ENV_TEMPLATE, c.templ); + setEnv(LUAScript::ENV_MACRO, c.macro); + setEnv(LUAScript::ENV_USER, t.user); + + std::map<std::string, std::string>::iterator i = c.fields.begin(); + while(i != c.fields.end()) { + addValue(i->first, i->second); + i++; } - - return commit.fields[name]; -} - -std::string LUAResume::run(std::string program) -{ - if(L == NULL) { - ERR(luaresume, "LUA state not initialized!"); - return ""; - } - - DEBUG(luaresume, "Running %s\n", program.c_str()); - - /* - lua_pushstring(L, value.toStdString().c_str()); - lua_setglobal(L, "value"); - - lua_pushstring(L, name.toStdString().c_str()); - lua_setglobal(L, "name"); - */ - - int top = lua_gettop(L); - - if(luaL_loadbuffer(L, program.c_str(), program.size(), - "lua resume generator")) { - ERR(luaresume, "loadbufer: %s\n", lua_tostring(L, lua_gettop(L))); - return ""; - } - - // Run the loaded code - if(lua_pcall(L, 0, LUA_MULTRET, 0)) { - ERR(luaresume, "pcall: %s\n" , lua_tostring(L, lua_gettop(L))); - return ""; - } - - if(top != lua_gettop(L) - 1) { - ERR(luaresume, "Program did not return a single value.\n"); - return ""; - } - - if(lua_isstring(L, lua_gettop(L)) == false) { - ERR(luaresume, "Program did not return a string value.\n"); - return ""; - } - - std::string res = lua_tostring(L, lua_gettop(L)); - lua_pop(L, 1); - - return res; -} - -void LUAResume::error(std::string message) -{ - ERR(luaresume, "LUA ERROR: %s\n", message.c_str()); } #ifdef TEST_LUARESUME diff --git a/server/src/luaresume.h b/server/src/luaresume.h index 1132f26..47a7652 100644 --- a/server/src/luaresume.h +++ b/server/src/luaresume.h @@ -27,26 +27,16 @@ #ifndef __PRACRO_LUARESUME_H__ #define __PRACRO_LUARESUME_H__ -#include <lua.hpp> -#include <lauxlib.h> +#include "luascript.h" #include "transaction.h" #include <string> -class LUAResume { +class LUAResume : public LUAScript { public: - LUAResume(Commit &commit); - ~LUAResume(); - - std::string run(std::string program); + LUAResume(Transaction &transaction, Commit &commit); - std::string value(std::string name); - - void error(std::string message); - -private: - lua_State *L; - Commit &commit; + const char *name() { return "lua resume generator"; } }; #endif/*__PRACRO_LUARESUME_H__*/ diff --git a/server/src/luascript.cc b/server/src/luascript.cc new file mode 100644 index 0000000..f4aef40 --- /dev/null +++ b/server/src/luascript.cc @@ -0,0 +1,303 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * luascript.cc + * + * Tue Jan 10 14:43:39 CET 2012 + * Copyright 2012 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "luascript.h" + +#include "configuration.h" +#include "debug.h" + +#include "luautil.h" +#include "luapraxisd.h" + +#define GLOBAL_POINTER "_pracroGlobalLUAObjectPointerThisShouldBeANameThatIsNotAccidentallyOverwritten" + +static int _value(lua_State *L) +{ + Pracro::checkParameters(L, + Pracro::T_STRING, + Pracro::T_END); + + std::string name = lua_tostring(L, lua_gettop(L)); + + lua_getglobal(L, GLOBAL_POINTER); + LUAScript *lua = (LUAScript*)lua_touserdata(L, lua_gettop(L)); + + if(!lua) { + lua_pushstring(L, "No LUA pointer!"); + lua_error(L); + return 1; + } + + if(lua->hasValue(name)) { + lua_pushstring(L, lua->value(name).c_str()); + } else { + lua_pushnil(L); + } + + return 1; +} + +static int _patientid(lua_State *L) +{ + Pracro::checkParameters(L, Pracro::T_END); + + lua_getglobal(L, GLOBAL_POINTER); + LUAScript *lua = (LUAScript*)lua_touserdata(L, lua_gettop(L)); + + if(!lua) { + lua_pushstring(L, "No LUA pointer!"); + lua_error(L); + return 1; + } + + lua_pushstring(L, lua->env(LUAScript::ENV_PATIENTID).c_str()); + + return 1; +} + +static int _template(lua_State *L) +{ + Pracro::checkParameters(L, Pracro::T_END); + + lua_getglobal(L, GLOBAL_POINTER); + LUAScript *lua = (LUAScript*)lua_touserdata(L, lua_gettop(L)); + + if(!lua) { + lua_pushstring(L, "No LUA pointer!"); + lua_error(L); + return 1; + } + + lua_pushstring(L, lua->env(LUAScript::ENV_TEMPLATE).c_str()); + + return 1; +} + +static int _macro(lua_State *L) +{ + Pracro::checkParameters(L, Pracro::T_END); + + lua_getglobal(L, GLOBAL_POINTER); + LUAScript *lua = (LUAScript*)lua_touserdata(L, lua_gettop(L)); + + if(!lua) { + lua_pushstring(L, "No LUA pointer!"); + lua_error(L); + return 1; + } + + lua_pushstring(L, lua->env(LUAScript::ENV_MACRO).c_str()); + + return 1; +} + +static int _user(lua_State *L) +{ + Pracro::checkParameters(L, Pracro::T_END); + + lua_getglobal(L, GLOBAL_POINTER); + LUAScript *lua = (LUAScript*)lua_touserdata(L, lua_gettop(L)); + + if(!lua) { + lua_pushstring(L, "No LUA pointer!"); + lua_error(L); + return 1; + } + + lua_pushstring(L, lua->env(LUAScript::ENV_USER).c_str()); + + return 1; +} + +LUAScript::LUAScript() +{ + L = NULL; +} + +void LUAScript::init() + throw(Exception) +{ + if(L) return; + + L = luaL_newstate(); + if(L == NULL) { + ERR(luaresume, "Could not create LUA state.\n"); + throw Exception("Could not create LUA state."); + } + + luaL_openlibs(L); + + lua_pushlightuserdata(L, this); // Push the pointer to 'this' instance + lua_setglobal(L, GLOBAL_POINTER); // Assign it to a global lua var. + + lua_register(L, "value", _value); + lua_register(L, "patientid", _patientid); + lua_register(L, "template", _template); + lua_register(L, "macro", _macro); + lua_register(L, "user", _user); + + register_praxisd(L); +} + +void LUAScript::addFile(std::string src) +{ + std::string file = + Conf::xml_basedir + "/include/" + src; + FILE *fp = fopen(file.c_str(), "r"); + if(fp) { + char buf[64]; + size_t sz; + std::string inc; + while((sz = fread(buf, 1, sizeof(buf), fp)) != 0) { + inc.append(buf, sz); + } + fclose(fp); + addCode(inc, file); + } +} + +void LUAScript::addCode(std::string c, std::string name) +{ + scripts.push_back(std::make_pair(c, name)); +} + +void LUAScript::addValue(std::string name, const std::string &value) +{ + values[name] = value; +} + +void LUAScript::addScripts(std::vector< Script > &scripts) +{ + std::vector< Script >::iterator spi = scripts.begin(); + while(spi != scripts.end()) { + if(spi->attributes.find("src") != spi->attributes.end()) { + std::string src = spi->attributes["src"]; + addFile(src); + } else { + addCode(spi->code); + } + spi++; + } +} + +void LUAScript::run() + throw(Exception) +{ + try { + init(); + } catch(Exception &e) { + throw Exception(e.msg); + } + + if(L == NULL) { + ERR(luaresume, "LUA state not initialized!"); + return; + } + + top = lua_gettop(L); + + std::vector<std::pair<std::string, std::string> >::iterator i = + scripts.begin(); + while(i != scripts.end()) { + std::string program = i->first; + std::string codename = name(); + if(i->second != "") codename += ": " + i->second; + + DEBUG(luaresume, "Running %s: %s\n", codename.c_str(), program.c_str()); + + if(luaL_loadbuffer(L, program.c_str(), program.size(), codename.c_str())) { + ERR(luaresume, "loadbufer: %s\n", lua_tostring(L, lua_gettop(L))); + throw Exception(lua_tostring(L, lua_gettop(L))); + } + + // Run the loaded code + if(lua_pcall(L, 0, LUA_MULTRET, 0)) { + ERR(luaresume, "pcall: %s\n" , lua_tostring(L, lua_gettop(L))); + throw Exception(lua_tostring(L, lua_gettop(L))); + } + + i++; + } +} + +std::string LUAScript::resultString() throw(Exception) +{ + if(top != lua_gettop(L) - 1) { + ERR(luaresume, "Program did not return a single value.\n"); + throw Exception("Program did not return a single value."); + } + + if(lua_isstring(L, lua_gettop(L)) == false) { + ERR(luaresume, "Program did not return a string value.\n"); + throw Exception("Program did not return a string value."); + } + + std::string res = lua_tostring(L, lua_gettop(L)); + lua_pop(L, 1); + + return res; +} + +bool LUAScript::hasValue(std::string name) +{ + return values.find(name) != values.end(); +} + +std::string LUAScript::value(std::string name) +{ + if(values.find(name) != values.end()) return values[name]; + return ""; +} + +std::string LUAScript::env(LUAScript::env_t id) +{ + if(_env.find(id) == _env.end()) return ""; + return _env[id]; +} + +void LUAScript::setEnv(LUAScript::env_t id, std::string value) +{ + _env[id] = value; +} + +#ifdef TEST_LUASCRIPT +//Additional dependency files +//deps: +//Required cflags (autoconf vars may be used) +//cflags: +//Required link options (autoconf vars may be used) +//libs: +#include "test.h" + +TEST_BEGIN; + +// TODO: Put some testcode here (see test.h for usable macros). +TEST_TRUE(false, "No tests yet!"); + +TEST_END; + +#endif/*TEST_LUASCRIPT*/ diff --git a/server/src/luascript.h b/server/src/luascript.h new file mode 100644 index 0000000..ec6c95f --- /dev/null +++ b/server/src/luascript.h @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * luascript.h + * + * Tue Jan 10 14:43:39 CET 2012 + * Copyright 2012 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __PRACRO_LUASCRIPT_H__ +#define __PRACRO_LUASCRIPT_H__ + +#include <lua.hpp> +#include <lauxlib.h> + +#include <string> +#include <map> +#include <vector> + +#include "template.h" + +class LUAScript { + friend class SessionSerialiser; + friend class SessionParser; +public: + typedef enum { + ENV_PATIENTID, + ENV_TEMPLATE, + ENV_MACRO, + ENV_USER + } env_t; + + class Exception { + public: + Exception(std::string m) : msg(m) {} + std::string msg; + }; + + LUAScript(); + + virtual const char *name() { return ""; } + + void init() throw(Exception); + + void addFile(std::string file); + void addCode(std::string code, std::string codename = ""); + void addScripts(std::vector< Script > &scripts); + + void addValue(std::string name, const std::string &value); + + void run() throw(Exception); + + bool hasValue(std::string name); + std::string value(std::string value); + + std::string env(env_t id); + void setEnv(env_t id, std::string value); + + std::string resultString() throw(Exception); + +protected: + lua_State *L; + + std::map<env_t, std::string> _env; + +private: + std::vector<std::pair<std::string, std::string> > scripts; + std::map<std::string, std::string> values; + + int top; +}; + + +#endif/*__PRACRO_LUASCRIPT_H__*/ diff --git a/server/src/macroparser.cc b/server/src/macroparser.cc index 7d3f367..be781aa 100644 --- a/server/src/macroparser.cc +++ b/server/src/macroparser.cc @@ -116,6 +116,11 @@ void MacroParser::characterData(std::string &data) assert(current_script); // No script present! current_script->code.append(data); } + + if(state == COMMIT_SCRIPT) { + assert(current_commit_script); // No script present! + current_commit_script->code.append(data); + } } void MacroParser::startTag(std::string name, attributes_t &attr) @@ -147,6 +152,18 @@ void MacroParser::startTag(std::string name, attributes_t &attr) return; } + // Enable oncommit parsing + if(name == "oncommit") { + if(state != MACRO) error("oncommit found outside macro."); + state = COMMIT_SCRIPTS; + + m->resume.attributes = attr; + + assert(m); // No macro is currently available, cannot create resume! + + return; + } + // Enable Query parsing if(name == "queries") { if(state != MACRO) error("queries found outside macro."); @@ -232,8 +249,18 @@ void MacroParser::startTag(std::string name, attributes_t &attr) current_resume_script = &(m->resume_scripts.back()); } break; + case COMMIT_SCRIPTS: + { + state = COMMIT_SCRIPT; + + Script s; + s.attributes = attr; + m->commit_scripts.push_back(s); + current_commit_script = &(m->commit_scripts.back()); + } + break; default: - error("<script> tag found outside <scripts> or <resume> tags."); + error("<script> tag found outside <scripts>, <commitscripts> or <resume> tags."); break; } return; @@ -290,6 +317,7 @@ void MacroParser::endTag(std::string name) state = UNDEFINED; } if(name == "resume") state = MACRO; + if(name == "oncommit") state = MACRO; if(name == "queries") state = MACRO; if(name == "query") state = QUERIES; if(name == "maps") state = MACRO; @@ -310,6 +338,11 @@ void MacroParser::endTag(std::string name) state = RESUME; break; + case COMMIT_SCRIPT: + current_commit_script = NULL; + state = COMMIT_SCRIPTS; + break; + default: // tag mismatch? break; diff --git a/server/src/macroparser.h b/server/src/macroparser.h index 71ef911..ab6fda5 100644 --- a/server/src/macroparser.h +++ b/server/src/macroparser.h @@ -42,7 +42,9 @@ class MacroParser : public SAXParser { MAP, WIDGETS, SCRIPTS, - SCRIPT + SCRIPT, + COMMIT_SCRIPTS, + COMMIT_SCRIPT } ParserState; public: @@ -76,6 +78,7 @@ private: Map *current_map; Script *current_script; Script *current_resume_script; + Script *current_commit_script; std::vector< Widget* > widgetstack; // Error callback function. diff --git a/server/src/pracrodaopgsql.cc b/server/src/pracrodaopgsql.cc index f9a773f..db0a9aa 100644 --- a/server/src/pracrodaopgsql.cc +++ b/server/src/pracrodaopgsql.cc @@ -228,6 +228,7 @@ void PracroDAOPgsql::commitTransaction(std::string sessionid, } +//#define NEW Values PracroDAOPgsql::getLatestValues(std::string sessionid, std::string patientid, Macro *macro, @@ -261,6 +262,70 @@ Values PracroDAOPgsql::getLatestValues(std::string sessionid, } pqxx::work W(*conn); + +#ifdef NEW + + // Do not search for nothing... + if(fieldnames.size() == 0) return values; + + std::string names; + std::vector< std::string >::iterator fni = fieldnames.begin(); + while(fni != fieldnames.end()) { + if(names != "") names += " OR "; + names += "name='" + W.esc(*fni) + "'"; + fni++; + } + + std::string macros; + if(macro) { + macros += " AND macro='" + macro->name + "'"; + if(macro->version != "") + macros += " AND t.version='" + macro->version + "'"; + } + + uncom = uncom; + query = "SELECT uid FROM commits WHERE patientid='"+patientid+"' AND" + " \"timestamp\">="+soldest.str()+" AND" + " (status='committed' OR uid="+sessionid+");"; + DEBUG(sql, "Query: %s\n", query.c_str()); + pqxx::result commits = W.exec(query); + pqxx::result::const_iterator ci = commits.begin(); + while(ci != commits.end()) { + std::string cid = (*ci)[0].c_str(); + + query = "SELECT uid, \"timestamp\" FROM transactions WHERE cid="+cid+ + macros+";"; + DEBUG(sql, "Query: %s\n", query.c_str()); + pqxx::result transactions = W.exec(query); + pqxx::result::const_iterator ti = transactions.begin(); + while(ti != transactions.end()) { + std::string tid = (*ti)[0].c_str(); + time_t timestamp = atol((*ti)[1].c_str()); + + query = "SELECT name, value FROM fields WHERE" + " transaction="+tid+" AND ("+ names +");"; + DEBUG(sql, "Query: %s\n", query.c_str()); + pqxx::result fields = W.exec(query); + DEBUG(sql, "Results: %lu\n", fields.size()); + pqxx::result::const_iterator fi = fields.begin(); + while(fi != fields.end()) { + std::string name = (*fi)[0].c_str(); + if(values.find(name) == values.end() || + values[name].timestamp <= timestamp) { + Value v; + v.value = (*fi)[1].c_str(); + v.timestamp = timestamp; + values[name] = v; + } + fi++; + } + + ti++; + } + + ci++; + } +#else/*NEW*/ std::string namecond; if(fieldnames.size() > 0) { @@ -318,6 +383,7 @@ Values PracroDAOPgsql::getLatestValues(std::string sessionid, values[(*ri)[0].c_str()] = v; ri++; } +#endif/*NEW*/ } catch (std::exception &e) { ERR_LOG(db, "Query failed: %s: %s\n", e.what(), query.c_str()); } diff --git a/server/src/resumeparser.cc b/server/src/resumeparser.cc deleted file mode 100644 index 1c5335a..0000000 --- a/server/src/resumeparser.cc +++ /dev/null @@ -1,81 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/*************************************************************************** - * resumeparser.cc - * - * Mon Oct 1 11:17:35 CEST 2007 - * Copyright 2007 Bent Bisballe Nyeng - * deva@aasimon.org - ****************************************************************************/ - -/* - * This file is part of Pracro. - * - * Pracro is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Pracro is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Pracro; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ -#include "resumeparser.h" - -#include <string.h> - -#include "luaresume.h" -#include "configuration.h" - -std::string resume_parser(Macro ¯o, Commit &commit) -{ - LUAResume luaresume(commit); - - std::string code; - - std::vector< Script >::iterator spi = macro.resume_scripts.begin(); - while(spi != macro.resume_scripts.end()) { - if(spi->attributes.find("src") != spi->attributes.end()) { - std::string src = spi->attributes["src"]; - std::string file = - Conf::xml_basedir + "/include/" + src; - FILE *fp = fopen(file.c_str(), "r"); - if(fp) { - char buf[64]; - size_t sz; - std::string inc; - while((sz = fread(buf, 1, sizeof(buf), fp)) != 0) { - inc.append(buf, sz); - } - fclose(fp); - code += "\n-- BEGIN INCLUDE: '" + src + "'\n"; - code += inc; - code += "\n-- END INCLUDE: '" + src + "'\n"; - } - } else { - code += spi->code; - } - spi++; - } - - return luaresume.run(code); -} - -#ifdef TEST_RESUMEPARSER -//deps: luaresume.cc configuration.cc debug.cc log.cc luautil.cc -//cflags: -I.. $(LUA_CFLAGS) -//libs: $(LUA_LIBS) -#include <test.h> - -TEST_BEGIN; - -// TODO: Put some testcode here (see test.h for usable macros). -TEST_TRUE(false, "No tests yet!"); - -TEST_END; - -#endif/*TEST_RESUMEPARSER*/ diff --git a/server/src/session.cc b/server/src/session.cc index fcd138a..234f004 100644 --- a/server/src/session.cc +++ b/server/src/session.cc @@ -121,12 +121,21 @@ void Session::setIdle(bool idle) } } -void Session::commit() +void Session::commit() throw(LUAScript::Exception, Journal::Exception) { DEBUG(session, "[%p] commit(sessionid: '%s')\n", this, sessionid.c_str()); if(_journal != NULL) { - _journal->commit(); + try { + _journal->runOnCommitScripts(); + } catch(LUAScript::Exception &e) { + throw e; + } + try { + _journal->commit(); + } catch(Journal::Exception &e) { + throw e; + } delete _journal; _journal = NULL; } diff --git a/server/src/session.h b/server/src/session.h index 4d1ed12..85382f5 100644 --- a/server/src/session.h +++ b/server/src/session.h @@ -37,6 +37,9 @@ #include "transaction.h" #include "template.h" +#include "luascript.h" +#include "journal.h" + class Environment; class Journal; @@ -52,7 +55,7 @@ public: void lock(); void unlock(); - void commit(); + void commit() throw(LUAScript::Exception, Journal::Exception); void nocommit(); void discard(); diff --git a/server/src/sessionparser.cc b/server/src/sessionparser.cc index 6b3653e..8913e3c 100644 --- a/server/src/sessionparser.cc +++ b/server/src/sessionparser.cc @@ -37,6 +37,9 @@ SessionParser::SessionParser() totalbytes = 0; inresume = false; indatabase = false; + invalue = false; + inscript = false; + inenv = false; } SessionParser::~SessionParser() @@ -49,6 +52,26 @@ void SessionParser::characterData(std::string &data) entries[entries.size()-1].resume += data; } + if(inscript) { + Entry &e = entries[entries.size() - 1]; + LUAOnCommit *oncommit = e.oncommit; + std::pair<std::string, std::string> &val = + oncommit->scripts[oncommit->scripts.size() - 1]; + val.first += data; + } + + if(invalue) { + Entry &e = entries[entries.size() - 1]; + LUAOnCommit *oncommit = e.oncommit; + oncommit->values[valuename] += data; + } + + if(inenv) { + Entry &e = entries[entries.size() - 1]; + LUAOnCommit *oncommit = e.oncommit; + oncommit->_env[envid] += data; + } + if(indatabase) { database += data; } @@ -80,12 +103,51 @@ void SessionParser::startTag(std::string name, attributes_t &attr) e.index = atoi(attr["index"].c_str()); e.macro = attr["macro"]; e.user = attr["user"]; + e.oncommit = NULL; entries.push_back(e); } if(name == "resume") { inresume = true; } + + if(name == "oncommit") { + Entry &e = entries[entries.size() - 1]; + if(e.oncommit != NULL) { + ERR(sessionparser, "Multiple oncommit tags in journal!\n"); + return; + } + e.oncommit = new LUAOnCommit(); + } + if(name == "envs") { } + + if(name == "env") { + if(attr["id"] == "ENV_PATIENTID") envid = LUAScript::ENV_PATIENTID; + else if(attr["id"] == "ENV_TEMPLATE") envid = LUAScript::ENV_TEMPLATE; + else if(attr["id"] == "ENV_MACRO") envid = LUAScript::ENV_MACRO; + else if(attr["id"] == "ENV_USER") envid = LUAScript::ENV_USER; + else { + // Unknown env id + return; + } + inenv = true; + } + + if(name == "values") { } + + if(name == "value") { + valuename = attr["name"]; + invalue = true; + } + + if(name == "scripts") {} + + if(name == "script") { + Entry &e = entries[entries.size() - 1]; + LUAOnCommit *oncommit = e.oncommit; + oncommit->addCode("", attr["name"]); + inscript = true; + } } void SessionParser::endTag(std::string name) @@ -96,6 +158,15 @@ void SessionParser::endTag(std::string name) if(name == "database") { indatabase = false; } + if(name == "env") { + inenv = false; + } + if(name == "value") { + invalue = false; + } + if(name == "script") { + inscript = false; + } } void SessionParser::parseError(const char *buf, size_t len, diff --git a/server/src/sessionparser.h b/server/src/sessionparser.h index df32b06..8734fbd 100644 --- a/server/src/sessionparser.h +++ b/server/src/sessionparser.h @@ -30,6 +30,8 @@ #include "saxparser.h" +#include "luaoncommit.h" + #include <string> #include <vector> @@ -50,10 +52,11 @@ public: std::string userid; std::string database; std::string dbtype; - + class Entry { public: int index; + LUAOnCommit *oncommit; std::string macro; std::string resume; std::string user; @@ -63,7 +66,12 @@ public: private: bool inresume; + bool inscript; + bool invalue; + bool inenv; + std::string valuename; bool indatabase; + LUAScript::env_t envid; }; #endif/*__PRACRO_SESSIONPARSER_H__*/ diff --git a/server/src/sessionserialiser.cc b/server/src/sessionserialiser.cc index 36d0a0d..386a115 100644 --- a/server/src/sessionserialiser.cc +++ b/server/src/sessionserialiser.cc @@ -88,7 +88,8 @@ Session *SessionSerialiser::loadStr(const std::string &xml) j->setPatientID(XDEC(parser.patientid)); std::vector<SessionParser::Entry>::iterator i = parser.entries.begin(); while(i != parser.entries.end()) { - j->addEntry(XDEC(i->resume), XDEC(i->macro), XDEC(i->user), i->index); + j->addEntry(XDEC(i->resume), XDEC(i->macro), XDEC(i->user), i->index, + i->oncommit); i++; } @@ -122,6 +123,50 @@ std::string SessionSerialiser::saveStr(Session *session) " macro=\"" + XENC(i->second.macro) + "\"" " user=\"" + XENC(i->second.user) + "\">\n"; xml += " <resume>" + XENC(i->second.resume) + "</resume>\n"; + LUAOnCommit *oncommit = i->second.oncommit; + if(oncommit != NULL) { + xml += " <oncommit>\n"; + + xml += " <envs>\n"; + std::map<LUAScript::env_t, std::string>::iterator ei = + oncommit->_env.begin(); + while(ei != oncommit->_env.end()) { + std::string id; + switch(ei->first) { + case LUAScript::ENV_PATIENTID: id = "ENV_PATIENTID"; break; + case LUAScript::ENV_TEMPLATE: id = "ENV_TEMPLATE"; break; + case LUAScript::ENV_MACRO: id = "ENV_MACRO"; break; + case LUAScript::ENV_USER: id = "ENV_USER"; break; + } + + xml += " <env id=\"" + XENC(id) + "\">"+ + XENC(ei->second) + "</env>\n"; + ei++; + } + xml += " </envs>\n"; + + xml += " <values>\n"; + std::map<std::string, std::string>::iterator vi = + oncommit->values.begin(); + while(vi != oncommit->values.end()) { + xml += " <value name=\"" + XENC(vi->first) + "\">"+ + XENC(vi->second) + "</value>\n"; + vi++; + } + xml += " </values>\n"; + + xml += " <scripts>\n"; + std::vector<std::pair<std::string, std::string> >::iterator si = + oncommit->scripts.begin(); + while(si != oncommit->scripts.end()) { + xml += " <script name=\"" + XENC(si->second) + "\">"+ + XENC(si->first) + "</script>\n"; + si++; + } + xml += " </scripts>\n"; + + xml += " </oncommit>\n"; + } xml += " </entry>\n"; i++; diff --git a/server/src/template.h b/server/src/template.h index e549ef3..853db3d 100644 --- a/server/src/template.h +++ b/server/src/template.h @@ -67,6 +67,7 @@ public: maps_t maps; std::vector< Script > scripts; std::vector< Script > resume_scripts; + std::vector< Script > commit_scripts; Widget widgets; Resume resume; diff --git a/server/src/templateparser.cc b/server/src/templateparser.cc index 704b215..8fc3eff 100644 --- a/server/src/templateparser.cc +++ b/server/src/templateparser.cc @@ -133,7 +133,7 @@ void TemplateParser::startTag(std::string name, attributes_t &attr) // Enable script parsing if(name == "scripts") { - if(state != MACRO) error("scripts found outside macro."); + if(state != TEMPLATE) error("scripts found outside template."); state = SCRIPTS; assert(t); // No template is currently available, cannot create maps! @@ -158,7 +158,7 @@ void TemplateParser::startTag(std::string name, attributes_t &attr) } break; default: - error("<script> tag found outside <scripts> or <resume> tags."); + error("<script> tag found outside <scripts> tag."); break; } return; diff --git a/server/src/transactionhandler.cc b/server/src/transactionhandler.cc index 7ec38f8..a52dd50 100644 --- a/server/src/transactionhandler.cc +++ b/server/src/transactionhandler.cc @@ -28,12 +28,13 @@ #include "transactionhandler.h" #include "macroparser.h" -#include "resumeparser.h" #include "templateparser.h" #include "templateheaderparser.h" #include "courseparser.h" #include "configuration.h" #include "luaquerymapper.h" +#include "luaresume.h" +#include "luaoncommit.h" #include "queryhandlerpentominos.h" #include "queryhandlerpracro.h" #include "xml_encode_decode.h" @@ -60,6 +61,7 @@ static std::string error_box(std::string message) static std::string handleCommits(Transaction &transaction, Environment &env, Session &session) + throw(LUAScript::Exception) { std::string answer; @@ -71,19 +73,32 @@ static std::string handleCommits(Transaction &transaction, Environment &env, mp.parse(); Macro *macro = mp.getMacro(); - std::string resume = resume_parser(*macro, commit); - commit.fields["journal.resume"] = resume; - session.commitMacro(transaction, commit, *macro); + std::string resume; + try { + LUAResume luaresume(transaction, commit); + luaresume.addScripts(macro->resume_scripts); + luaresume.run(); + resume = luaresume.resultString(); + commit.fields["journal.resume"] = resume; + session.commitMacro(transaction, commit, *macro); + } catch(LUAScript::Exception &e) { + throw e; + } - if(resume != "") { - + LUAOnCommit *oncommit = NULL; + if(macro->commit_scripts.size() != 0) { + oncommit = new LUAOnCommit(transaction, commit); + oncommit->addScripts(macro->commit_scripts); + } + + if(resume != "" || oncommit != NULL) { TemplateParser tp(env.templatelist.getLatestVersion(commit.templ)); tp.parse(); Template *templ = tp.getTemplate(); - - session.journal()->addEntry(transaction, commit, resume, templ); - } + session.journal()->addEntry(transaction, commit, resume, templ, oncommit); + } + i++; } @@ -92,6 +107,7 @@ static std::string handleCommits(Transaction &transaction, Environment &env, static std::string handleRequest(Request &request, Environment &env, Session &session) + throw(NotFoundException) { std::string answer; @@ -321,9 +337,9 @@ std::string handleTransaction(Request &request, try { answer += handleCommits(transaction, env, session); - } catch( std::exception &e ) { - ERR(server, "Commit error: %s\n", e.what()); - return error_box(xml_encode(e.what())); + } catch( LUAScript::Exception &e ) { + ERR(server, "Commit error: %s\n", e.msg.c_str()); + return error_box(xml_encode(e.msg)); } try { |