AWA Core

Initialization

The AWA application is represented by the Application type which should be extended for the final application to provide the modules and specific components of the final application.

The initialization of an AWA application is made in several steps represented by different procedures of the main Application type. The whole initialization is handled by the Initialize procedure which gets a first set of configuration properties and a factory to build specific component.

The Initialize procedure will perform the following steps:

  • It uses the factory to allocate the ASF lifecycle handler, the navigation handler, the security manager, the OAuth manager, the exception handlers.
  • It calls the Initialize_Components procedure to let the application register all the ASF components. These components must be registered before any configuration file is read.
  • It calls the Initialize_Config
  • It calls the Initialize_Servlets procedure to allow the application to register all the servlet instances used by the application.
  • It calls the Initialize_Filters procedure to allow the application to register all the servlet filter instances. The application servlets and filters must be registered before reading the global configuration file.
  • It loads the global application configuration by reading the awa.xml file as well as some application specific files. By reading this configuration, some global configuration is established on the servlets, filters, navigation rules. The list of XML files loaded during this step is controlled by the app.config configuration.
  • It calls the Initialize_Modules procedure so that all the application modules can be registered, configured and initialized. Each module brings its own component, servlet and filter. They are configured by their own XML configuration file.
  • It loads the module application configuration by reading the XML files described by the app.config.plugins configuration. This last step allows the application to setup and update the configuration of all modules that have been registered.

Configuration

The following global configuration parameter are defined:

Name Description
awa_url_scheme The application URL scheme to use when building URL.
#{empty app_url_scheme ? 'http://' : app_url_scheme}
awa_url_host The application URL host to use when building URL.
#{empty app_url_host ? 'localhost' : app_url_host}
awa_url_port The application TCP port to use when building URL.
#{empty app_url_port ? ':8080' : app_url_port}
app_url_base The application URL base to use when building URL.
#{empty app_url_base ? 'http://localhost:8080' : app_url_base}
app_oauth_url_base
http://localhost:8080
view.ext Defines the extension used for Ada Server Faces presentation pages.
.html
view.dir Defines a list of paths separated by ';' where the XHTML files are searched. The default searches for the 'web' directory in the application search paths.
#{fn:composePath(app_search_dirs,'web')}
content-type.default Defines the default content type for the file servlet.
text/plain
ado.queries.load Controls whether the database query definitions are loaded.
true
ado.queries.paths Defines a list of paths separated by ';' where the database query files are searched. The default searches for the 'db' directory in the application search paths.
#{fn:composePath(app_search_dirs,'db')}
ado.migrate.paths Defines a list of paths separated by ';' where the database migration files are searched. The default searches for the 'db/migrate' directory in the application search paths.
#{fn:composePath(app_search_dirs,'db/migrate')}
bundle.dir Defines a list of paths separated by ';' where the resource bundle files are searched. The default searches for the 'bundles' directory in the application search paths.
#{fn:composePath(app_search_dirs,'bundles')}
app.modules.dir Defines a list of paths separated by ';' where the module configuration files are searched. The default searches for the 'config' directory in the application search paths.
#{fn:composePath(app_search_dirs,'config')}

AWA Modules

A module is a software component that can be integrated in the web application. The module can bring a set of service APIs, some Ada beans and some presentation files. The AWA framework allows to configure various parts of a module when it is integrated in an application. Some modules are designed to be re-used by several applications (for example a mail module, a users module, ...). Other modules could be specific to an application. An application will be made of several modules, some will be generic some others specific to the application.

Registration

The module should have only one instance per application and it must be registered when the application is initialized. The module instance should be added to the application record as follows:

type Application is new AWA.Applications.Application with record
   Xxx       : aliased Xxx_Module;
end record;

The application record must override the Initialize_Module procedure and it must register the module instance. This is done as follows:

overriding
procedure Initialize_Modules (App : in out Application) is
begin
   Register (App    => App.Self.all'Access,
             Name   => Xxx.Module.NAME,
             URI    => "xxx",
             Module => App.User_Module'Access);
end Initialize_Modules;

The module is registered under a unique name. That name is used to load the module configuration.

Configuration

The module is configured by using an XML or a properties file. The configuration file is used to define:

  • the Ada beans that the module defines and uses,
  • the events that the module wants to receive and the action that must be performed when the event is posted,
  • the permissions that the module needs and how to check them,
  • the navigation rules which are used for the module web interface,
  • the servlet and filter mappings used by the module

The module configuration is located in the config directory and must be the name of the module followed by the file extension (example: module-name.xml or module-name.properties).

AWA Permissions

The AWA.Permissions framework defines and controls the permissions used by an application to verify and grant access to the data and application service. The framework provides a set of services and API that helps an application in enforcing its specific permissions. Permissions are verified by a permission controller which uses the service context to have information about the user and other context. The framework allows to use different kinds of permission controllers. The Entity_Controller is the default permission controller which uses the database and an XML configuration to verify a permission.

Declaration

To be used in the application, the first step is to declare the permission. This is a static definition of the permission that will be used to ask to verify the permission. The permission is given a unique name that will be used in configuration files:

with Security.Permissions;
...
package ACL_Create_Post is new Security.Permissions.Definition ("blog-create-post");

Checking for a permission

A permission can be checked in Ada as well as in the presentation pages. This is done by using the Check procedure and the permission definition. This operation acts as a barrier: it does not return anything but returns normally if the permission is granted. If the permission is denied, it raises the NO_PERMISSION exception.

Several Check operation exists. Some require no argument and some others need a context such as some entity identifier to perform the check.

with AWA.Permissions;
...
AWA.Permissions.Check (Permission => ACL_Create_Post.Permission,
                       Entity     => Blog_Id);

Configuring a permission

The AWA.Permissions framework supports a simple permission model The application configuration file must provide some information to help in checking the permission. The permission name is referenced by the name XML entity. The entity-type refers to the database entity (ie, the table) that the permission concerns. The sql XML entity represents the SQL statement that must be used to verify the permission.

<entity-permission>
  <name>blog-create-post</name>
  <entity-type>blog</entity-type>
  <description>Permission to create a new post.</description>
  <sql>
    SELECT acl.id FROM acl
    WHERE acl.entity_type = :entity_type
    AND acl.user_id = :user_id
    AND acl.entity_id = :entity_id
  </sql>
</entity-permission>

Adding a permission

Adding a permission means to create an ACL database record that links a given database entity to the user. This is done easily with the Add_Permission procedure:

with AWA.Permissions.Services;
...
AWA.Permissions.Services.Add_Permission (Session => DB,
                                         User    => User,
                                         Entity  => Blog);

Data Model

Queries

Queries

Name Description
check-entity-permission Get the permission for a user and an entity
remove-permission Delete the permission associated with a user and an object
remove-entity-permission Delete all the permission associated with an object
remove-user-permission Delete all the permission associated with a user

AWA Events

The AWA.Events package defines an event framework for modules to post events and have Ada bean methods be invoked when these events are dispatched. Subscription to events is done through configuration files. This allows to configure the modules and integrate them together easily at configuration time.

Declaration

Modules define the events that they can generate by instantiating the Definition package. This is a static definition of the event. Each event is given a unique name.

with AWA.Events.Definition;
...
package Event_New_User is new AWA.Events.Definition ("new-user");

Posting an event

The module can post an event to inform other modules or the system that a particular action occurred. The module creates the event instance of type Module_Event and populates that event with useful properties for event receivers.

with AWA.Events;
...
Event : AWA.Events.Module_Event;
Event.Set_Event_Kind (Event_New_User.Kind);
Event.Set_Parameter ("email", "harry.potter@hogwarts.org");

The module will post the event by using the Send_Event operation.

Manager.Send_Event (Event);

Receiving an event

Modules or applications interested by a particular event will configure the event manager to dispatch the event to an Ada bean event action. The Ada bean is an object that must implement a procedure that matches the prototype:

type Action_Bean is new Util.Beans.Basic.Readonly_Bean ...;
procedure Action (Bean : in out Action_Bean;
                  Event : in AWA.Events.Module_Event'Class);

The Ada bean method and object are registered as other Ada beans.

The configuration file indicates how to bind the Ada bean action and the event together. The action is specified using an EL Method Expression (See Ada EL or JSR 245).

<on-event name="new_user">
    <action>#{ada_bean.action}</action>
</on-event>

Event queues and dispatchers

The AWA.Events framework posts events on queues and it uses a dispatcher to process them. There are two kinds of dispatchers:

  • Synchronous dispatcher process the event when it is posted. The task which posts the event invokes the Ada bean action. In this dispatching mode, there is no event queue. If the action method raises an exception, it will however be blocked.

  • Asynchronous dispatcher are executed by dedicated tasks. The event is put in an event queue. A dispatcher task processes the event and invokes the action method at a later time.

When the event is queued, there are two types of event queues:

  • A Fifo memory queue manages the event and dispatches them in FIFO order. If the application is stopped, the events present in the Fifo queue are lost.

  • A persistent event queue manages the event in a similar way as the FIFO queue but saves them in the database. If the application is stopped, events that have not yet been processed will be dispatched when the application is started again.

Data Model

AWA Commands

The AWA.Commands package provides a simple framework with commands that allow to start, stop, configure and manage the web application. It is also possible to provide your own commands. The command framework handles the parsing of command line options, identification of the command to execute and execution of the selected command.

Command Usage

SYNOPSIS

driver [-v] [-vv] [-vvv] [-c config-file ] command [-k file ] [ -d dir ] [ -p password ] [--password password ] [--passfile file ] [--passenv name ] [--passfd fd ] [--passask] [--passcmd cmd ] [--wallet-key-file file ]

DESCRIPTION

The AWA.Commands.Drivers framework integrates the Ada Keystore support to access some sensitive configuration information such as passwords, database connection strings, API secret keys. The use of the Ada Keystore storage is optional. It is enabled when the -k _file_ option is specified. When such secure storage is used, a primary password to unlock the keystore is necessary. Passwords are retrieved using one of the following options:

  • by reading a file that contains the password,
  • by looking at an environment variable,
  • by using a command line argument,
  • by getting the password through the ssh-askpass(1) external command,
  • by running an external command,
  • by using a GPG private key,
  • by asking interactively the user for the password,
  • by asking through a network socket for the password.

When the Ada Keystore is used, it is global to all the applications that are registered in the Web server container. To allow to differentiate application specific configuration, each configuration parameter is prefixed by the application name.

To create and update the keystore file, it is necessary to use the akt(1) tool. The tool provides many commands for the creation, insertion, removal and update of content that is stored in the keystore file.

If the keystore file was locked by using GPG, it is not necessary to specify any specific option to unlock the keystore. All is needed is the availability of the gpg2(1) command with the private key to unlock the keystore.

The server global configuration file that is read with the -c config-file option can contain the following configuration:

Name Description
keystore-path The path to the global keystore file
keystore-masterkey-path The path of the file that contains the master keys
keystore-password-path The path of the file that contains the keystore password
gpg-encrypt When GPG is used, the GPG command to encrypt some content
gpg-decrypt When GPG is used, the GPG command to decrypt some content
gpg-list-keys When GPG is used, the GPG command to list the available GPG keys

OPTIONS

The following options are recognized by the command driver:

-V

Prints the application version.

-v

Enable the verbose mode.

-vv

Enable debugging output.

-vvv

Enable debugging output.

-c config-file

Defines the path of the global server configuration file.

-k file

Specifies the path of the keystore file to open.

-p password

The keystore password is passed within the command line. Using this method is convenient but is not safe.

--passenv envname

The keystore password is passed within an environment variable with the given name. Using this method is considered safer but still provides some security leaks.

--passfile path

The keystore password is passed within a file that is read. The file must not be readable or writable by other users or group: its mode must be r??------. The directory that contains the file must also satisfy the not readable by other users or group members, This method is safer.

--passfd fd

The keystore password is passed within a pipe whose file descriptor number is given. The file descriptor is read to obtain the password. This method is safer.

--passask

The keystore password is retrieved by the running the external tool ssh-askpass(1) which will ask the password through either KDE, Gnome or another desktop interactive application. The password is retrieved through a pipe that the driver sets while launching the command.

--passcmd cmd

The keystore password is retrieved by the running the external command defined in cmd. The command should print the password on its standard output without end of line. The password is retrieved through a pipe that the driver sets while launching the command.

--wallet-key-file file

Defines the path of a file which contains the wallet master key file.

COMMANDS

The start command

driver start [--management-port PORT] [--port PORT] [--connection COUNT] [--upload DIR] [--tcp-no-delay] [--daemon] [--keystore PATH]

The start command allows to start the server and all applications that are registered to the web container. When a keystore is specified, it is first unlocked by using one of the unlock mechanism (password, GPG, ...). Then, each application is configured by using its own configuration file and a subset of the keystore secured configuration. Once each application is configured, the web server container is started to listen to the TCP/IP port defined by the --port=PORT option. Applications are then started and they can serve HTTP requests through the web server container.

At the same time, a management port is created and used exclusively by the stop command to stop the web server container. The start command will wait on that management port for the stop command to be executed. The management port is configured by the --management=PORT option. The management port is local to the host and cannot be accessed remotely.

When the --daemon option is used, the server will first put itself in the background. This option is supported only under some Unix systems like GNU/Linux and FreeBSD and more generally every system where the daemon(3) C-library call is supported. On other systems, this option has no effect.

The --tcp-no-delay option is supported for recent version of Ada Web Server and configure the web server to use the TCP_NO_DELAY option of the TCP/IP stack (strongly recommended).

The --upload=DIR option indicates to the web server container the directory that can be used to store temporarily the uploaded files that the server receives.

The --connection=COUNT option controls the maximum number of active HTTP requests that the server can handle.

The setup command

driver setup [--management-port PORT] [--port PORT] [--connection COUNT] [--upload DIR] [--tcp-no-delay] [--daemon] [--keystore PATH] NAME

The setup command is very close to the start command but it starts the Setup Application to configure the application by using a web browser.

The stop command

driver stop [--management-port PORT] [--keystore PATH]

The stop command is used to inform a running web server container to stop. The management port is used to connect to the web server container and stop it. The management port is local to the host and cannot be accessed remotely.

The management port is configured with the --management-port=PORT option.

The migrate command

driver migrate [--application NAME] [--keystore PATH] [--execute]

The migrate command is provided to perform the database schema migration when the application or one of its module is upgraded and uses a new database schema. Each module is versionned and the migrate command handles the schema upgrade for each module version included and used by the application. It should be safe to execute this command multiple times since once a database schema is migrated the command will detect this and do nothing.

By default the SQL migration scripts are not executed and the command prints the list of SQL scripts that will be executed if the migration is executed.

The --execute option runs the SQL migration scripts.

The list command

driver list [--application NAME] [--keystore PATH] [--users] [--jobs] [--sessions] [--tables]

The list command is intended to help in looking at the application database and see some important information. Because the database is specific to each application, it may be necessary to indicate the application name by using the --application=NAME option.

The --tables option triggers the list of database tables with the number of entries they contain.

The --users option triggers the list of users that are registered in the application.

The --sessions option triggers the list of user connection sessions.

The --jobs option triggers the list of jobs that have been created and scheduled.

The info command

driver info [--application NAME] [--keystore PATH] [--long-lines]

The info command reports the current configuration used by the application. The configuration is extracted from the AWA default XML configuration files and can be overriden by the application specific configuration. The command allows to see what is the actual configuration. The configuration is printed with the configuration name and its associated value.

The list of configuration parameters are grouped in several categories:

  • Database configuration gives the configuration properties for the database configuration.
  • Server faces configuration lists the configuration use by the Ada Servlets and Ada Server Faces.
  • AWA Application lists the core AWA configuration properties.

Depending on whether a module is used by the application, a number of modules are listed with their configuration.

The --long-lines option triggers the list of database tables with the number of entries they contain.

Integration

The AWA.Commands framework is split in several generic packages that must be instantiated. The AWA.Commands.Drivers generic package is the primary package that must be instantiated. It provides the core command line driver framework on top of which the commands are implemented. The instantiation needs two parameter: the name of the application and the type of the web server container. When using Ada Web Server, the following instantiation example can be used:

with Servlet.Server.Web;
with AWA.Commands.Drivers;
...
package Server_Commands is
   new AWA.Commands.Drivers
      (Driver_Name    => "atlas",
       Container_Type => Servlet.Server.Web.AWS_Container);

The Driver_Name is used to print the name of the command when some usage or help is printed. The Container_Type is used to declare the web container onto which applications are registered and which provides the web server core implementation. The web server container instance is available through the WS variable defined in the Server_Commands package.

Once the command driver is instantiated, it is necessary to instantiate each command that you wish to integrate in the final application. For example, to integrate the start command, you will do:

with AWA.Commands.Start;
...
package Start_Command is new AWA.Commands.Start (Server_Commands);

To integrate the stop command, you will do:

with AWA.Commands.Stop;
...
package Stop_Command is new AWA.Commands.Stop (Server_Commands);

The instantiation of one of the command, automatically registers the command to the command driver.