webmcp

view framework/bin/mcp.lua @ 316:a2c733535b8e

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

Impressum / About Us