summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrandomdan <randomdan@localhost>2011-10-17 19:29:44 +0000
committerrandomdan <randomdan@localhost>2011-10-17 19:29:44 +0000
commit0647f598f61c5434d5c35ad3e12b0ffdfe2c392c (patch)
treed02d1824d0efb520cda0564292500ea7c386ce0c
parentAdd some functional variables for date handling and string manipulation (diff)
downloadproject2-0647f598f61c5434d5c35ad3e12b0ffdfe2c392c.tar.bz2
project2-0647f598f61c5434d5c35ad3e12b0ffdfe2c392c.tar.xz
project2-0647f598f61c5434d5c35ad3e12b0ffdfe2c392c.zip
Adds the JSON and CouchDB module
-rw-r--r--project2/Jamfile.jam1
-rw-r--r--project2/json/Jamfile.jam26
-rw-r--r--project2/json/conversion.cpp24
-rw-r--r--project2/json/conversion.h28
-rw-r--r--project2/json/couchSession.cpp211
-rw-r--r--project2/json/json.h43
-rw-r--r--project2/json/parse.cpp222
-rw-r--r--project2/json/pch.hpp12
-rw-r--r--project2/json/serialize.cpp94
9 files changed, 661 insertions, 0 deletions
diff --git a/project2/Jamfile.jam b/project2/Jamfile.jam
index e0797a3..65a0e49 100644
--- a/project2/Jamfile.jam
+++ b/project2/Jamfile.jam
@@ -13,6 +13,7 @@ alias p2parts : : : :
<library>mail//p2mail
<library>regex//p2regex
<library>xml//p2xml
+ <library>json//p2json
;
project
diff --git a/project2/json/Jamfile.jam b/project2/json/Jamfile.jam
new file mode 100644
index 0000000..92a6e28
--- /dev/null
+++ b/project2/json/Jamfile.jam
@@ -0,0 +1,26 @@
+alias libxmlpp : : : :
+ <cflags>"`pkg-config --cflags libxml++-2.6`"
+ <linkflags>"`pkg-config --libs libxml++-2.6`" ;
+lib boost_filesystem : : <name>boost_filesystem ;
+lib boost_date_time : : <name>boost_date_time ;
+
+cpp-pch pch : pch.hpp :
+ <include>../../libmisc
+ <library>libxmlpp
+ <library>../common//p2common
+ ;
+lib p2json :
+ pch [ glob *.cpp ]
+ :
+ <include>../libmisc
+ <library>libxmlpp
+ <library>../common//p2common
+ <library>../uuid//p2uuid
+ <library>../url//p2url
+ <library>boost_filesystem
+ <library>boost_date_time
+ : :
+ <library>../uuid//p2uuid
+ <include>.
+ ;
+
diff --git a/project2/json/conversion.cpp b/project2/json/conversion.cpp
new file mode 100644
index 0000000..d13a324
--- /dev/null
+++ b/project2/json/conversion.cpp
@@ -0,0 +1,24 @@
+#include <pch.hpp>
+#include "conversion.h"
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+json::Value Project2ToJson::operator()(const boost::posix_time::ptime & i) const {
+ return boost::posix_time::to_iso_extended_string(i);
+}
+json::Value Project2ToJson::operator()(const Null & c) const {
+ return c;
+}
+json::Value Project2ToJson::operator()(const Glib::ustring & c) const {
+ return c;
+}
+
+VariableType JsonToProject2::operator()(const json::Boolean &) const {
+ throw ConversionNotSupported();
+}
+VariableType JsonToProject2::operator()(const json::Object &) const {
+ throw ConversionNotSupported();
+}
+VariableType JsonToProject2::operator()(const json::Array &) const {
+ throw ConversionNotSupported();
+}
+
diff --git a/project2/json/conversion.h b/project2/json/conversion.h
new file mode 100644
index 0000000..acd22fb
--- /dev/null
+++ b/project2/json/conversion.h
@@ -0,0 +1,28 @@
+#include "variables.h"
+#include "json.h"
+#include <boost/numeric/conversion/cast.hpp>
+
+class ConversionNotSupported { };
+class Project2ToJson : public boost::static_visitor<json::Value> {
+ public:
+ json::Value operator()(const boost::posix_time::ptime & i) const;
+ json::Value operator()(const Null & c) const;
+ json::Value operator()(const Glib::ustring & c) const;
+ template <class Numeric>
+ json::Value operator()(const Numeric & i) const {
+ return boost::numeric_cast<json::Number>(i);
+ }
+};
+
+class JsonToProject2 : public boost::static_visitor<VariableType> {
+ public:
+ VariableType operator()(const json::Boolean &) const;
+ VariableType operator()(const json::Object &) const;
+ VariableType operator()(const json::Array &) const;
+ template <class Copyable>
+ VariableType operator()(const Copyable & i) const {
+ return i;
+ }
+};
+
+
diff --git a/project2/json/couchSession.cpp b/project2/json/couchSession.cpp
new file mode 100644
index 0000000..d2eeca2
--- /dev/null
+++ b/project2/json/couchSession.cpp
@@ -0,0 +1,211 @@
+#include <pch.hpp>
+#include "curlHelper.h"
+#include "safeMapFind.h"
+#include "exceptions.h"
+#include "../libmisc/buffer.h"
+#include "../libmisc/curlsup.h"
+#include <xmlObjectLoader.h>
+#include <sessionContainer.h>
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+#include "json.h"
+#include "safeMapFind.h"
+#include "conversion.h"
+#include <boost/program_options.hpp>
+
+class CouchSession : public Session {
+ public:
+ CouchSession(const UUID & sid) : Session(sid) {
+ }
+ CouchSession(const UUID & sid, const json::Object & o) : Session(sid), obj(o) {
+ }
+ virtual VariableType GetValue(const Glib::ustring & name) const {
+ return boost::apply_visitor(JsonToProject2(), *safeMapFind<VariableNotFound>(obj, name)->second);
+ }
+ virtual Values GetValuesCopy() const {
+ Values vs;
+ BOOST_FOREACH(const json::Object::value_type & v, obj) {
+ vs.insert(Values::value_type(v.first, boost::apply_visitor(JsonToProject2(), *v.second)));
+ }
+ return vs;
+ }
+ virtual void SetValue(const Glib::ustring & name, const VariableType & v) {
+ obj[name] = json::ValuePtr(new json::Value(boost::apply_visitor(Project2ToJson(), v)));
+ }
+ virtual void ClearValue(const Glib::ustring & name) {
+ obj.erase(name);
+ }
+ virtual time_t ExpiryTime() const {
+ return boost::get<json::Number>(*safeMapFind<VariableNotFound>(obj, ExpiryKey)->second);
+ }
+ virtual void ExpiryTime(time_t t) {
+ obj[ExpiryKey] = json::ValuePtr(new json::Value(json::Number(t)));
+ }
+ private:
+ json::Object obj;
+ friend class CouchSessionContainer;
+ friend class CustomCouchSessionLoader;
+ static const Glib::ustring ExpiryKey;
+};
+const Glib::ustring CouchSession::ExpiryKey("project2:expires");
+
+class CouchDBFailure : public std::exception { };
+
+class CouchSessionContainer : public SessionContainer {
+ public:
+ CouchSessionContainer() {
+ }
+ virtual SessionPtr getSession(const UUID & uuid) const {
+ try {
+ SessionPtr s = new CouchSession(uuid, getSessionFromServer(uuid));
+ if (s->ExpiryTime() > time(NULL)) {
+ return s;
+ }
+ }
+ catch (...) {
+ }
+ return new CouchSession(UUID::generate_random());
+ }
+
+ virtual void SaveSession(SessionPtr s) const {
+ CurlPtr c = new Curl();
+ c->setopt(CURLOPT_UPLOAD, 1L);
+ c->setopt(CURLOPT_FAILONERROR, 1);
+ Glib::ustring out = json::serializeObject(boost::dynamic_pointer_cast<CouchSession>(s)->obj);
+ c->setopt(CURLOPT_INFILESIZE_LARGE, (curl_off_t)out.size());
+ unsigned int off = 0;
+ BOOST_FOREACH(const std::string & b, baseUrls) {
+ c->setopt(CURLOPT_URL, (b + s->ID.str()).c_str());
+ try {
+ c->performSend(boost::bind(send, &out, &off, _1, _2));
+ return;
+ }
+ catch (...) {
+ }
+ }
+ throw CouchDBFailure();
+ }
+
+ json::Object getSessionFromServer(const UUID & uuid) const {
+ CurlPtr c = new Curl();
+ c->setopt(CURLOPT_FAILONERROR, 1);
+ Glib::ustring msg;
+ BOOST_FOREACH(const std::string & b, baseUrls) {
+ try {
+ c->setopt(CURLOPT_URL, (b + uuid.str()).c_str());
+ c->performRead(boost::bind(append, &msg, _1, _2));
+ json::Object o = json::parseObject(msg);
+ return o;
+ }
+ catch (...) {
+ }
+ }
+ throw CouchDBFailure();
+ }
+
+ static size_t send(Glib::ustring * buf, unsigned int * off, char * str, size_t l) {
+ size_t len = std::min(buf->size() - *off, l);
+ memcpy(str, buf->c_str() + *off, len);
+ return len;
+ }
+
+ static size_t append(Glib::ustring * buf, const char * str, size_t l) {
+ buf->append(str, l);
+ return l;
+ }
+
+ void setopt_s(CurlHandle::Ptr c, CURLoption o, const char * v) {
+ c->setopt(o, v);
+ }
+
+ void setopt_l(CurlHandle::Ptr c, CURLoption o, int64_t v) {
+ c->setopt(o, (long)v);
+ }
+
+ static std::vector<std::string> baseUrls;
+};
+std::vector<std::string> CouchSessionContainer::baseUrls;
+
+namespace po = boost::program_options;
+class CustomCouchSessionLoader : public SessionContainerLoaderImpl<CouchSessionContainer> {
+ public:
+ CustomCouchSessionLoader() :
+ opts("Session CouchDB options")
+ {
+ opts.add_options()
+ ("session.couchdb.baseurl", po::value(&CouchSessionContainer::baseUrls)->composing(),
+ "Base URL to store sessions in")
+ ;
+ }
+ po::options_description *
+ options()
+ {
+ return &opts;
+ }
+
+ void onPeriodic() {
+ deleteSessions();
+ compactDB();
+ }
+
+ private:
+ static size_t discard(size_t l) {
+ return l;
+ }
+
+ void compactDB() {
+ CurlPtr c = new Curl();
+ c->setopt(CURLOPT_POST, 1);
+ c->appendHeader("Content-Type: application/json");
+ BOOST_FOREACH(const std::string & b, CouchSessionContainer::baseUrls) {
+ c->setopt(CURLOPT_URL, (b + "_compact").c_str());
+ c->performRead(boost::bind(discard, _2));
+ }
+ }
+ void deleteSessions() {
+ // Create the server side search map
+ json::Object map;
+ Buffer mapBuf;
+ mapBuf.appendf("function(doc) { var exp = doc['%s']; if (exp < (new Date().getTime() / 1000)) { emit(exp, doc._rev); } }",
+ CouchSession::ExpiryKey.c_str());
+ map["map"] = json::ValuePtr(new json::Value(mapBuf.str()));
+ Glib::ustring mapStr(json::serializeObject(map));
+ // Create the CURL handle
+ CurlPtr c = new Curl();
+ c->setopt(CURLOPT_FAILONERROR, 1);
+ c->appendHeader("Content-Type: application/json");
+ c->setopt(CURLOPT_POST, 1);
+ c->setopt(CURLOPT_POSTFIELDS, mapStr.c_str());
+ c->setopt(CURLOPT_POSTFIELDSIZE, mapStr.bytes());
+ BOOST_FOREACH(const std::string & b, CouchSessionContainer::baseUrls) {
+ Glib::ustring msg;
+ try {
+ c->setopt(CURLOPT_URL, (b + "_temp_view").c_str());
+ c->performRead(boost::bind(CouchSessionContainer::append, &msg, _1, _2));
+ json::Object o = json::parseObject(msg);
+ BOOST_FOREACH(const json::Array::value_type & v, boost::get<json::Array>(*safeMapFind<ParamNotFound>(o, "rows")->second)) {
+ json::Object rec = boost::get<json::Object>(*v);
+ UUID u = boost::get<json::String>(*safeMapFind<ParamNotFound>(rec, "id")->second).raw();
+ Glib::ustring & rev = boost::get<json::String>(*safeMapFind<ParamNotFound>(rec, "value")->second);
+ deleteSession(u, rev);
+ }
+ return;
+ }
+ catch (...) {
+ }
+ }
+ }
+
+ void deleteSession(const UUID & sid, const Glib::ustring & rev) const {
+ CurlPtr c = new Curl();
+ c->setopt(CURLOPT_CUSTOMREQUEST, "DELETE");
+ BOOST_FOREACH(const std::string & b, CouchSessionContainer::baseUrls) {
+ c->setopt(CURLOPT_URL, (b + sid.str() + "?rev=" + rev).c_str());
+ c->performRead(boost::bind(discard, _2));
+ return;
+ }
+ }
+ po::options_description opts;
+};
+DECLARE_CUSTOM_COMPONENT_LOADER("couchsession", CouchSessionContainer, CustomCouchSessionLoader, SessionContainerLoader);
+
diff --git a/project2/json/json.h b/project2/json/json.h
new file mode 100644
index 0000000..44ad9e4
--- /dev/null
+++ b/project2/json/json.h
@@ -0,0 +1,43 @@
+#ifndef JSON_H
+#define JSON_H
+
+#include <glibmm/ustring.h>
+#include <boost/shared_ptr.hpp>
+#include <variables.h>
+
+namespace json {
+ typedef Glib::ustring String;
+ typedef double Number;
+ typedef bool Boolean;
+ class Value;
+ typedef boost::shared_ptr<Value> ValuePtr;
+ typedef std::map<Glib::ustring, ValuePtr> Object;
+ typedef std::list<ValuePtr> Array;
+ typedef boost::variant<Null, String, Number, Object, Array, Boolean> VT;
+ class Value : public VT {
+ public:
+ template <class X>
+ Value(const X & x) : VT(x) { }
+ };
+
+ Object parseObject(Glib::ustring::const_iterator &);
+ Object parseObject(const Glib::ustring &);
+ String parseString(Glib::ustring::const_iterator & s);
+ Number parseNumber(Glib::ustring::const_iterator & s);
+ Boolean parseBoolean(Glib::ustring::const_iterator & s);
+ Null parseNull(Glib::ustring::const_iterator & s);
+ Value parseValue(Glib::ustring::const_iterator & s);
+ Array parseArray(Glib::ustring::const_iterator &);
+
+ void serializeObject(const Object &, Glib::ustring & s);
+ void serializeValue(const Value &, Glib::ustring & s);
+ void serializeArray(const Array &, Glib::ustring & s);
+ void serializeString(const String &, Glib::ustring & s);
+ void serializeNumber(const Number &, Glib::ustring & s);
+ void serializeBoolean(const Boolean &, Glib::ustring & s);
+ void serializeNull(const Null &, Glib::ustring & s);
+ Glib::ustring serializeObject(const Object &);
+}
+
+#endif
+
diff --git a/project2/json/parse.cpp b/project2/json/parse.cpp
new file mode 100644
index 0000000..66735cb
--- /dev/null
+++ b/project2/json/parse.cpp
@@ -0,0 +1,222 @@
+#include "json.h"
+#include <stdio.h>
+
+namespace json {
+ class ParseError { };
+ String parseString(Glib::ustring::const_iterator & s) {
+ while (Glib::Unicode::isspace(*s)) s++;
+ if (*s++ != '"') throw ParseError();
+ String str;
+ while (*s != '"') {
+ if (*s == '\\') {
+ switch (*s) {
+ case '"':
+ str += '"';
+ break;
+ case '\\':
+ str += '\\';
+ break;
+ case '/':
+ str += '/';
+ break;
+ case 'b':
+ str += gunichar(8);
+ break;
+ case 'f':
+ str += gunichar(12);
+ break;
+ case 'n':
+ str += gunichar(10);
+ break;
+ case 'r':
+ str += gunichar(13);
+ break;
+ case 't':
+ str += gunichar(9);
+ break;
+ case 'u':
+ {
+ unsigned int c = 0;
+ for (int n = 0; n < 4; n += 1) {
+ c *= 16;
+ if (*s >= '0' && *s <= '9') {
+ c += (*s - '0');
+ }
+ else if (*s >= 'a' && *s <= 'f') {
+ c += (*s - 'a');
+ }
+ else if (*s >= 'A' && *s <= 'F') {
+ c += (*s - 'A');
+ }
+ else {
+ throw ParseError();
+ }
+ s++;
+ }
+ s--;
+ }
+ break;
+ default:
+ throw ParseError();
+ }
+ }
+ else {
+ str += *s;
+ }
+ s++;
+ }
+ if (*s++ != '"') throw ParseError();
+ return str;
+ }
+ Number parseNumber(Glib::ustring::const_iterator & s) {
+ while (Glib::Unicode::isspace(*s)) s++;
+ bool neg = false;
+ double v = 0;
+ if (*s == '-') {
+ neg = true;
+ s++;
+ }
+ bool dot = false, e = false;
+ double frac = 1;
+ unsigned int digits = 0;
+ while (true) {
+ if (Glib::Unicode::isdigit(*s)) {
+ if (dot) {
+ frac /= 10;
+ v += (frac * (*s - '0'));
+ }
+ else {
+ v *= 10;
+ v += (*s - '0');
+ digits += 1;
+ }
+ }
+ else if (*s == '.') {
+ if (dot || e) throw ParseError();
+ dot = true;
+ }
+ else if (*s == 'e' || *s == 'E') {
+ e = true;
+ s++;
+ bool eneg = false;
+ if (*s == '+') {
+ }
+ else if (*s == '-') {
+ eneg = true;
+ s++;
+ }
+ int ev = 0;
+ while (Glib::Unicode::isdigit(*s)) {
+ ev *= 10;
+ ev += (*s - '0');
+ s++;
+ }
+ while (ev--) {
+ if (eneg) {
+ v /= 10;
+ }
+ else {
+ v *= 10;
+ }
+ }
+ break;
+ }
+ else {
+ break;
+ }
+ s++;
+ }
+ if (digits < 1) throw ParseError();
+ return neg ? -v : v;
+ }
+ Value parseValue(Glib::ustring::const_iterator & s) {
+ while (Glib::Unicode::isspace(*s)) s++;
+ switch (*s) {
+ case '"':
+ return parseString(s);
+ case '{':
+ return parseObject(s);
+ case '[':
+ return parseArray(s);
+ case 'n':
+ return parseNull(s);
+ case 't':
+ case 'f':
+ return parseBoolean(s);
+ default:
+ return parseNumber(s);
+ }
+ }
+ Object parseObject(const Glib::ustring & s) {
+ Glib::ustring::const_iterator i = s.begin();
+ Object o = parseObject(i);
+ if (i != s.end()) throw ParseError();
+ return o;
+ }
+ Object parseObject(Glib::ustring::const_iterator & s) {
+ Object o;
+ while (Glib::Unicode::isspace(*s)) s++;
+ if (*s != '{') throw ParseError();
+ do {
+ s++;
+ while (Glib::Unicode::isspace(*s)) s++;
+ if (*s == '}') return o;
+ String key = parseString(s);
+ while (Glib::Unicode::isspace(*s)) s++;
+ if (*s++ != ':') throw ParseError();
+ if (!o.insert(Object::value_type(key, ValuePtr(new Value(parseValue(s))))).second) throw ParseError();
+ while (Glib::Unicode::isspace(*s)) s++;
+ } while (*s == ',');
+ if (*s == '}') {
+ s++;
+ while (Glib::Unicode::isspace(*s)) s++;
+ return o;
+ }
+ throw ParseError();
+ }
+ Array parseArray(Glib::ustring::const_iterator & s) {
+ Array a;
+ while (Glib::Unicode::isspace(*s)) s++;
+ if (*s != '[') throw ParseError();
+ do {
+ s++;
+ while (Glib::Unicode::isspace(*s)) s++;
+ if (*s == ']') {
+ s++;
+ return a;
+ }
+ a.push_back(ValuePtr(new Value(parseValue(s))));
+ while (Glib::Unicode::isspace(*s)) s++;
+ } while (*s == ',');
+ if (*s == ']') {
+ s++;
+ return a;
+ }
+ throw ParseError();
+ }
+ Null parseNull(Glib::ustring::const_iterator & s) {
+ if (*s++ != 'n') throw ParseError();
+ if (*s++ != 'u') throw ParseError();
+ if (*s++ != 'l') throw ParseError();
+ if (*s++ != 'l') throw ParseError();
+ return Null();
+ }
+ Boolean parseBoolean(Glib::ustring::const_iterator & s) {
+ if (*s == 't') {
+ s++;
+ if (*s++ != 'r') throw ParseError();
+ if (*s++ != 'u') throw ParseError();
+ if (*s++ != 'e') throw ParseError();
+ return true;
+ }
+ else if (*s == 'f') {
+ s++;
+ if (*s++ != 'a') throw ParseError();
+ if (*s++ != 'l') throw ParseError();
+ if (*s++ != 's') throw ParseError();
+ if (*s++ != 'e') throw ParseError();
+ return false;
+ }
+ throw ParseError();
+ }
+}
diff --git a/project2/json/pch.hpp b/project2/json/pch.hpp
new file mode 100644
index 0000000..173770f
--- /dev/null
+++ b/project2/json/pch.hpp
@@ -0,0 +1,12 @@
+#ifdef BOOST_BUILD_PCH_ENABLED
+#ifndef JSON_PCH
+#define JSON_PCH
+
+#include <boost/variant.hpp>
+#include <glibmm/ustring.h>
+#include <variables.h>
+#include <map>
+
+#endif
+#endif
+
diff --git a/project2/json/serialize.cpp b/project2/json/serialize.cpp
new file mode 100644
index 0000000..f3245e9
--- /dev/null
+++ b/project2/json/serialize.cpp
@@ -0,0 +1,94 @@
+#include "json.h"
+#include <stdio.h>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+
+namespace json {
+ class JsonSerialize : public boost::static_visitor<> {
+ public:
+ JsonSerialize(Glib::ustring & out) : o(out) {
+ }
+ void operator()(const String & s) const {
+ serializeString(s, o);
+ }
+ void operator()(const Number & s) const {
+ serializeNumber(s, o);
+ }
+ void operator()(const Array & s) const {
+ serializeArray(s, o);
+ }
+ void operator()(const Object & s) const {
+ serializeObject(s, o);
+ }
+ void operator()(const Null & s) const {
+ serializeNull(s, o);
+ }
+ void operator()(const Boolean & s) const {
+ serializeBoolean(s, o);
+ }
+ private:
+ Glib::ustring & o;
+ };
+ void serializeObject(const Object & o, Glib::ustring & s) {
+ s += '{';
+ BOOST_FOREACH(const Object::value_type & v, o) {
+ if (&v != &*o.begin()) {
+ s += ',';
+ }
+ serializeString(v.first, s);
+ s += ':';
+ serializeValue(*v.second, s);
+ }
+ s += '}';
+ }
+ void serializeValue(const Value & v, Glib::ustring & s) {
+ boost::apply_visitor(JsonSerialize(s), v);
+ }
+ void serializeArray(const Array & a, Glib::ustring & s) {
+ s += '[';
+ BOOST_FOREACH(const Array::value_type & v, a) {
+ if (&v != &*a.begin()) {
+ s += ',';
+ }
+ serializeValue(v, s);
+ }
+ s += ']';
+ }
+ void serializeString(const String & str, Glib::ustring & s) {
+ s += '"';
+ BOOST_FOREACH(gunichar c, str) {
+ switch (c) {
+ case '\\':
+ s += "\\\\";
+ break;
+ case '"':
+ s += "\\\"";
+ break;
+ default:
+ s += c;
+ break;
+ }
+ }
+ s += '"';
+ }
+ void serializeNumber(const Number & n, Glib::ustring & s) {
+ s += boost::lexical_cast<Glib::ustring>(n);
+ }
+ void serializeBoolean(const Boolean & b, Glib::ustring & s) {
+ if (b) {
+ s += "true";
+ }
+ else {
+ s += "false";
+ }
+ }
+ void serializeNull(const Null &, Glib::ustring & s) {
+ s += "null";
+ }
+ Glib::ustring serializeObject(const Object & o) {
+ Glib::ustring out;
+ serializeObject(o, out);
+ return out;
+ }
+}
+