webmcp

view framework/bin/mcp.lua @ 323:4603e8781fb9

Check _MOONBRIDGE_VERSION instead of global "listen" function to identify Moonbridge environment
author jbe
date Mon Mar 23 22:17:33 2015 +0100 (2015-03-23)
parents bbdfc5e1ca80
children 1c3ba14bd679
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 mechanism)', 2)
40 else
41 error('Attempt to set global variable', 2)
42 end
43 end
44 end
45 end
46 --//--
48 --[[--
49 lua_func = -- compiled Lua function
50 loadcached(
51 filename -- path to a Lua source or byte-code file
52 )
54 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.
56 --]]--
57 do
58 local cache = {}
59 function loadcached(filename)
60 local cached_func = cache[filename]
61 if cached_func then
62 return cached_func
63 end
64 local file, read_error = io.open(filename, "r")
65 if file then
66 local filedata = assert(file:read("*a"))
67 assert(file:close())
68 local func, compile_error = load(filedata, "=" .. filename, nil, _ENV)
69 if func then
70 cache[filename] = func
71 return func
72 end
73 error(compile_error, 0)
74 else
75 return nil, read_error
76 end
77 end
78 end
79 --//--
81 --[[--
82 WEBMCP_MODE
84 A constant set to "listen" in case of a network request, or set to "interactive" in case of interactive mode.
85 --]]--
86 if _MOONBRIDGE_VERSION then
87 WEBMCP_MODE = "listen"
88 else
89 WEBMCP_MODE = "interactive"
90 end
91 --//--
93 --[[--
94 WEBMCP_CONFIG_NAMES
96 A list of the selected configuration names.
97 --]]--
98 -- configuration names are provided as 4th, 5th, etc. command line argument
99 WEBMCP_CONFIG_NAMES = {select(4, ...)}
100 --//--
102 --[[--
103 WEBMCP_FRAMEWORK_PATH
105 Directory of the WebMCP framework (always includes a trailing slash).
106 --]]--
107 -- set in mcp.lua
108 --//--
110 --[[--
111 WEBMCP_BASE_PATH
113 Base directory of the application (always includes a trailing slash).
114 --]]--
115 -- set in mcp.lua
116 --//--
118 --[[--
119 WEBMCP_APP_NAME
121 Application name (usually "main"). May be nil in case of interactive mode.
122 --]]--
123 -- set in mcp.lua
124 --//--
126 -- determine framework and bath path from command line arguments
127 -- or print usage synopsis (if applicable)
128 do
129 local arg1, arg2, arg3 = ...
130 local helpout
131 if
132 arg1 == "-h" or arg1 == "--help" or
133 arg2 == "-h" or arg2 == "--help" -- if first arg is provided by wrapper
134 then
135 helpout = io.stdout
136 elseif #WEBMCP_CONFIG_NAMES < 1 then
137 helpout = io.stderr
138 end
139 if helpout then
140 helpout:write("Usage: moonbridge [moonbr opts] -- <framework path>/bin/mcp.lua <framework path> <app base path> <app name> <config name> [<config name> ...]\n")
141 helpout:write(" or: lua -i [Lua opts] -- <framework path>/bin/mcp.lua <framework path> <app base path> <app name> <config name> [<config name> ...]\n")
142 if helpout == io.stderr then
143 return 1
144 else
145 return 0
146 end
147 end
148 local function append_trailing_slash(str)
149 return string.gsub(str, "([^/])$", function(last) return last .. "/" end)
150 end
151 WEBMCP_FRAMEWORK_PATH = append_trailing_slash(arg1)
152 WEBMCP_BASE_PATH = append_trailing_slash(arg2)
153 WEBMCP_APP_NAME = arg3
154 end
156 -- check if framework path is correct
157 do
158 local file, errmsg = io.open(WEBMCP_FRAMEWORK_PATH .. "webmcp_version", "r")
159 if not file then
160 error('Could not find "webmcp_version" file: ' .. errmsg, 0)
161 end
162 local version = assert(file:read())
163 assert(file:close())
164 if version ~= WEBMCP_VERSION then
165 error('Version mismatch in file "' .. WEBMCP_FRAMEWORK_PATH .. 'webmcp_version"')
166 end
167 end
169 -- setup search paths for libraries
170 do
171 if string.match(package.path, "^[^;]") then
172 package.path = ";" .. package.path
173 end
174 package.path = WEBMCP_FRAMEWORK_PATH .. "lib/?.lua" .. package.path
175 -- find out which file name extension shared libraries have
176 local slib_exts = {}
177 for ext in string.gmatch(package.cpath, "%?%.([A-Za-z0-9_-]+)") do
178 if not slib_exts[ext] then
179 slib_exts[#slib_exts+1] = ext
180 slib_exts[ext] = true
181 end
182 end
183 local paths = {}
184 for i, ext in ipairs(slib_exts) do
185 paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "accelerator/?." .. ext
186 end
187 for i, ext in ipairs(slib_exts) do
188 paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "lib/?." .. ext
189 end
190 paths[#paths+1] = package.cpath
191 package.cpath = table.concat(paths, ";")
192 end
194 -- autoloader system for WebMCP environment "$WEBMCP_FRAMEWORK_PATH/env/",
195 -- application environment extensions "$WEBMCP_BASE_PATH/env/"
196 -- and models "$WEBMCP_BASE_PATH/model/"
197 local root_init -- function which executes the __init.lua file in the environment's root
198 do
199 local weakkey_mt = { __mode = "k" }
200 local autoloader_category = setmetatable({}, weakkey_mt)
201 local autoloader_path = setmetatable({}, weakkey_mt)
202 local autoloader_mt = {}
203 local function install_autoloader(self, category, path_fragment)
204 autoloader_category[self] = category
205 autoloader_path[self] = path_fragment
206 setmetatable(self, autoloader_mt)
207 end
208 local function try_exec(filename)
209 local func = loadcached(filename)
210 if func then
211 func()
212 return true
213 else
214 return false
215 end
216 end
217 function autoloader_mt.__index(self, key)
218 local category, base_path, merge_base_path, file_key
219 local merge = false
220 if
221 string.find(key, "^[a-z_][A-Za-z0-9_]*$") and
222 not string.find(key, "^__")
223 then
224 category = "env"
225 base_path = WEBMCP_FRAMEWORK_PATH .. "env/"
226 merge = true
227 merge_base_path = WEBMCP_BASE_PATH .. "env/"
228 file_key = key
229 elseif string.find(key, "^[A-Z][A-Za-z0-9]*$") then
230 category = "model"
231 base_path = WEBMCP_BASE_PATH .. "model/"
232 local first = true
233 file_key = string.gsub(key, "[A-Z]",
234 function(c)
235 if first then
236 first = false
237 return string.lower(c)
238 else
239 return "_" .. string.lower(c)
240 end
241 end
242 )
243 else
244 return
245 end
246 local required_category = autoloader_category[self]
247 if required_category and required_category ~= category then return end
248 local path_fragment = autoloader_path[self]
249 local path = base_path .. path_fragment .. file_key
250 local merge_path
251 if merge then
252 merge_path = merge_base_path .. path_fragment .. file_key
253 end
254 local function try_dir(dirname)
255 local dir = io.open(dirname)
256 if dir then
257 io.close(dir)
258 local obj = {}
259 install_autoloader(obj, category, path_fragment .. file_key .. "/")
260 rawset(self, key, obj)
261 try_exec(path .. "/__init.lua")
262 if merge then try_exec(merge_path .. "/__init.lua") end
263 return true
264 else
265 return false
266 end
267 end
268 if self == _G then
269 allowed_globals[key] = true
270 end
271 if merge and try_exec(merge_path .. ".lua") then
272 elseif merge and try_dir(merge_path .. "/") then
273 elseif try_exec(path .. ".lua") then
274 elseif try_dir(path .. "/") then
275 else end
276 if self == _G then
277 allowed_globals[key] = nil
278 end
279 return rawget(self, key)
280 end
281 install_autoloader(_G, nil, "")
282 function root_init() -- upvalue
283 try_exec(WEBMCP_FRAMEWORK_PATH .. "env/__init.lua")
284 try_exec(WEBMCP_BASE_PATH .. "env/__init.lua")
285 end
286 end
288 -- define post-fork initialization function (including loading of "multirand" library)
289 local function postfork_init()
290 _G.multirand = require "multirand"
291 execute.postfork_initializers()
292 end
294 -- prepare for interactive or listen mode
295 if WEBMCP_MODE == "interactive" then
296 function listen() -- overwrite Moonbridge's listen function
297 -- ignore listen function calls for interactive mode
298 end
299 trace.disable() -- avoids memory leakage when scripts are running endlessly
300 else
301 local moonbridge_listen = listen
302 local http = require("moonbridge_http")
303 function listen(args) -- overwrite Moonbridge's listen function
304 assert(args, "No argument passed to listen function")
305 local min_requests_per_fork = args.min_requests_per_fork or 50
306 local max_requests_per_fork = args.max_requests_per_fork or 100
307 local interval_handlers = {}
308 for j, listener in ipairs(args) do
309 if listener.proto == "interval" then
310 local name = listener.name or "Unnamed interval #" .. #interval_handlers+1
311 interval_handlers[name] = listener.handler
312 listener.name = name
313 end
314 end
315 local request_count = 0
316 local function inner_handler(http_request)
317 request.initialize()
318 request.handler(http_request, request_count >= max_requests_per_fork)
319 end
320 local outer_handler = http.generate_handler(inner_handler, http_options)
321 args.prepare = postfork_init
322 args.connect = function(socket)
323 request_count = request_count + 1
324 if socket.interval then
325 request.initialize()
326 interval_handlers[socket.interval]()
327 else
328 outer_handler(socket)
329 end
330 return request_count < min_requests_per_fork
331 end
332 args.finish = execute.finalizers
333 moonbridge_listen(args)
334 end
335 end
337 -- execute the __init.lua file in the environment's root
338 root_init()
340 -- prohibit (unintended) definition of new global variables
341 protect_globals()
343 -- execute configurations and pre-fork initializers
344 for i, config_name in ipairs(WEBMCP_CONFIG_NAMES) do
345 execute.config(config_name)
346 end
347 execute.prefork_initializers()
349 -- perform post-fork initializations in case of interactive mode
350 if WEBMCP_MODE == "interactive" then
351 postfork_init()
352 end

Impressum / About Us