webmcp

view framework/bin/mcp.lua @ 317:732c4d53a823

Invoke Moonbridge's listen function without delay (requires passing http_options as argument); Code cleanup
author jbe
date Mon Mar 23 19:05:32 2015 +0100 (2015-03-23)
parents a2c733535b8e
children f5660406ad3b
line source
1 #!/usr/bin/env moonbridge
3 --[[--
4 WEBMCP_VERSION
6 A string containing the WebMCP version, e.g. "2.0.0"
7 --]]--
8 WEBMCP_VERSION = "2.0.0"
9 --//--
11 --[[--
12 _G
14 A reference to the global namespace. To avoid accidental programming errors, global variables cannot be set directly, but they must be set through the _G reference, e.g. use _G.foo = true to set the variable "foo" to a value of true.
16 The only exception is the <framework>/env/__init.lua file and the <application>/env/__init.lua file, in which global variables may be set without using the _G reference.
18 Note that the global namespace may or may not be shared between requests (Moonbridge creates multiple forks of the Lua machine). To set variables that are to be cleared after the request has been finished, an application may use the "app" table, e.g. app.foo = true to set the variable app.foo to a value of true, which will be cleared automatically when the request has ended.
20 --]]--
21 local _G = _G
22 local allowed_globals = {}
23 local global_metatable = {
24 __index = _G,
25 __newindex = function(self, key, value)
26 _G[key] = value
27 end
28 }
29 _ENV = setmetatable(
30 {}, -- proxy environment used within mcp.lua and by all chunks loaded through loadcached(...)
31 global_metatable
32 )
33 local function protect_globals() -- called before first configuration file is loaded
34 function global_metatable.__newindex(self, key, value)
35 if allowed_globals[key] then
36 _G[key] = value
37 else
38 if type(key) == "string" and string.match(key, "^[A-Za-z_][A-Za-z_0-9]*$") then
39 error('Attempt to set global variable "' .. key .. '" (hint: use _G.' .. key .. '=<value> to override protection mechnamism)', 2)
40 else
41 error('Attempt to set global variable', 2)
42 end
43 end
44 end
45 end
47 --[[--
48 lua_func = -- compiled Lua function
49 loadcached(
50 filename -- path to a Lua source or byte-code file
51 )
53 Loads, compiles and caches a Lua chunk. If the file does not exist, nil and an error string are returned. Otherwise the file is loaded, compiled, and cached. The cached value (i.e. the compiled function) is returned. An error is raised if the compilation was not successful.
55 --]]--
56 do
57 local cache = {}
58 function loadcached(filename)
59 local cached_func = cache[filename]
60 if cached_func then
61 return cached_func
62 end
63 local file, read_error = io.open(filename, "r")
64 if file then
65 local filedata = assert(file:read("*a"))
66 assert(file:close())
67 local func, compile_error = load(filedata, "=" .. filename, nil, _ENV)
68 if func then
69 cache[filename] = func
70 return func
71 end
72 error(compile_error, 0)
73 else
74 return nil, read_error
75 end
76 end
77 end
78 --//--
80 --[[--
81 WEBMCP_MODE
83 A constant set to "listen" in case of a network request, or set to "interactive" in case of interactive mode.
84 --]]--
85 if listen then -- defined by moonbridge
86 WEBMCP_MODE = "listen"
87 else
88 WEBMCP_MODE = "interactive"
89 end
90 --//--
92 --[[--
93 WEBMCP_CONFIG_NAMES
95 A list of the selected configuration names.
96 --]]--
97 -- configuration names are provided as 4th, 5th, etc. command line argument
98 WEBMCP_CONFIG_NAMES = {select(4, ...)}
99 --//--
101 --[[--
102 WEBMCP_FRAMEWORK_PATH
104 Directory of the WebMCP framework (always includes a trailing slash).
105 --]]--
106 -- set in mcp.lua
107 --//--
109 --[[--
110 WEBMCP_BASE_PATH
112 Base directory of the application (always includes a trailing slash).
113 --]]--
114 -- set in mcp.lua
115 --//--
117 --[[--
118 WEBMCP_APP_NAME
120 Application name (usually "main"). May be nil in case of interactive mode.
121 --]]--
122 -- set in mcp.lua
123 --//--
125 -- determine framework and bath path from command line arguments
126 -- or print usage synopsis (if applicable)
127 do
128 local arg1, arg2, arg3 = ...
129 local helpout
130 if
131 arg1 == "-h" or arg1 == "--help" or
132 arg2 == "-h" or arg2 == "--help" -- if first arg is provided by wrapper
133 then
134 helpout = io.stdout
135 elseif #WEBMCP_CONFIG_NAMES < 1 then
136 helpout = io.stderr
137 end
138 if helpout then
139 helpout:write("Usage: moonbridge [moonbr opts] -- <framework path>/bin/mcp.lua <framework path> <app base path> <app name> <config name> [<config name> ...]\n")
140 helpout:write(" or: lua -i [Lua opts] -- <framework path>/bin/mcp.lua <framework path> <app base path> <app name> <config name> [<config name> ...]\n")
141 if helpout == io.stderr then
142 return 1
143 else
144 return 0
145 end
146 end
147 local function append_trailing_slash(str)
148 return string.gsub(str, "([^/])$", function(last) return last .. "/" end)
149 end
150 WEBMCP_FRAMEWORK_PATH = append_trailing_slash(arg1)
151 WEBMCP_BASE_PATH = append_trailing_slash(arg2)
152 WEBMCP_APP_NAME = arg3
153 end
155 -- check if framework path is correct
156 do
157 local file, errmsg = io.open(WEBMCP_FRAMEWORK_PATH .. "webmcp_version", "r")
158 if not file then
159 error('Could not find "webmcp_version" file: ' .. errmsg, 0)
160 end
161 local version = assert(file:read())
162 assert(file:close())
163 if version ~= WEBMCP_VERSION then
164 error('Version mismatch in file "' .. WEBMCP_FRAMEWORK_PATH .. 'webmcp_version"')
165 end
166 end
168 -- setup search paths for libraries
169 do
170 if string.match(package.path, "^[^;]") then
171 package.path = ";" .. package.path
172 end
173 package.path = WEBMCP_FRAMEWORK_PATH .. "lib/?.lua" .. package.path
174 -- find out which file name extension shared libraries have
175 local slib_exts = {}
176 for ext in string.gmatch(package.cpath, "%?%.([A-Za-z0-9_-]+)") do
177 if not slib_exts[ext] then
178 slib_exts[#slib_exts+1] = ext
179 slib_exts[ext] = true
180 end
181 end
182 local paths = {}
183 for i, ext in ipairs(slib_exts) do
184 paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "accelerator/?." .. ext
185 end
186 for i, ext in ipairs(slib_exts) do
187 paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "lib/?." .. ext
188 end
189 paths[#paths+1] = package.cpath
190 package.cpath = table.concat(paths, ";")
191 end
193 -- autoloader system for WebMCP environment "$WEBMCP_FRAMEWORK_PATH/env/",
194 -- application environment extensions "$WEBMCP_BASE_PATH/env/"
195 -- and models "$WEBMCP_BASE_PATH/model/"
196 local root_init -- function which executes the __init.lua file in the environment's root
197 do
198 local weakkey_mt = { __mode = "k" }
199 local autoloader_category = setmetatable({}, weakkey_mt)
200 local autoloader_path = setmetatable({}, weakkey_mt)
201 local autoloader_mt = {}
202 local function install_autoloader(self, category, path_fragment)
203 autoloader_category[self] = category
204 autoloader_path[self] = path_fragment
205 setmetatable(self, autoloader_mt)
206 end
207 local function try_exec(filename)
208 local func = loadcached(filename)
209 if func then
210 func()
211 return true
212 else
213 return false
214 end
215 end
216 function autoloader_mt.__index(self, key)
217 local category, base_path, merge_base_path, file_key
218 local merge = false
219 if
220 string.find(key, "^[a-z_][A-Za-z0-9_]*$") and
221 not string.find(key, "^__")
222 then
223 category = "env"
224 base_path = WEBMCP_FRAMEWORK_PATH .. "env/"
225 merge = true
226 merge_base_path = WEBMCP_BASE_PATH .. "env/"
227 file_key = key
228 elseif string.find(key, "^[A-Z][A-Za-z0-9]*$") then
229 category = "model"
230 base_path = WEBMCP_BASE_PATH .. "model/"
231 local first = true
232 file_key = string.gsub(key, "[A-Z]",
233 function(c)
234 if first then
235 first = false
236 return string.lower(c)
237 else
238 return "_" .. string.lower(c)
239 end
240 end
241 )
242 else
243 return
244 end
245 local required_category = autoloader_category[self]
246 if required_category and required_category ~= category then return end
247 local path_fragment = autoloader_path[self]
248 local path = base_path .. path_fragment .. file_key
249 local merge_path
250 if merge then
251 merge_path = merge_base_path .. path_fragment .. file_key
252 end
253 local function try_dir(dirname)
254 local dir = io.open(dirname)
255 if dir then
256 io.close(dir)
257 local obj = {}
258 install_autoloader(obj, category, path_fragment .. file_key .. "/")
259 rawset(self, key, obj)
260 try_exec(path .. "/__init.lua")
261 if merge then try_exec(merge_path .. "/__init.lua") end
262 return true
263 else
264 return false
265 end
266 end
267 if self == _G then
268 allowed_globals[key] = true
269 end
270 if merge and try_exec(merge_path .. ".lua") then
271 elseif merge and try_dir(merge_path .. "/") then
272 elseif try_exec(path .. ".lua") then
273 elseif try_dir(path .. "/") then
274 else end
275 if self == _G then
276 allowed_globals[key] = nil
277 end
278 return rawget(self, key)
279 end
280 install_autoloader(_G, nil, "")
281 function root_init() -- upvalue
282 try_exec(WEBMCP_FRAMEWORK_PATH .. "env/__init.lua")
283 try_exec(WEBMCP_BASE_PATH .. "env/__init.lua")
284 end
285 end
287 -- define post-fork initialization function (including loading of "multirand" library)
288 local function postfork_init()
289 _G.multirand = require "multirand"
290 execute.postfork_initializers()
291 end
293 -- prepare for interactive or listen mode
294 if WEBMCP_MODE == "interactive" then
295 function listen() -- overwrite Moonbridge's listen function
296 -- ignore listen function calls for interactive mode
297 end
298 trace.disable() -- avoids memory leakage when scripts are running endlessly
299 else
300 local moonbridge_listen = listen
301 local http = require("moonbridge_http")
302 function listen(args) -- overwrite Moonbridge's listen function
303 local http_options = args.http_options or {}
304 local min_requests_per_fork = http_options.min_requests_per_fork or 50
305 local max_requests_per_fork = http_options.max_requests_per_fork or 100
306 local interval_handlers = {}
307 for j, listener in ipairs(args) do
308 if listener.proto == "interval" then
309 local name = listener.name or "Unnamed interval #" .. #interval_handlers+1
310 interval_handlers[name] = listener.handler
311 listener.name = name
312 end
313 end
314 local request_count = 0
315 local function inner_handler(http_request)
316 request.initialize()
317 request.handler(http_request, request_count >= max_requests_per_fork)
318 end
319 local outer_handler = http.generate_handler(inner_handler, http_options)
320 args.prepare = postfork_init
321 args.connect = function(socket)
322 request_count = request_count + 1
323 if socket.interval then
324 request.initialize()
325 interval_handlers[socket.interval]()
326 else
327 outer_handler(socket)
328 end
329 return request_count < min_requests_per_fork
330 end
331 args.finish = execute.finalizers
332 moonbridge_listen(args)
333 end
334 end
336 -- execute the __init.lua file in the environment's root
337 root_init()
339 -- prohibit (unintended) definition of new global variables
340 protect_globals()
342 -- execute configurations and pre-fork initializers
343 for i, config_name in ipairs(WEBMCP_CONFIG_NAMES) do
344 execute.config(config_name)
345 end
346 execute.prefork_initializers()
348 -- perform post-fork initializations in case of interactive mode
349 if WEBMCP_MODE == "interactive" then
350 postfork_init()
351 end

Impressum / About Us