webmcp

view framework/bin/mcp.lua @ 311:9fef75a02542

Bugfix for newly introduced loadcached function (use the cache)
author jbe
date Sun Mar 22 22:30:19 2015 +0100 (2015-03-22)
parents 4e69ce9a6365
children d34b7b5e5e5c
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
112 #WEBMCP_CONFIG_NAMES < 1 or
113 (WEBMCP_MODE == "interactive") ~= (arg3 == "INTERACTIVE")
114 then
115 helpout = io.stderr
116 end
117 if helpout then
118 helpout:write("Usage: moonbridge -- <framework path>/bin/mcp.lua <framework path> <app base path> <app name> <config name> [<config name> ...]\n")
119 helpout:write(" or: lua -i <framework path>/bin/mcp.lua <framework path> <app base path> INTERACTIVE <config name> [<config name> ...]\n")
120 if helpout == io.stderr then
121 return 1
122 else
123 return 0
124 end
125 end
126 local function append_trailing_slash(str)
127 return string.gsub(str, "([^/])$", function(last) return last .. "/" end)
128 end
129 WEBMCP_FRAMEWORK_PATH = append_trailing_slash(arg1)
130 WEBMCP_BASE_PATH = append_trailing_slash(arg2)
131 if WEBMCP_MODE == "listen" then
132 WEBMCP_APP_NAME = arg3
133 end
134 end
136 -- setup search paths for libraries
137 do
138 if string.match(package.path, "^[^;]") then
139 package.path = ";" .. package.path
140 end
141 package.path = WEBMCP_FRAMEWORK_PATH .. "lib/?.lua" .. package.path
142 -- find out which file name extension shared libraries have
143 local slib_exts = {}
144 for ext in string.gmatch(package.cpath, "%?%.([A-Za-z0-9_-]+)") do
145 if not slib_exts[ext] then
146 slib_exts[#slib_exts+1] = ext
147 slib_exts[ext] = true
148 end
149 end
150 local paths = {}
151 for i, ext in ipairs(slib_exts) do
152 paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "accelerator/?." .. ext
153 end
154 for i, ext in ipairs(slib_exts) do
155 paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "lib/?." .. ext
156 end
157 paths[#paths+1] = package.cpath
158 package.cpath = table.concat(paths, ";")
159 end
161 -- autoloader system for WebMCP environment "$WEBMCP_FRAMEWORK_PATH/env/",
162 -- application environment extensions "$WEBMCP_BASE_PATH/env/"
163 -- and models "$WEBMCP_BASE_PATH/model/"
164 do
165 local weakkey_mt = { __mode = "k" }
166 local autoloader_category = setmetatable({}, weakkey_mt)
167 local autoloader_path = setmetatable({}, weakkey_mt)
168 local autoloader_mt = {}
169 local function install_autoloader(self, category, path_fragment)
170 autoloader_category[self] = category
171 autoloader_path[self] = path_fragment
172 setmetatable(self, autoloader_mt)
173 end
174 local function try_exec(filename)
175 local func = loadcached(filename)
176 if func then
177 func()
178 return true
179 else
180 return false
181 end
182 end
183 function autoloader_mt.__index(self, key)
184 local category, base_path, merge_base_path, file_key
185 local merge = false
186 if
187 string.find(key, "^[a-z_][A-Za-z0-9_]*$") and
188 not string.find(key, "^__")
189 then
190 category = "env"
191 base_path = WEBMCP_FRAMEWORK_PATH .. "env/"
192 merge = true
193 merge_base_path = WEBMCP_BASE_PATH .. "env/"
194 file_key = key
195 elseif string.find(key, "^[A-Z][A-Za-z0-9]*$") then
196 category = "model"
197 base_path = WEBMCP_BASE_PATH .. "model/"
198 local first = true
199 file_key = string.gsub(key, "[A-Z]",
200 function(c)
201 if first then
202 first = false
203 return string.lower(c)
204 else
205 return "_" .. string.lower(c)
206 end
207 end
208 )
209 else
210 return
211 end
212 local required_category = autoloader_category[self]
213 if required_category and required_category ~= category then return end
214 local path_fragment = autoloader_path[self]
215 local path = base_path .. path_fragment .. file_key
216 local merge_path
217 if merge then
218 merge_path = merge_base_path .. path_fragment .. file_key
219 end
220 local function try_dir(dirname)
221 local dir = io.open(dirname)
222 if dir then
223 io.close(dir)
224 local obj = {}
225 install_autoloader(obj, category, path_fragment .. file_key .. "/")
226 rawset(self, key, obj)
227 try_exec(path .. "/__init.lua")
228 if merge then try_exec(merge_path .. "/__init.lua") end
229 return true
230 else
231 return false
232 end
233 end
234 if self == _G then
235 allowed_globals[key] = true
236 end
237 if merge and try_exec(merge_path .. ".lua") then
238 elseif merge and try_dir(merge_path .. "/") then
239 elseif try_exec(path .. ".lua") then
240 elseif try_dir(path .. "/") then
241 else end
242 if self == _G then
243 allowed_globals[key] = nil
244 end
245 return rawget(self, key)
246 end
247 install_autoloader(_G, nil, "")
248 try_exec(WEBMCP_FRAMEWORK_PATH .. "env/__init.lua")
249 try_exec(WEBMCP_BASE_PATH .. "env/__init.lua")
250 end
252 -- replace Moonbridge listen function
253 local moonbridge_listen = listen
254 local listeners = {}
255 function listen(args)
256 listeners[#listeners+1] = args
257 end
259 -- prohibit (unintended) definition of new global variables
260 function global_metatable.__newindex(self, key, value)
261 if not allowed_globals[key] then
262 error("Setting of global variable prohibited", 2)
263 end
264 _G[key] = value
265 end
267 -- execute configurations and pre-fork initializers
268 for i, config_name in ipairs(WEBMCP_CONFIG_NAMES) do
269 execute.config(config_name)
270 end
271 execute.prefork_initializers()
273 -- define post-fork initialization function (including loading of "multirand" library)
274 local function postfork_init()
275 _G.multirand = require "multirand"
276 execute.postfork_initializers()
277 end
279 -- interactive console mode
280 if WEBMCP_MODE == "interactive" then
281 postfork_init()
282 trace.disable() -- avoids memory leakage
283 end
285 -- invoke moonbridge
286 if WEBMCP_MODE == "listen" then
287 local http_options = request.get_http_options()
288 local min_requests_per_fork = http_options.min_requests_per_fork or 50
289 local max_requests_per_fork = http_options.max_requests_per_fork or 100
290 local http = require("moonbridge_http")
291 for i, listener in ipairs(listeners) do
292 local request_count = 0
293 local function inner_handler(http_request)
294 request_count = request_count + 1
295 request.handler(http_request, request_count >= max_requests_per_fork)
296 end
297 local outer_handler = http.generate_handler(inner_handler, http_options)
298 listener.prepare = postfork_init
299 listener.connect = function(socket)
300 outer_handler(socket)
301 return request_count < min_requests_per_fork
302 end
303 listener.finish = execute.finalizers
304 moonbridge_listen(listener)
305 end
306 end

Impressum / About Us