29 #include <drizzled/module/module.h>
30 #include <drizzled/module/context.h>
31 #include <drizzled/plugin/plugin.h>
32 #include <drizzled/plugin.h>
33 #include <drizzled/plugin/daemon.h>
34 #include <drizzled/sys_var.h>
35 #include <drizzled/gettext.h>
36 #include <drizzled/error.h>
37 #include <drizzled/session.h>
38 #include <drizzled/internal/my_sys.h>
39 #include <drizzled/internal/m_string.h>
42 #include <boost/program_options.hpp>
44 #include <drizzled/constrained_value.h>
47 #include <drizzled/execute.h>
48 #include <drizzled/sql/result_set.h>
50 #include <drizzled/plugin/listen.h>
51 #include <drizzled/plugin/client.h>
52 #include <drizzled/catalog/local.h>
54 #include <drizzled/pthread_globals.h>
55 #include <boost/bind.hpp>
58 #include <drizzled/version.h>
59 #include <plugin/json_server/json/json.h>
60 #include <plugin/json_server/db_access.h>
61 #include <plugin/json_server/http_handler.h>
62 #include <plugin/json_server/http_server.h>
64 namespace po= boost::program_options;
65 using namespace drizzled;
68 namespace drizzle_plugin
72 static const string DEFAULT_SCHEMA =
"test";
73 static const string DEFAULT_TABLE =
"";
74 static const string JSON_SERVER_VERSION =
"0.3";
75 static const uint32_t DEFAULT_MAX_THREADS= 32;
76 static const bool DEFAULT_ALLOW_DROP_TABLE=
false;
77 bool allow_drop_table;
78 string default_schema;
81 uint32_t clone_max_threads=0;
84 void updateMaxThreads(
Session *, sql_var_t);
87 static in_port_t getPort(
void)
92 extern "C" void process_request(
struct evhttp_request *req,
void* );
93 extern "C" void process_root_request(
struct evhttp_request *req,
void* );
94 extern "C" void process_api01_version_req(
struct evhttp_request *req,
void* );
95 extern "C" void process_version_req(
struct evhttp_request *req,
void* );
96 extern "C" void process_sql_req(
struct evhttp_request *req,
void* );
98 extern "C" void process_request(
struct evhttp_request *req,
void* )
100 struct evbuffer *buf = evbuffer_new();
101 if (buf == NULL)
return;
102 evbuffer_add_printf(buf,
"Requested: %s\n", evhttp_request_uri(req));
103 evhttp_send_reply(req, HTTP_OK,
"OK", buf);
106 extern "C" void process_root_request(
struct evhttp_request *req,
void* )
108 struct evbuffer *buf = evbuffer_new();
109 if (buf == NULL)
return;
113 output.append(
"<html><head><title>JSON DATABASE interface demo</title></head>\n"
115 "<script lang=\"javascript\">\n"
116 "function to_table(obj) {\n"
117 " var str = '<table border=\"1\">';\n"
118 "for (var r=0; r< obj.length; r++) {\n"
120 " for (var c=0; c < obj[r].length; c++) {\n"
121 " str+= '<td>' + obj[r][c] + '</td>';\n"
128 "function to_table_from_json(obj) {\n"
129 " var str = '<table border=\"1\">';\n"
130 "for (var r=0; r< obj.length; r++) {\n"
132 " str+='<td>' + obj[r]['_id'] + '</td>';\n"
133 " str+='<td>' + JSON.stringify(obj[r]['document']) + '</td>';\n"
139 "function run_sql_query()\n"
141 "var url = window.location;\n"
142 "var query= document.getElementById(\"sql_query\").value;\n"
143 "var xmlHttp = new XMLHttpRequest();\n"
144 "xmlHttp.onreadystatechange = function () {\n"
145 "document.getElementById(\"responseText\").value = xmlHttp.responseText;\n"
146 "if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {\n"
147 "var info = eval ( \"(\" + xmlHttp.responseText + \")\" );\n"
148 "document.getElementById( \"resultset\").innerHTML= to_table(info.result_set);\n"
151 "xmlHttp.open(\"POST\", url + \"sql\", true);\n"
152 "xmlHttp.send(query);\n"
155 "function run_json_query()\n"
158 "var url = window.location;\n"
159 "var method= document.getElementById(\"json_method\").value;\n"
160 "var query= document.getElementById(\"json_query\").value;\n"
161 "var schema= document.getElementById(\"schema\").value;\n"
162 "var table= document.getElementById(\"table\").value;\n"
163 "var xmlHttp = new XMLHttpRequest();\n"
164 "xmlHttp.onreadystatechange = function () {\n"
166 "document.getElementById(\"responseText\").value = xmlHttp.responseText;\n"
167 "if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {\n"
168 "var info = eval ( \"(\" + xmlHttp.responseText + \")\" );\n"
169 "document.getElementById( \"resultset\").innerHTML= to_table_from_json(info.result_set);\n"
172 "if( method == \"POST\" ) {\n"
173 "xmlHttp.open(method, url + \"json?schema=\" + schema + \"&table=\" + table, true);\n"
174 "xmlHttp.send(query);\n"
176 "xmlHttp.open(method, url + \"json?schema=\" + schema + \"&table=\" + table + \"&query=\" + encodeURIComponent(query), true);\n"
181 "function update_version()\n"
182 "{drizzle_version(window.location);}\n\n"
183 "function drizzle_version($url)\n"
185 "var xmlHttp = new XMLHttpRequest();\n"
186 "xmlHttp.onreadystatechange = function () {\n"
187 "if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {\n"
188 "var info = eval ( \"(\" + xmlHttp.responseText + \")\" );\n"
189 "document.getElementById( \"drizzleversion\").innerHTML= info.version;\n"
192 "xmlHttp.open(\"GET\", $url + \"version\", true);\n"
193 "xmlHttp.send(null);\n"
196 "<p>Drizzle server version: <a id=\"drizzleversion\"></a></p>\n"
197 "<p><textarea rows=\"3\" cols=\"80\" id=\"sql_query\">\n"
198 "SELECT * from DATA_DICTIONARY.GLOBAL_STATUS;\n"
200 "<button type=\"button\" onclick=\"run_sql_query();\">Execute SQL Query</button>\n"
202 "<textarea rows=\"8\" cols=\"80\" id=\"json_query\">\n"
205 "<button type=\"button\" onclick=\"run_json_query();\">Execute JSON Query</button>\n"
207 "<select id=\"json_method\"><option value=\"GET\">GET</option>"
208 "<option value=\"POST\">POST</option>"
209 "<option value=\"PUT\">PUT</option>"
210 "<option value=\"DELETE\">DELETE</option></select>"
211 "<script lang=\"javascript\">document.write(window.location);</script>json?schema=\n"
212 "<input type=\"text\" id=\"schema\" value=\"test\"/>"
213 "&table=<input type=\"text\" id=\"table\" value=\"jsonkv\"/>\n"
214 "</p><hr />\n<div id=\"resultset\"></div>\n"
215 "<hr /><p><textarea rows=\"12\" cols=\"80\" id=\"responseText\" ></textarea></p>"
216 "<script lang=\"javascript\">update_version(); run_sql_query();</script>\n"
219 evbuffer_add(buf, output.c_str(), output.length());
220 evhttp_send_reply(req, HTTP_OK,
"OK", buf);
223 extern "C" void process_api01_version_req(
struct evhttp_request *req,
void* )
225 struct evbuffer *buf = evbuffer_new();
226 if (buf == NULL)
return;
229 root[
"version"]= ::drizzled::version();
232 std::string output= writer.
write(root);
234 evbuffer_add(buf, output.c_str(), output.length());
235 evhttp_send_reply(req, HTTP_OK,
"OK", buf);
238 extern "C" void process_version_req(
struct evhttp_request *req,
void* )
240 struct evbuffer *buf = evbuffer_new();
241 if (buf == NULL)
return;
244 root[
"version"]= ::drizzled::version();
245 root[
"json_server_version"]=JSON_SERVER_VERSION;
248 std::string output= writer.
write(root);
250 evbuffer_add(buf, output.c_str(), output.length());
251 evhttp_send_reply(req, HTTP_OK,
"OK", buf);
254 extern "C" void process_sql_req(
struct evhttp_request *req,
void* )
256 struct evbuffer *buf = evbuffer_new();
257 if (buf == NULL)
return;
263 l= evbuffer_remove(req->input_buffer, buffer, 1024);
264 input.append(buffer, l);
268 drizzled::catalog::local());
269 drizzled::identifier::user::mptr user_id= identifier::User::make_shared();
270 user_id->setUser(
"");
271 _session->setUser(user_id);
272 _session->set_schema(
"test");
279 execute.run(input, result_set);
282 drizzled::error_t err= exception.getErrorCode();
285 root[
"sqlstate"]= exception.getSQLState();
287 if ((err != drizzled::EE_OK) && (err != drizzled::ER_EMPTY_QUERY))
289 root[
"error_message"]= exception.getErrorMessage();
290 root[
"error_code"]= exception.getErrorCode();
291 root[
"schema"]=
"test";
294 while (result_set.next())
297 for (
size_t x= 0; x < result_set.getMetaData().getColumnCount(); x++)
299 if (not result_set.isNull(x))
301 json_row[x]= result_set.getString(x);
304 root[
"result_set"].
append(json_row);
307 root[
"query"]= input;
310 std::string output= writer.
write(root);
312 evbuffer_add(buf, output.c_str(), output.length());
313 evhttp_send_reply(req, HTTP_OK,
"OK", buf);
335 if(!handler->
validate(default_schema,default_table,allow_drop_table))
360 static void shutdown_event(
int fd,
short,
void *arg)
362 struct event_base *base= (
struct event_base *)arg;
363 event_base_loopbreak(base);
367 static void run(
struct event_base *base)
369 internal::my_thread_init();
371 event_base_dispatch(base);
378 std::vector<drizzled::thread_ptr> json_threads;
380 struct evhttp *httpd;
381 struct event_base *base;
383 struct event wakeup_event;
396 if (pipe(wakeup_fd) < 0)
403 if ((returned_flags= fcntl(wakeup_fd[0], F_GETFL, 0)) < 0)
405 sql_perror(
"fcntl:F_GETFL");
409 if (fcntl(wakeup_fd[0], F_SETFL, returned_flags | O_NONBLOCK) < 0)
412 sql_perror(
"F_SETFL");
415 if ((nfd=BindSocket(
"0.0.0.0", getPort())) == -1)
417 sql_perror(
"evhttp_bind_socket()");
422 if(not createThreads(max_threads))
430 bool createThreads(uint32_t num_threads)
432 for(uint32_t i =0;i<num_threads;i++)
434 if ((base= event_init()) == NULL)
436 sql_perror(
"event_init()");
440 if ((httpd= evhttp_new(base)) == NULL)
442 sql_perror(
"evhttp_new()");
446 if(evhttp_accept_socket(httpd,nfd))
448 sql_perror(
"evhttp_accept_socket()");
453 evhttp_set_cb(httpd,
"/", process_root_request, NULL);
455 evhttp_set_cb(httpd,
"/0.1/version", process_api01_version_req, NULL);
457 evhttp_set_cb(httpd,
"/0.2/version", process_api01_version_req, NULL);
459 evhttp_set_cb(httpd,
"/0.3/version", process_version_req, NULL);
460 evhttp_set_cb(httpd,
"/0.3/sql", process_sql_req, NULL);
463 evhttp_set_cb(httpd,
"/latest/version", process_version_req, NULL);
464 evhttp_set_cb(httpd,
"/latest/sql", process_sql_req, NULL);
466 evhttp_set_cb(httpd,
"/version", process_version_req, NULL);
467 evhttp_set_cb(httpd,
"/sql", process_sql_req, NULL);
471 event_set(&wakeup_event, wakeup_fd[0], EV_READ | EV_PERSIST, shutdown_event, base);
472 event_base_set(base, &wakeup_event);
473 if (event_add(&wakeup_event, NULL) < 0)
475 sql_perror(
"event_add");
478 drizzled::thread_ptr local_thread;
479 local_thread.reset(
new boost::thread((boost::bind(&run, base))));
480 json_threads.push_back(local_thread);
482 if (not json_threads[i])
493 if ((write(wakeup_fd[1], &buffer, 1)) == 1)
495 for(uint32_t i=0;i<max_threads;i++)
497 json_threads[i]->join();
500 event_base_free(base);
506 void updateMaxThreads(
Session *, sql_var_t)
508 if (clone_max_threads < max_threads)
510 if(server->createThreads(max_threads - clone_max_threads))
512 clone_max_threads=max_threads;
519 errmsg_printf(error::ERROR,_(
"json_server unable to create more threads"));
524 max_threads = clone_max_threads;
526 errmsg_printf(error::ERROR, _(
"json_server_max_threadscannot be smaller than previous configured value"));
535 std::string new_schema(var->value->
str_value.data());
536 default_schema=new_schema;
539 errmsg_printf(error::ERROR, _(
"json_server_schema cannot be NULL"));
545 std::string new_table(var->value->
str_value.data());
546 default_table=new_table;
553 server =
new JsonServer(port);
556 context.registerVariable(
new sys_var_std_string(
"schema", default_schema, NULL, &updateSchema));
557 context.registerVariable(
new sys_var_std_string(
"table", default_table, NULL, &updateTable));
558 context.registerVariable(
new sys_var_bool_ptr(
"allow_drop_table", &allow_drop_table));
559 context.registerVariable(
new sys_var_uint32_t_ptr(
"max_threads",&max_threads,&updateMaxThreads));
561 clone_max_threads=max_threads;
564 if (server and not server->init())
569 return bool(server) ? 0 : 1;
575 po::value<port_constraint>(&port)->default_value(8086),
576 _(
"Port number to use for connection or 0 for default (port 8086) "));
578 po::value<string>(&default_schema)->default_value(DEFAULT_SCHEMA),
579 _(
"Schema in use by json server"));
581 po::value<string>(&default_table)->default_value(DEFAULT_TABLE),
582 _(
"table in use by json server"));
583 context(
"allow_drop_table",
584 po::value<bool>(&allow_drop_table)->default_value(DEFAULT_ALLOW_DROP_TABLE),
585 _(
"allow to drop table"));
586 context(
"max_threads",
587 po::value<uint32_t>(&max_threads)->default_value(DEFAULT_MAX_THREADS),
588 _(
"Maximum threads in use by json server"));
595 DRIZZLE_DECLARE_PLUGIN
600 "Stewart Smith, Henrik Ingo, Mohit Srivastava",
601 N_(
"JSON HTTP interface"),
603 drizzle_plugin::json_server::json_server_init,
605 drizzle_plugin::json_server::init_options
607 DRIZZLE_DECLARE_PLUGIN_END;
const Json::Value getOutputJson() const
const Json::Value getInputJson() const
bool validate(string &default_schema, string &default_table, bool allow_drop_table)
virtual std::string write(const Value &root)
Serialize a Value in JSON format.
An Proxy Wrapper around boost::program_options::variables_map.
const Json::Value getOutputJson() const
void process_json_req(struct evhttp_request *req, void *)
void setOutputJson(Json::Value &json_out)
const char * getTable() const
static plugin::Client * getNullClient()
Defines the event_t struct that encapsulates an event.
const char * getSchema() const
Writes a Value in JSON format in a human friendly way.
Value & append(const Value &value)
Append value to array at the end.