webmcp

view framework/bin/mcp.lua @ 313:d34b7b5e5e5c

Check if framework path is correct in mcp.lua (gives a more helpful error message if the framework path is wrong)
author jbe
date Sun Mar 22 23:08:18 2015 +0100 (2015-03-22)
parents 9fef75a02542
children a2c733535b8e
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 -- check if framework path is correct
137 do
138 local file, errmsg = io.open(WEBMCP_FRAMEWORK_PATH .. "webmcp_version", "r")
139 if not file then
140 error('Could not find "webmcp_version" file: ' .. errmsg, 0)
141 end
142 local version = assert(file:read())
143 assert(file:close())
144 if version ~= WEBMCP_VERSION then
145 error('Version mismatch in file "' .. WEBMCP_FRAMEWORK_PATH .. 'webmcp_version"')
146 end
147 end
149 -- setup search paths for libraries
150 do
151 if string.match(package.path, "^[^;]") then
152 package.path = ";" .. package.path
153 end
154 package.path = WEBMCP_FRAMEWORK_PATH .. "lib/?.lua" .. package.path
155 -- find out which file name extension shared libraries have
156 local slib_exts = {}
157 for ext in string.gmatch(package.cpath, "%?%.([A-Za-z0-9_-]+)") do
158 if not slib_exts[ext] then
159 slib_exts[#slib_exts+1] = ext
160 slib_exts[ext] = true
161 end
162 end
163 local paths = {}
164 for i, ext in ipairs(slib_exts) do
165 paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "accelerator/?." .. ext
166 end
167 for i, ext in ipairs(slib_exts) do
168 paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "lib/?." .. ext
169 end
170 paths[#paths+1] = package.cpath
171 package.cpath = table.concat(paths, ";")
172 end
174 -- autoloader system for WebMCP environment "$WEBMCP_FRAMEWORK_PATH/env/",
175 -- application environment extensions "$WEBMCP_BASE_PATH/env/"
176 -- and models "$WEBMCP_BASE_PATH/model/"
177 do
178 local weakkey_mt = { __mode = "k" }
179 local autoloader_category = setmetatable({}, weakkey_mt)
180 local autoloader_path = setmetatable({}, weakkey_mt)
181 local autoloader_mt = {}
182 local function install_autoloader(self, category, path_fragment)
183 autoloader_category[self] = category
184 autoloader_path[self] = path_fragment
185 setmetatable(self, autoloader_mt)
186 end
187 local function try_exec(filename)
188 local func = loadcached(filename)
189 if func then
190 func()
191 return true
192 else
193 return false
194 end
195 end
196 function autoloader_mt.__index(self, key)
197 local category, base_path, merge_base_path, file_key
198 local merge = false
199 if
200 string.find(key, "^[a-z_][A-Za-z0-9_]*$") and
201 not string.find(key, "^__")
202 then
203 category = "env"
204 base_path = WEBMCP_FRAMEWORK_PATH .. "env/"
205 merge = true
206 merge_base_path = WEBMCP_BASE_PATH .. "env/"
207 file_key = key
208 elseif string.find(key, "^[A-Z][A-Za-z0-9]*$") then
209 category = "model"
210 base_path = WEBMCP_BASE_PATH .. "model/"
211 local first = true
212 file_key = string.gsub(key, "[A-Z]",
213 function(c)
214 if first then
215 first = false
216 return string.lower(c)
217 else
218 return "_" .. string.lower(c)
219 end
220 end
221 )
222 else
223 return
224 end
225 local required_category = autoloader_category[self]
226 if required_category and required_category ~= category then return end
227 local path_fragment = autoloader_path[self]
228 local path = base_path .. path_fragment .. file_key
229 local merge_path
230 if merge then
231 merge_path = merge_base_path .. path_fragment .. file_key
232 end
233 local function try_dir(dirname)
234 local dir = io.open(dirname)
235 if dir then
236 io.close(dir)
237 local obj = {}
238 install_autoloader(obj, category, path_fragment .. file_key .. "/")
239 rawset(self, key, obj)
240 try_exec(path .. "/__init.lua")
241 if merge then try_exec(merge_path .. "/__init.lua") end
242 return true
243 else
244 return false
245 end
246 end
247 if self == _G then
248 allowed_globals[key] = true
249 end
250 if merge and try_exec(merge_path .. ".lua") then
251 elseif merge and try_dir(merge_path .. "/") then
252 elseif try_exec(path .. ".lua") then
253 elseif try_dir(path .. "/") then
254 else end
255 if self == _G then
256 allowed_globals[key] = nil
257 end
258 return rawget(self, key)
259 end
260 install_autoloader(_G, nil, "")
261 try_exec(WEBMCP_FRAMEWORK_PATH .. "env/__init.lua")
262 try_exec(WEBMCP_BASE_PATH .. "env/__init.lua")
263 end
265 -- replace Moonbridge listen function
266 local moonbridge_listen = listen
267 local listeners = {}
268 function listen(args)
269 listeners[#listeners+1] = args
270 end
272 -- prohibit (unintended) definition of new global variables
273 function global_metatable.__newindex(self, key, value)
274 if not allowed_globals[key] then
275 error("Setting of global variable prohibited", 2)
276 end
277 _G[key] = value
278 end
280 -- execute configurations and pre-fork initializers
281 for i, config_name in ipairs(WEBMCP_CONFIG_NAMES) do
282 execute.config(config_name)
283 end
284 execute.prefork_initializers()
286 -- define post-fork initialization function (including loading of "multirand" library)
287 local function postfork_init()
288 _G.multirand = require "multirand"
289 execute.postfork_initializers()
290 end
292 -- interactive console mode
293 if WEBMCP_MODE == "interactive" then
294 postfork_init()
295 trace.disable() -- avoids memory leakage
296 end
298 -- invoke moonbridge
299 if WEBMCP_MODE == "listen" then
300 local http_options = request.get_http_options()
301 local min_requests_per_fork = http_options.min_requests_per_fork or 50
302 local max_requests_per_fork = http_options.max_requests_per_fork or 100
303 local http = require("moonbridge_http")
304 for i, listener in ipairs(listeners) do
305 local request_count = 0
306 local function inner_handler(http_request)
307 request_count = request_count + 1
308 request.handler(http_request, request_count >= max_requests_per_fork)
309 end
310 local outer_handler = http.generate_handler(inner_handler, http_options)
311 listener.prepare = postfork_init
312 listener.connect = function(socket)
313 outer_handler(socket)
314 return request_count < min_requests_per_fork
315 end
316 listener.finish = execute.finalizers
317 moonbridge_listen(listener)
318 end
319 end

Impressum / About Us