00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <config.h>
00023
00024 #include <unistd.h>
00025 #include <fcntl.h>
00026
00027 #include <drizzled/module/module.h>
00028 #include <drizzled/module/context.h>
00029 #include <drizzled/plugin/plugin.h>
00030 #include <drizzled/plugin.h>
00031 #include <drizzled/plugin/daemon.h>
00032 #include <drizzled/sys_var.h>
00033 #include <drizzled/gettext.h>
00034 #include <drizzled/error.h>
00035 #include <drizzled/session.h>
00036 #include <drizzled/internal/my_sys.h>
00037 #include <drizzled/internal/m_string.h>
00038 #include <algorithm>
00039 #include <iostream>
00040 #include <boost/program_options.hpp>
00041 #include <drizzled/module/option_map.h>
00042 #include <drizzled/constrained_value.h>
00043 #include <evhttp.h>
00044 #include <event.h>
00045 #include <drizzled/execute.h>
00046 #include <drizzled/sql/result_set.h>
00047
00048 #include <drizzled/plugin/listen.h>
00049 #include <drizzled/plugin/client.h>
00050 #include <drizzled/catalog/local.h>
00051
00052 #include <drizzled/pthread_globals.h>
00053 #include <boost/bind.hpp>
00054
00055
00056 #include <drizzled/version.h>
00057 #include <plugin/json_server/json/json.h>
00058
00059 namespace po= boost::program_options;
00060 using namespace drizzled;
00061 using namespace std;
00062
00063 namespace drizzle_plugin
00064 {
00065 namespace json_server
00066 {
00067
00068 static port_constraint port;
00069
00070 static in_port_t getPort(void)
00071 {
00072 return port.get();
00073 }
00074
00075 extern "C" void process_request(struct evhttp_request *req, void* );
00076 extern "C" void process_root_request(struct evhttp_request *req, void* );
00077 extern "C" void process_api01_version_req(struct evhttp_request *req, void* );
00078 extern "C" void process_api01_sql_req(struct evhttp_request *req, void* );
00079
00080 extern "C" void process_request(struct evhttp_request *req, void* )
00081 {
00082 struct evbuffer *buf = evbuffer_new();
00083 if (buf == NULL) return;
00084 evbuffer_add_printf(buf, "Requested: %s\n", evhttp_request_uri(req));
00085 evhttp_send_reply(req, HTTP_OK, "OK", buf);
00086 }
00087
00088 extern "C" void process_root_request(struct evhttp_request *req, void* )
00089 {
00090 struct evbuffer *buf = evbuffer_new();
00091 if (buf == NULL) return;
00092
00093 std::string output;
00094
00095 output.append("<html><head><title>JSON DATABASE interface demo</title></head>"
00096 "<body>"
00097 "<script lang=\"javascript\">"
00098 "function to_table(obj) {"
00099 " var str = '<table>';"
00100 "for (var r=0; r< obj.length; r++) {"
00101 " str+='<tr>';"
00102 " for (var c=0; c < obj[r].length; c++) {"
00103 " str+= '<td>' + obj[r][c] + '</td>';"
00104 " }"
00105 " str+='</tr>';"
00106 "}"
00107 "str+='</table>';"
00108 "return str;"
00109 "}"
00110 "function run_query()\n"
00111 "{"
00112 "var url = document.getElementById(\"baseurl\").innerHTML;\n"
00113 "var query= document.getElementById(\"query\").value;\n"
00114 "var xmlHttp = new XMLHttpRequest();\n"
00115 "xmlHttp.onreadystatechange = function () {\n"
00116 "if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {\n"
00117 "var info = eval ( \"(\" + xmlHttp.responseText + \")\" );\n"
00118 "document.getElementById( \"resultset\").innerHTML= to_table(info.result_set);\n"
00119 "}\n"
00120 "};\n"
00121 "xmlHttp.open(\"POST\", url + \"/0.1/sql\", true);"
00122 "xmlHttp.send(query);"
00123 "}"
00124 "\n\n"
00125 "function update_version()\n"
00126 "{drizzle_version(document.getElementById(\"baseurl\").innerHTML);}\n\n"
00127 "function drizzle_version($url)"
00128 "{"
00129 "var xmlHttp = new XMLHttpRequest();\n"
00130 "xmlHttp.onreadystatechange = function () {\n"
00131 "if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {\n"
00132 "var info = eval ( \"(\" + xmlHttp.responseText + \")\" );\n"
00133 "document.getElementById( \"drizzleversion\").innerHTML= info.version;\n"
00134 "}\n"
00135 "};\n"
00136 "xmlHttp.open(\"GET\", $url + \"/0.1/version\", true);"
00137 "xmlHttp.send(null);"
00138 "}"
00139 "</script>"
00140 "<p>Drizzle Server at: <a id=\"baseurl\">http://localhost:8765</a></p>"
00141 "<p>Drizzle server version: <a id=\"drizzleversion\"></a></p>"
00142 "<p><textarea rows=\"3\" cols=\"40\" id=\"query\">"
00143 "SELECT * from DATA_DICTIONARY.GLOBAL_STATUS;"
00144 "</textarea>"
00145 "<button type=\"button\" onclick=\"run_query();\">Execute Query</button>"
00146 "<div id=\"resultset\"/>"
00147 "<script lang=\"javascript\">update_version(); run_query();</script>"
00148 "</body></html>");
00149
00150 evbuffer_add(buf, output.c_str(), output.length());
00151 evhttp_send_reply(req, HTTP_OK, "OK", buf);
00152 }
00153
00154 extern "C" void process_api01_version_req(struct evhttp_request *req, void* )
00155 {
00156 struct evbuffer *buf = evbuffer_new();
00157 if (buf == NULL) return;
00158
00159 Json::Value root;
00160 root["version"]= ::drizzled::version();
00161
00162 Json::StyledWriter writer;
00163 std::string output= writer.write(root);
00164
00165 evbuffer_add(buf, output.c_str(), output.length());
00166 evhttp_send_reply(req, HTTP_OK, "OK", buf);
00167 }
00168
00169 extern "C" void process_api01_sql_req(struct evhttp_request *req, void* )
00170 {
00171 struct evbuffer *buf = evbuffer_new();
00172 if (buf == NULL) return;
00173
00174 std::string input;
00175 char buffer[1024];
00176 int l=0;
00177 do {
00178 l= evbuffer_remove(req->input_buffer, buffer, 1024);
00179 input.append(buffer, l);
00180 }while(l);
00181
00182 drizzled::Session::shared_ptr _session= drizzled::Session::make_shared(drizzled::plugin::Listen::getNullClient(),
00183 drizzled::catalog::local());
00184 drizzled::identifier::user::mptr user_id= identifier::User::make_shared();
00185 user_id->setUser("");
00186 _session->setUser(user_id);
00187 _session->set_schema("test");
00188
00189 drizzled::Execute execute(*(_session.get()), true);
00190
00191 drizzled::sql::ResultSet result_set(1);
00192
00193
00194 execute.run(input, result_set);
00195 drizzled::sql::Exception exception= result_set.getException();
00196
00197 drizzled::error_t err= exception.getErrorCode();
00198
00199 Json::Value root;
00200 root["sqlstate"]= exception.getSQLState();
00201
00202 if ((err != drizzled::EE_OK) && (err != drizzled::ER_EMPTY_QUERY))
00203 {
00204 root["error_message"]= exception.getErrorMessage();
00205 root["error_code"]= exception.getErrorCode();
00206 }
00207
00208 while (result_set.next())
00209 {
00210 Json::Value json_row;
00211 for (size_t x= 0; x < result_set.getMetaData().getColumnCount(); x++)
00212 {
00213 if (not result_set.isNull(x))
00214 {
00215 json_row[x]= result_set.getString(x);
00216 }
00217 }
00218 root["result_set"].append(json_row);
00219 }
00220
00221 root["query"]= input;
00222
00223 Json::StyledWriter writer;
00224 std::string output= writer.write(root);
00225
00226 evbuffer_add(buf, output.c_str(), output.length());
00227 evhttp_send_reply(req, HTTP_OK, "OK", buf);
00228 }
00229
00230 static void shutdown_event(int fd, short, void *arg)
00231 {
00232 struct event_base *base= (struct event_base *)arg;
00233 event_base_loopbreak(base);
00234 close(fd);
00235 }
00236
00237
00238 static void run(struct event_base *base)
00239 {
00240 internal::my_thread_init();
00241
00242 event_base_dispatch(base);
00243 }
00244
00245
00246 class JsonServer : public drizzled::plugin::Daemon
00247 {
00248 private:
00249 drizzled::thread_ptr json_thread;
00250 in_port_t _port;
00251 struct evhttp *httpd;
00252 struct event_base *base;
00253 int wakeup_fd[2];
00254 struct event wakeup_event;
00255
00256 public:
00257 JsonServer(in_port_t port_arg) :
00258 drizzled::plugin::Daemon("JSON Server"),
00259 _port(port_arg),
00260 httpd(NULL),
00261 base(NULL)
00262 { }
00263
00264 bool init()
00265 {
00266 if (pipe(wakeup_fd) < 0)
00267 {
00268 sql_perror("pipe");
00269 return false;
00270 }
00271
00272 int returned_flags;
00273 if ((returned_flags= fcntl(wakeup_fd[0], F_GETFL, 0)) < 0)
00274 {
00275 sql_perror("fcntl:F_GETFL");
00276 return false;
00277 }
00278
00279 if (fcntl(wakeup_fd[0], F_SETFL, returned_flags | O_NONBLOCK) < 0)
00280
00281 {
00282 sql_perror("F_SETFL");
00283 return false;
00284 }
00285
00286 if ((base= event_init()) == NULL)
00287 {
00288 sql_perror("event_init()");
00289 return false;
00290 }
00291
00292 if ((httpd= evhttp_new(base)) == NULL)
00293 {
00294 sql_perror("evhttp_new()");
00295 return false;
00296 }
00297
00298
00299 if ((evhttp_bind_socket(httpd, "0.0.0.0", getPort())) == -1)
00300 {
00301 sql_perror("evhttp_bind_socket()");
00302 return false;
00303 }
00304
00305 evhttp_set_cb(httpd, "/", process_root_request, NULL);
00306 evhttp_set_cb(httpd, "/0.1/version", process_api01_version_req, NULL);
00307 evhttp_set_cb(httpd, "/0.1/sql", process_api01_sql_req, NULL);
00308 evhttp_set_gencb(httpd, process_request, NULL);
00309
00310 event_set(&wakeup_event, wakeup_fd[0], EV_READ | EV_PERSIST, shutdown_event, base);
00311 event_base_set(base, &wakeup_event);
00312 if (event_add(&wakeup_event, NULL) < 0)
00313 {
00314 sql_perror("event_add");
00315 return false;
00316 }
00317
00318 json_thread.reset(new boost::thread((boost::bind(&run, base))));
00319
00320 if (not json_thread)
00321 return false;
00322
00323 return true;
00324 }
00325
00326 ~JsonServer()
00327 {
00328
00329 char buffer[1];
00330 buffer[0]= 4;
00331 if ((write(wakeup_fd[1], &buffer, 1)) == 1)
00332 {
00333 json_thread->join();
00334 evhttp_free(httpd);
00335 event_base_free(base);
00336 }
00337 }
00338 };
00339
00340 static int json_server_init(drizzled::module::Context &context)
00341 {
00342 context.registerVariable(new sys_var_constrained_value_readonly<in_port_t>("port", port));
00343
00344 JsonServer *server;
00345 context.add(server= new JsonServer(port));
00346
00347 if (server and not server->init())
00348 {
00349 return -2;
00350 }
00351
00352 return bool(server) ? 0 : 1;
00353 }
00354
00355 static void init_options(drizzled::module::option_context &context)
00356 {
00357 context("port",
00358 po::value<port_constraint>(&port)->default_value(8086),
00359 _("Port number to use for connection or 0 for default (port 8086) "));
00360 }
00361
00362 }
00363 }
00364
00365 DRIZZLE_DECLARE_PLUGIN
00366 {
00367 DRIZZLE_VERSION_ID,
00368 "json-server",
00369 "0.1",
00370 "Stewart Smith",
00371 "JSON HTTP interface",
00372 PLUGIN_LICENSE_GPL,
00373 drizzle_plugin::json_server::json_server_init,
00374 NULL,
00375 drizzle_plugin::json_server::init_options
00376 }
00377 DRIZZLE_DECLARE_PLUGIN_END;