webmcp

view framework/bin/mcp.lua @ 330:22275c74023a

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

Impressum / About Us