webmcp
view framework/bin/mcp.lua @ 204:b059efd81649
Moonbridge invocation in framework/bin/mcp.lua
| author | jbe | 
|---|---|
| date | Fri Jan 09 04:54:50 2015 +0100 (2015-01-09) | 
| parents | c6ef9991b911 | 
| children | eb3e236d261d | 
 line source
     1 #!/usr/bin/env moonbridge
     3 WEBMCP_VERSION = "2.0.0_devel"
     5 -- check if interactive mode
     6 if listen then  -- defined by moonbridge
     7   WEBMCP_MODE = "listen"
     8 else
     9   WEBMCP_MODE = "interactive"
    10 end
    12 -- store extra command line arguments
    13 local extraargs = {select(3, ...)}
    15 -- determine framework and bath path from command line arguments
    16 -- or print usage synopsis (if applicable)
    17 do
    18   local arg1, arg2 = ...
    19   local helpout
    20   if
    21     arg1 == "-h" or arg1 == "--help" or
    22     arg2 == "-h" or arg2 == "--help"
    23   then
    24     helpout = io.stdout
    25   elseif
    26     (WEBMCP_MODE == "listen" and (#extraargs < 2 or #extraargs % 2 ~= 0)) or
    27     (WEBMCP_MODE == "interactive" and arg2 == nil)
    28   then
    29     helpout = io.stderr
    30   end
    31   helpout:write("Usage: moonbridge -- mcp.lua <framework path> <app base path> <app name 1> <config name 1> [<app name 2> <config name 2> ...]\n")
    32   helpout:write("   or: lua -i mcp.lua <framework path> <app base path> [<config name>]\n")
    33   if helpout then
    34     if helpout == io.stderr then
    35       return 1
    36     else
    37       return 0
    38     end
    39   end
    40   local function append_trailing_slash(str)
    41     return string.sub(str, "([^/])$", function(last) return last .. "/" end)
    42   end
    43   WEBMCP_FRAMEWORK_PATH = append_trailing_slash(arg1)
    44   WEBMCP_BASE_PATH      = append_trailing_slash(arg2)
    45 end
    47 -- setup search paths for libraries
    48 do
    49   package.path = WEBMCP_FRAMEWORK_PATH .. "lib/?.lua;" .. package.path
    50   -- find out which file name extension shared libraries have
    51   local slib_exts = {}
    52   for ext in string.gmatch(package.cpath, "%?%.([A-Za-z0-9_-]+)") do
    53     slib_exts[ext] = true
    54   end
    55   local paths = {}
    56   for ext in pairs(slib_exts) do
    57     paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "accelerator/?." .. ext
    58   end
    59   for ext in pairs(slib_exts) do
    60     paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "lib/?." .. ext
    61   end
    62   paths[#paths+1] = package.cpath
    63   package.cpath = table.concat(paths, ";")
    64 end
    66 -- autoloader system for WebMCP environment "$WEBMCP_FRAMEWORK_PATH/env/",
    67 -- application environment extensions "$WEBMCP_BASE_PATH/env/"
    68 -- and models "$WEBMCP_BASE_PATH/model/"
    69 do
    70   local weakkey_mt = { __mode = "k" }
    71   local autoloader_category = setmetatable({}, weakkey_mt)
    72   local autoloader_path     = setmetatable({}, weakkey_mt)
    73   local autoloader_mt       = {}
    74   local function install_autoloader(self, category, path)
    75     autoloader_category[self] = category
    76     autoloader_path[self]     = path
    77     setmetatable(self, autoloader_mt)
    78   end
    79   local function try_exec(filename)
    80     local file = io.open(filename, "r")
    81     if file then
    82       local filedata = file:read("*a")
    83       io.close(file)
    84       local func, errmsg = load(filedata, "=" .. filename)
    85       if func then
    86         func()
    87         return true
    88       else
    89         error(errmsg, 0)
    90       end
    91     else
    92       return false
    93     end
    94   end
    95   local function compose_path_string(base, path, key)
    96     if #path == 0 then
    97       return base .. "/" .. key
    98     else
    99       return base .. table.concat(path, "/") .. "/" .. key
   100     end
   101   end
   102   function autoloader_mt.__index(self, key)
   103     local category, base_path, merge_base_path, file_key
   104     local merge = false
   105     if
   106       string.find(key, "^[a-z_][A-Za-z0-9_]*$") and
   107       not string.find(key, "^__")
   108     then
   109       category        = "env"
   110       base_path       = WEBMCP_FRAMEWORK_PATH .. "env/"
   111       merge           = true
   112       merge_base_path = WEBMCP_BASE_PATH .. "env/"
   113       file_key        = key
   114     elseif string.find(key, "^[A-Z][A-Za-z0-9]*$") then
   115       category        = "model"
   116       base_path       = WEBMCP_BASE_PATH .. "model/"
   117       local first = true
   118       file_key = string.gsub(key, "[A-Z]",
   119         function(c)
   120           if first then
   121             first = false
   122             return string.lower(c)
   123           else
   124             return "_" .. string.lower(c)
   125           end
   126         end
   127       )
   128     else
   129       return
   130     end
   131     local required_category = autoloader_category[self]
   132     if required_category and required_category ~= category then return end
   133     local path = autoloader_path[self]
   134     local path_string = compose_path_string(base_path, path, file_key)
   135     local merge_path_string
   136     if merge then
   137       merge_path_string = compose_path_string(
   138         merge_base_path, path, file_key
   139       )
   140     end
   141     local function try_dir(dirname)
   142       local dir = io.open(dirname)
   143       if dir then
   144         io.close(dir)
   145         local obj = {}
   146         local sub_path = {}
   147         for i = 1, #path do sub_path[i] = path[i] end
   148         sub_path[#path+1] = file_key
   149         install_autoloader(obj, category, sub_path)
   150         rawset(self, key, obj)
   151         try_exec(path_string .. "/__init.lua")
   152         if merge then try_exec(merge_path_string .. "/__init.lua") end
   153         return true
   154       else
   155         return false
   156       end
   157     end
   158     if merge and try_exec(merge_path_string .. ".lua") then
   159     elseif merge and try_dir(merge_path_string .. "/") then
   160     elseif try_exec(path_string .. ".lua") then
   161     elseif try_dir(path_string .. "/") then
   162     else end
   163     return rawget(self, key)
   164   end
   165   install_autoloader(_G, nil, {})
   166   try_exec(WEBMCP_FRAMEWORK_PATH .. "env/__init.lua")
   167   try_exec(WEBMCP_BASE_PATH .. "env/__init.lua")
   168 end
   170 -- prohibit (unintended) definition of new global variables
   171 _ENV = setmetatable({}, {
   172   __index = _G,
   173   __newindex = function()
   174     error("Setting of global variable prohibited")
   175   end
   176 })
   178 -- interactive console mode
   179 if WEBMCP_MODE == "interactive" then
   180   trace.disable()  -- avoids memory leakage (TODO: needs general solution for moonbridge?)
   181   local config_name = select(3, ...)
   182   if config_name then
   183     execute.config(config_name)
   184   end
   185   return
   186 end
   188 -- invoke moonbridge
   189 local moonbridge_listen = listen
   190 local listeners
   191 function _G.listen(args)
   192   listeners[#listeners+1] = args
   193 end
   194 for i = 1, #extraargs/2 do
   195   local config = {}
   196   local function prepare_globals()
   197     _G.WEBMCP_APP_NAME = extraargs[2*i-1]
   198     _G.WEBMCP_CONFIG_NAME = extraargs[2*i]
   199     _G.config = config
   200   end
   201   prepare_globals()
   202   listeners = {}
   203   execute.config(config_name)
   204   for i, listener in ipairs(listeners) do
   205     function listener.prepare()
   206       prepare_globals()
   207       request.execute_preparers()
   208     end
   209     listener.connect = request.connect
   210     listener.finish = request.execute_finishers
   211   end
   212 end
   214 --[[ TODO: following lines to be moved to request.connect(...)
   216 local success, error_info = xpcall(
   217   function()
   219     -- execute configuration file
   220     do
   221       local config_name = request.get_config_name()
   222       if config_name then
   223         execute.config(config_name)
   224       end
   225     end
   227     -- restore slots if coming from http redirect
   228     if cgi.params.tempstore then
   229       trace.restore_slots{}
   230       local blob = tempstore.pop(cgi.params.tempstore)
   231       if blob then slot.restore_all(blob) end
   232     end
   234     local function file_exists(filename)
   235       local file = io.open(filename, "r")
   236       if file then
   237         io.close(file)
   238         return true
   239       else
   240         return false
   241       end
   242     end
   244     if request.is_404() then
   245       request.set_status("404 Not Found")
   246       if request.get_404_route() then
   247         request.forward(request.get_404_route())
   248       else
   249         error("No 404 page set.")
   250       end
   251     elseif request.get_action() then
   252       trace.request{
   253         module = request.get_module(),
   254         action = request.get_action()
   255       }
   256       if
   257         request.get_404_route() and
   258         not file_exists(
   259           encode.action_file_path{
   260             module = request.get_module(),
   261             action = request.get_action()
   262           }
   263         )
   264       then
   265         request.set_status("404 Not Found")
   266         request.forward(request.get_404_route())
   267       else
   268         if cgi.method ~= "POST" then
   269           request.set_status("405 Method Not Allowed")
   270           cgi.add_header("Allow: POST")
   271           error("Tried to invoke an action with a GET request.")
   272         end
   273         local action_status = execute.filtered_action{
   274           module = request.get_module(),
   275           action = request.get_action(),
   276         }
   277         if not request.is_rerouted() then
   278           local routing_mode, routing_module, routing_view
   279           routing_mode   = cgi.params["_webmcp_routing." .. action_status .. ".mode"]
   280           routing_module = cgi.params["_webmcp_routing." .. action_status .. ".module"]
   281           routing_view   = cgi.params["_webmcp_routing." .. action_status .. ".view"]
   282           routing_anchor = cgi.params["_webmcp_routing." .. action_status .. ".anchor"]
   283           if not (routing_mode or routing_module or routing_view) then
   284             action_status = "default"
   285             routing_mode   = cgi.params["_webmcp_routing.default.mode"]
   286             routing_module = cgi.params["_webmcp_routing.default.module"]
   287             routing_view   = cgi.params["_webmcp_routing.default.view"]
   288             routing_anchor = cgi.params["_webmcp_routing.default.anchor"]
   289           end
   290           assert(routing_module, "Routing information has no module.")
   291           assert(routing_view,   "Routing information has no view.")
   292           if routing_mode == "redirect" then
   293             local routing_params = {}
   294             for key, value in pairs(cgi.params) do
   295               local status, stripped_key = string.match(
   296                 key, "^_webmcp_routing%.([^%.]*)%.params%.(.*)$"
   297               )
   298               if status == action_status then
   299                 routing_params[stripped_key] = value
   300               end
   301             end
   302             request.redirect{
   303               module = routing_module,
   304               view   = routing_view,
   305               id     = cgi.params["_webmcp_routing." .. action_status .. ".id"],
   306               params = routing_params,
   307               anchor = routing_anchor
   308             }
   309           elseif routing_mode == "forward" then
   310             request.forward{ module = routing_module, view = routing_view }
   311           else
   312             error("Missing or unknown routing mode in request parameters.")
   313           end
   314         end
   315       end
   316     else
   317       -- no action
   318       trace.request{
   319         module = request.get_module(),
   320         view   = request.get_view()
   321       }
   322       if
   323         request.get_404_route() and
   324         not file_exists(
   325           encode.view_file_path{
   326             module = request.get_module(),
   327             view   = request.get_view()
   328           }
   329         )
   330       then
   331         request.set_status("404 Not Found")
   332         request.forward(request.get_404_route())
   333       end
   334     end
   336     if not request.get_redirect_data() then
   337       request.process_forward()
   338       local view = request.get_view()
   339       if string.find(view, "^_") then
   340         error("Tried to call a private view (prefixed with underscore).")
   341       end
   342       execute.filtered_view{
   343         module = request.get_module(),
   344         view   = view,
   345       }
   346     end
   348     -- force error due to missing absolute base URL until its too late to display error message
   349     --if request.get_redirect_data() then
   350     --  request.get_absolute_baseurl()
   351     --end
   353   end,
   355   function(errobj)
   356     return {
   357       errobj = errobj,
   358       stacktrace = string.gsub(
   359         debug.traceback('', 2),
   360         "^\r?\n?stack traceback:\r?\n?", ""
   361       )
   362     }
   363   end
   364 )
   366 if not success then trace.error{} end
   368 -- laufzeitermittlung
   369 trace.exectime{ real = extos.monotonic_hires_time(), cpu = os.clock() }
   371 slot.select('trace', trace.render)  -- render trace information
   373 local redirect_data = request.get_redirect_data()
   375 -- log error and switch to error layout, unless success
   376 if not success then
   377   local errobj     = error_info.errobj
   378   local stacktrace = error_info.stacktrace
   379   if not request.get_status() and not request.get_json_request_slots() then
   380     request.set_status("500 Internal Server Error")
   381   end
   382   slot.set_layout('system_error')
   383   slot.select('system_error', function()
   384     if getmetatable(errobj) == mondelefant.errorobject_metatable then
   385       slot.put(
   386         "<p>Database error of class <b>",
   387         encode.html(errobj.code),
   388         "</b> occured:<br/><b>",
   389         encode.html(errobj.message),
   390         "</b></p>"
   391       )
   392     else
   393       slot.put("<p><b>", encode.html(tostring(errobj)), "</b></p>")
   394     end
   395     slot.put("<p>Stack trace follows:<br/>")
   396     slot.put(encode.html_newlines(encode.html(stacktrace)))
   397     slot.put("</p>")
   398   end)
   399 elseif redirect_data then
   400   local redirect_params = {}
   401   for key, value in pairs(redirect_data.params) do
   402     redirect_params[key] = value
   403   end
   404   local slot_dump = slot.dump_all()
   405   if slot_dump ~= "" then
   406     redirect_params.tempstore = tempstore.save(slot_dump)
   407   end
   408   local json_request_slots = request.get_json_request_slots()
   409   if json_request_slots then
   410     redirect_params["_webmcp_json_slots[]"] = json_request_slots  
   411   end
   412   cgi.redirect(
   413     encode.url{
   414       base   = request.get_absolute_baseurl(),
   415       module = redirect_data.module,
   416       view   = redirect_data.view,
   417       id     = redirect_data.id,
   418       params = redirect_params,
   419       anchor = redirect_data.anchor
   420     }
   421   )
   422   cgi.send_data()
   423 end
   425 if not success or not redirect_data then
   427   local http_status = request.get_status()
   428   if http_status then
   429     cgi.set_status(http_status)
   430   end
   432   local json_request_slots = request.get_json_request_slots()
   433   if json_request_slots then
   434     cgi.set_content_type('application/json')
   435     local data = {}
   436     for idx, slot_ident in ipairs(json_request_slots) do
   437       data[slot_ident] = slot.get_content(slot_ident)
   438     end
   439     cgi.send_data(encode.json(data))
   440   else
   441     cgi.set_content_type(slot.get_content_type())
   442     cgi.send_data(slot.render_layout())
   443   end
   444 end
   446 --]]
