Lua Environments and the .preload Script

Lua keeps all its variables and functions not prefixed by the keyword local in a regular Lua table called the environment. For an introduction to this environment, see the Lua manual, section 2.2.

Lua enables us to create multiple environments and the Mako Server takes advantage of this feature and provides multiple environments for the Lua applications running in the server.

The .preload Script

Keep in mind that deployed (completed) applications should preferably be zipped together and loaded as ZIP files instead of being loaded as directories!

The Mako Server makes it possible to load multiple web applications, which differs from the traditional application server that provides one www directory for one application. With the Mako Server, you can have a www1 and a www2 directory. In fact, you can have any number of directories that can be loaded by the Mako Server. The name of the directory is either provided as an argument or set in the mako.conf file. For example, assuming you have two www directories named app1 and app2, the Mako Server can be started as follows:

mako -l::app1 -l::app2

Create the two directories and run the Mako Server on the command line as shown above. When you run this command, you should see the following being printed in the console:

Info: app1/.preload not found
Info: app2/.preload not found

The .preload script is optional and the above is not an error or warning. The Mako Server simply informs the user that no .preload script could be found in the two loaded applications.

As explained on the Mako Specific Features page, a .preload script is used when creating application generic functions or when designing code that must persist and/or run for the lifetime of the loaded application.

Create the following file in app1/.preload and insert the following Lua code into the .preload script.

varInEnv = 10
print(_ENV == _G)
print(varInEnv == _ENV.varInEnv)
print(varInEnv == _G.varInEnv)

The variable varInEnv is inserted into the application's environment table _ENV since the variable is not prefixed by the keyword local.

You may run the .preload script using the standard Lua distribution as follows:

lua app1/.preload

If you have installed the standard Lua distribution and if you run the above command, the following is printed in the console:

true
true
true

Based on the above printouts, we can conclude that _ENV is the same as the global environment _G. Now start the Mako Server as follows:

mako -l::app1

The following is printed in the console when you run the above command:

false
true
false

Based on the above printouts, we can conclude that the .preload script's environment table _ENV is not the same as _G when run in the Mako Server.

The Mako Server creates a Lua table per loaded application and sets this table as the _ENV (application environment) table. The purpose with this table is to make it easy to separate code in different applications. Without this concept, two applications using the same function or variable name would conflict.

You may change a .preload script's environment variable to use the global scope by inserting the following line at top of the .preload script:

_ENV=_G

Edit the .preload script above, insert the above code line, and re-run the Mako Server command for loading app1. You should see 'true' being printed three times.

Application Environment and Inheritance

Lua provides the concept of inheritance and the Mako Server sets the application environment (_ENV) to inherit from the global environment (_G). The prototype inheritance provided by Lua works as follows: First, the local environment is checked and if no variable/function is found in this table, Lua goes up the prototype chain and looks in the global environment. For example when using the global function print in a .preload script, the function is not found in the _ENV table so Lua looks in the global _G table and finds function print there.

Create the file app2/.preload and insert the following code into this file:

print"Hello World"
print(print) -- output address of function print
local gprint=print
function print(...) gprint("local -> global:", ...) end
print"Hello World"
print(print)

Start the Mako Server as follows:

mako -l::app2

The following is printed in the console when you run the above command:

Hello World
function: 9b12e0
local -> global:        Hello World
local -> global:        function: 4b94298

The first line in the .preload script uses the global print function. Lua cannot find the function in the local scope so it searches and finds the function in the global scope. On line three, we save the address of the global print function in variable gprint. We then create function print in the local application scope, thus locally overriding (shading) the global print function. This function uses the saved global print function to print all arguments. Notice that the printed address for the two print functions differ. The first printout (line 2) prints the global address and the next printout (line 6) prints the address of the local print function.

How the Mako Server Creates an Application

When you instruct the Mako Server to load an application, the server executes the following code:

local dir=ba.create.resrdr(args to resrdr)
local appenv=setmetatable({io=io,dir=dir},{__index=_G})
dir:lspfilter(appenv)
dir:insert()
  1. A Resource Reader is created.
  2. The application environment table (_ENV) is created with pre-configured file IO (io) and reference to the Resource Reader (dir). The table is set to inherit from the global environment _G by setting the prototype inheritance using setmetatable.
  3. The Resource Reader is LSP enabled and the application table is set.
  4. The Resource Reader is inserted into the server's virtual file system.

The above Lua code snippet can be found inside mako.zip in the file .config. You may unzip mako.zip and study the code in .config to find out more on how the Mako Server's integrated Lua code manages applications.

Accessing the Application Environment from Lua Server Pages

The application environment can be accessed from LSP pages by simply prefixing variables and functions with app.

In the app2 directory, create the file env.lsp and insert the following code into the file:

<?lsp
  print"Hello Browser"
  _G.print"Hello Console"
  app.print"Hello Console" -- Access print in app _ENV table
  trace"Hello Trace"
?>

Save the file, use your browser and navigate to http://localhost/env.lsp. You should see Hello Browser in the browser window and the three following lines being printed in the server's console window:

Hello Console
local -> global:        Hello Console
5: Hello Trace

The first code line in our LSP page is using the print function set for the LSP page's ephemeral environment (_ENV), which sends the printed output to the browser. The global environment is accessible using _G and the application environment is accessible using app; thus the next two code lines print using the global print function and the one we defined in the application table in the .preload script for app2.

The LSP page is also inheriting from the global environment and since no trace function is set in the LSP page's environment table, the global trace function is used on line 5.

See the Command (Request/Response) Environment documentation for more information on the scoping rules.

Application Modules

A large code base is usually split up into sections or in Lua terminology, split into modules.

You may create two types of modules, global modules that can be loaded using the Lua function require or local application modules. The benefit in using application modules for an application is that they can use the application environment.

Create the sub directory .lua in the app2 directory and create the file app2/.lua/mymodule.lua

Insert the following code into mymodule.lua and save the file.

app.print"Hello from mymodule"

Open app2/.preload, add the following code line at the end of the file, and save the file:

io:dofile(".lua/mymodule.lua", _ENV)

Restart the Mako Server or start the Mako Server as follows if it is not running:

mako -l::app2

The following is printed in the console when the Mako Server starts:

Hello World
function: 9b12e0
local -> global:        Hello World
local -> global:        function: 4d69220
local -> global:        Hello from mymodule

The last printed line is from the loaded module. Notice that the module is using the print function defined in the .preload script.

Dynamically Loading and Terminating Applications

As mentioned above, applications can be loaded when the Mako Server starts. All applications are gracefully closed when the Mako Server receives a termination command such as pressing Ctrl-C on the command line. Applications can also be created, terminated, and restarted dynamically. An application can be upgraded in a running system without having to restart the server.

When an application terminates, it is sometimes necessary to run code designed to gracefully stop the application. The Mako Server looks for the function onunload in the application's environment table and executes this function, if found, prior to terminating an application.

Open app2/.preload, add the following code line at the end of the file, and save the file.

function onunload()
   print"Closing app2"
end

Restart the Mako Server to load the new .preload script and then press Ctrl-C to terminate the server. You should see the following being printed just before the server exits:

Exiting...
local -> global:        Closing app2

Now that we have a way of knowing when an application terminates by printing a message in the onunload function, go ahead and create the file app2/restart.lsp and insert the following code into the file:

<?lsp
for ix,path in ipairs(mako.getapps()) do
   print("Reloading:",path)
   mako.reloadapp(ix)
end?>

Save the file, use your browser and navigate to http://localhost/restart.lsp. You should see the following being printed in the server's console window:

local -> global:        Closing app2
Hello World
function: 9b12e0
local -> global:        Hello World
local -> global:        function: 50ba108
local -> global:        Hello from mymodule

From the above printouts, we can see that app2 is terminated since the onunload function is run and then started again from the printouts in the .preload and mymodule.lua code. See the Mako Specific Features page to learn more about the functions for dynamically loading and terminating applications.

Discover More:

Whether you are a maker, a startup, or a large business, we've got you covered. Please send us an email if you have any questions or if you are unsure on what product to select. We are here to help you find the best solution, and we'd really like to help you with your hardware/software project challenges.


OPC-UA

OPC-UA Client & Server

An easy to use OPC UA stack that enables bridging of OPC-UA enabled industrial products with cloud services, IT, and HTML5 user interfaces.

Edge Controller

Edge Controller

Use our user programmable Edge-Controller as a tool to accelerate development of the next generation industrial edge products and to facilitate rapid IoT and IIoT development.

On-Premises IoT

On-Premises IoT Platform

Learn how to use the Barracuda App Server as your On-Premises IoT Foundation.

Embedded Web Server

Barracuda Embedded Web Server

The compact Web Server C library is included in the Barracuda App Server protocol suite but can also be used standalone.

WebSocket Server

Microcontroller Friendly

The tiny Minnow Server enables modern web server user interfaces to be used as the graphical front end for tiny microcontrollers. Make sure to check out the reference design and the Minnow Server design guide.

WebDAV Server

Network File System

Why use FTP when you can use your device as a secure network drive.

HTTP Client

Secure HTTP Client Library

PikeHTTP is a compact and secure HTTP client C library that greatly simplifies the design of HTTP/REST style apps in C or C++.

WebSocket Client

Microcontroller Friendly

The embedded WebSocket C library lets developers design tiny and secure IoT applications based on the WebSocket protocol.

SMTP Client

Secure Embedded SMTP Library

Send alarms and other notifications from any microcontroller powered product.

Crypto Library

RayCrypto C Library

The RayCrypto engine is an extremely small and fast embedded crypto library designed specifically for embedded resource-constrained devices.

Embedded PKI Service

Automatic SSL Certificate Management for Devices

Real Time Logic's SharkTrust™ service is an automatic Public Key Infrastructure (PKI) solution for products containing an Embedded Web Server.

Modbus

Modbus TCP client

The Modbus client enables bridging of Modbus enabled industrial products with modern IoT devices and HTML5 powered HMIs.

Posted in Tutorials