Drizzled Public API Documentation

json_server.cc
1 /* - mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3  *
4  * Copyright (C) 2011 Stewart Smith
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 
22 #include <config.h>
23 
24 #include <unistd.h>
25 #include <fcntl.h>
26 
27 #include <drizzled/module/module.h>
28 #include <drizzled/module/context.h>
29 #include <drizzled/plugin/plugin.h>
30 #include <drizzled/plugin.h>
31 #include <drizzled/plugin/daemon.h>
32 #include <drizzled/sys_var.h>
33 #include <drizzled/gettext.h>
34 #include <drizzled/error.h>
35 #include <drizzled/session.h>
36 #include <drizzled/internal/my_sys.h>
37 #include <drizzled/internal/m_string.h>
38 #include <algorithm>
39 #include <iostream>
40 #include <boost/program_options.hpp>
42 #include <drizzled/constrained_value.h>
43 #include <evhttp.h>
44 #include <event.h>
45 #include <drizzled/execute.h>
46 #include <drizzled/sql/result_set.h>
47 
48 #include <drizzled/plugin/listen.h>
49 #include <drizzled/plugin/client.h>
50 #include <drizzled/catalog/local.h>
51 
52 #include <drizzled/pthread_globals.h>
53 #include <boost/bind.hpp>
54 
55 
56 #include <drizzled/version.h>
57 #include <plugin/json_server/json/json.h>
58 
59 namespace po= boost::program_options;
60 using namespace drizzled;
61 using namespace std;
62 
63 namespace drizzle_plugin
64 {
65 namespace json_server
66 {
67 
68 static port_constraint port;
69 
70 static in_port_t getPort(void)
71 {
72  return port.get();
73 }
74 
75 extern "C" void process_request(struct evhttp_request *req, void* );
76 extern "C" void process_root_request(struct evhttp_request *req, void* );
77 extern "C" void process_api01_version_req(struct evhttp_request *req, void* );
78 extern "C" void process_api01_sql_req(struct evhttp_request *req, void* );
79 
80 extern "C" void process_request(struct evhttp_request *req, void* )
81 {
82  struct evbuffer *buf = evbuffer_new();
83  if (buf == NULL) return;
84  evbuffer_add_printf(buf, "Requested: %s\n", evhttp_request_uri(req));
85  evhttp_send_reply(req, HTTP_OK, "OK", buf);
86 }
87 
88 extern "C" void process_root_request(struct evhttp_request *req, void* )
89 {
90  struct evbuffer *buf = evbuffer_new();
91  if (buf == NULL) return;
92 
93  std::string output;
94 
95  output.append("<html><head><title>JSON DATABASE interface demo</title></head>"
96  "<body>"
97  "<script lang=\"javascript\">"
98  "function to_table(obj) {"
99  " var str = '<table>';"
100  "for (var r=0; r< obj.length; r++) {"
101  " str+='<tr>';"
102  " for (var c=0; c < obj[r].length; c++) {"
103  " str+= '<td>' + obj[r][c] + '</td>';"
104  " }"
105  " str+='</tr>';"
106  "}"
107  "str+='</table>';"
108  "return str;"
109  "}"
110  "function run_query()\n"
111  "{"
112  "var url = document.getElementById(\"baseurl\").innerHTML;\n"
113  "var query= document.getElementById(\"query\").value;\n"
114  "var xmlHttp = new XMLHttpRequest();\n"
115  "xmlHttp.onreadystatechange = function () {\n"
116  "if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {\n"
117  "var info = eval ( \"(\" + xmlHttp.responseText + \")\" );\n"
118  "document.getElementById( \"resultset\").innerHTML= to_table(info.result_set);\n"
119  "}\n"
120  "};\n"
121  "xmlHttp.open(\"POST\", url + \"/0.1/sql\", true);"
122  "xmlHttp.send(query);"
123  "}"
124  "\n\n"
125  "function update_version()\n"
126  "{drizzle_version(document.getElementById(\"baseurl\").innerHTML);}\n\n"
127  "function drizzle_version($url)"
128  "{"
129  "var xmlHttp = new XMLHttpRequest();\n"
130  "xmlHttp.onreadystatechange = function () {\n"
131  "if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {\n"
132  "var info = eval ( \"(\" + xmlHttp.responseText + \")\" );\n"
133  "document.getElementById( \"drizzleversion\").innerHTML= info.version;\n"
134  "}\n"
135  "};\n"
136  "xmlHttp.open(\"GET\", $url + \"/0.1/version\", true);"
137  "xmlHttp.send(null);"
138  "}"
139  "</script>"
140  "<p>Drizzle Server at: <a id=\"baseurl\">http://localhost:8765</a></p>"
141  "<p>Drizzle server version: <a id=\"drizzleversion\"></a></p>"
142  "<p><textarea rows=\"3\" cols=\"40\" id=\"query\">"
143  "SELECT * from DATA_DICTIONARY.GLOBAL_STATUS;"
144  "</textarea>"
145  "<button type=\"button\" onclick=\"run_query();\">Execute Query</button>"
146  "<div id=\"resultset\"/>"
147  "<script lang=\"javascript\">update_version(); run_query();</script>"
148  "</body></html>");
149 
150  evbuffer_add(buf, output.c_str(), output.length());
151  evhttp_send_reply(req, HTTP_OK, "OK", buf);
152 }
153 
154 extern "C" void process_api01_version_req(struct evhttp_request *req, void* )
155 {
156  struct evbuffer *buf = evbuffer_new();
157  if (buf == NULL) return;
158 
159  Json::Value root;
160  root["version"]= ::drizzled::version();
161 
162  Json::StyledWriter writer;
163  std::string output= writer.write(root);
164 
165  evbuffer_add(buf, output.c_str(), output.length());
166  evhttp_send_reply(req, HTTP_OK, "OK", buf);
167 }
168 
169 extern "C" void process_api01_sql_req(struct evhttp_request *req, void* )
170 {
171  struct evbuffer *buf = evbuffer_new();
172  if (buf == NULL) return;
173 
174  std::string input;
175  char buffer[1024];
176  int l=0;
177  do {
178  l= evbuffer_remove(req->input_buffer, buffer, 1024);
179  input.append(buffer, l);
180  }while(l);
181 
182  drizzled::Session::shared_ptr _session= drizzled::Session::make_shared(drizzled::plugin::Listen::getNullClient(),
183  drizzled::catalog::local());
184  drizzled::identifier::user::mptr user_id= identifier::User::make_shared();
185  user_id->setUser("");
186  _session->setUser(user_id);
187  _session->set_schema("test");
188 
189  drizzled::Execute execute(*(_session.get()), true);
190 
191  drizzled::sql::ResultSet result_set(1);
192 
193  /* Execute wraps the SQL to run within a transaction */
194  execute.run(input, result_set);
195  drizzled::sql::Exception exception= result_set.getException();
196 
197  drizzled::error_t err= exception.getErrorCode();
198 
199  Json::Value root;
200  root["sqlstate"]= exception.getSQLState();
201 
202  if ((err != drizzled::EE_OK) && (err != drizzled::ER_EMPTY_QUERY))
203  {
204  root["error_message"]= exception.getErrorMessage();
205  root["error_code"]= exception.getErrorCode();
206  }
207 
208  while (result_set.next())
209  {
210  Json::Value json_row;
211  for (size_t x= 0; x < result_set.getMetaData().getColumnCount(); x++)
212  {
213  if (not result_set.isNull(x))
214  {
215  json_row[x]= result_set.getString(x);
216  }
217  }
218  root["result_set"].append(json_row);
219  }
220 
221  root["query"]= input;
222 
223  Json::StyledWriter writer;
224  std::string output= writer.write(root);
225 
226  evbuffer_add(buf, output.c_str(), output.length());
227  evhttp_send_reply(req, HTTP_OK, "OK", buf);
228 }
229 
230 static void shutdown_event(int fd, short, void *arg)
231 {
232  struct event_base *base= (struct event_base *)arg;
233  event_base_loopbreak(base);
234  close(fd);
235 }
236 
237 
238 static void run(struct event_base *base)
239 {
240  internal::my_thread_init();
241 
242  event_base_dispatch(base);
243 }
244 
245 
247 {
248 private:
249  drizzled::thread_ptr json_thread;
250  in_port_t _port;
251  struct evhttp *httpd;
252  struct event_base *base;
253  int wakeup_fd[2];
254  struct event wakeup_event;
255 
256 public:
257  JsonServer(in_port_t port_arg) :
258  drizzled::plugin::Daemon("json_server"),
259  _port(port_arg),
260  httpd(NULL),
261  base(NULL)
262  { }
263 
264  bool init()
265  {
266  if (pipe(wakeup_fd) < 0)
267  {
268  sql_perror("pipe");
269  return false;
270  }
271 
272  int returned_flags;
273  if ((returned_flags= fcntl(wakeup_fd[0], F_GETFL, 0)) < 0)
274  {
275  sql_perror("fcntl:F_GETFL");
276  return false;
277  }
278 
279  if (fcntl(wakeup_fd[0], F_SETFL, returned_flags | O_NONBLOCK) < 0)
280 
281  {
282  sql_perror("F_SETFL");
283  return false;
284  }
285 
286  if ((base= event_init()) == NULL)
287  {
288  sql_perror("event_init()");
289  return false;
290  }
291 
292  if ((httpd= evhttp_new(base)) == NULL)
293  {
294  sql_perror("evhttp_new()");
295  return false;
296  }
297 
298 
299  if ((evhttp_bind_socket(httpd, "0.0.0.0", getPort())) == -1)
300  {
301  sql_perror("evhttp_bind_socket()");
302  return false;
303  }
304 
305  evhttp_set_cb(httpd, "/", process_root_request, NULL);
306  evhttp_set_cb(httpd, "/0.1/version", process_api01_version_req, NULL);
307  evhttp_set_cb(httpd, "/0.1/sql", process_api01_sql_req, NULL);
308  evhttp_set_gencb(httpd, process_request, NULL);
309 
310  event_set(&wakeup_event, wakeup_fd[0], EV_READ | EV_PERSIST, shutdown_event, base);
311  event_base_set(base, &wakeup_event);
312  if (event_add(&wakeup_event, NULL) < 0)
313  {
314  sql_perror("event_add");
315  return false;
316  }
317 
318  json_thread.reset(new boost::thread((boost::bind(&run, base))));
319 
320  if (not json_thread)
321  return false;
322 
323  return true;
324  }
325 
326  ~JsonServer()
327  {
328  // If we can't write(), we will just muddle our way through the shutdown
329  char buffer[1];
330  buffer[0]= 4;
331  if ((write(wakeup_fd[1], &buffer, 1)) == 1)
332  {
333  json_thread->join();
334  evhttp_free(httpd);
335  event_base_free(base);
336  }
337  }
338 };
339 
340 static int json_server_init(drizzled::module::Context &context)
341 {
342  context.registerVariable(new sys_var_constrained_value_readonly<in_port_t>("port", port));
343 
344  JsonServer *server;
345  context.add(server= new JsonServer(port));
346 
347  if (server and not server->init())
348  {
349  return -2;
350  }
351 
352  return bool(server) ? 0 : 1;
353 }
354 
355 static void init_options(drizzled::module::option_context &context)
356 {
357  context("port",
358  po::value<port_constraint>(&port)->default_value(8086),
359  _("Port number to use for connection or 0 for default (port 8086) "));
360 }
361 
362 } /* namespace json_server */
363 } /* namespace drizzle_plugin */
364 
365 DRIZZLE_DECLARE_PLUGIN
366 {
367  DRIZZLE_VERSION_ID,
368  "json_server",
369  "0.1",
370  "Stewart Smith",
371  N_("JSON HTTP interface"),
372  PLUGIN_LICENSE_GPL,
373  drizzle_plugin::json_server::json_server_init,
374  NULL,
375  drizzle_plugin::json_server::init_options
376 }
377 DRIZZLE_DECLARE_PLUGIN_END;