~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/json_server/json_server.cc

  • Committer: Mark Atwood
  • Date: 2011-08-11 03:05:03 UTC
  • mfrom: (2385.1.12 refactor4)
  • Revision ID: me@mark.atwood.name-20110811030503-rp9xjihc5x3y0x4q
merge lp:~olafvdspek/drizzle/refactor4

Show diffs side-by-side

added added

removed removed

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