This tutorial has three steps:
- First the definition of the database query,
- The implementation of the Ada review list bean,
- The writing of the XHTML facelet presentation file.
Step 1: Database query to list the reviews
Let's start with the database query that we will use to retrieve the reviews.
Since we need to access the list of reviews from the XHTML files, we will map the SQL query result
to a list of Ada Beans objects. For this,
an XML query mapping is created to
tell how to map the SQL query result into some Ada record. The XML query mapping is then processed
by Dynamo to generate the Ada Beans implementation. The XML query mapping
is also read by AWA to get the SQL query to execute.
A template of the XML query mapping can be added to a project by using the dynamo
add-query command. The first parameter is the module name (reviews)
and the second parameter the name of the query (list). The command will generate the file
db/reviews-list.xml.
dynamo add-query reviews list
The generated XML query mapping is an example of a query. You can replaced it or update it
according to your needs. The first part of the XML query mapping is a class declaration
that describes the type to represent each row returned by our query. Within the class,
a set of property definition describes the class attributes with their type and name.
<query-mapping package='Atlas.Reviews.Models'>
<class name="Atlas.Reviews.Models.List_Info" bean="yes">
<comment>The list of reviews.</comment>
<property type='Identifier' name="id">
<comment>the review identifier.</comment>
</property>
<property type='String' name="title">
<comment>the review title.</comment>
</property>
...
</class>
</query-mapping>
Following the class declaration, the query declaration describes a query
by giving it a name and describing the SQL statement to execute. By having the SQL statement
separate and external to the application, we can update, fix and tune the SQL without rebuilding
the application. The Dynamo code generator will use
the query declaration to generate a query definition that can be referenced and used from
the Ada code.
The SQL statement is defined within the sql XML entity. The optional
sql-count XML entity is used to associate a count query that can be used for the pagination.
We want to display the review with the author's name and email address.
The list will be sorted by date to show the newest reviews first. The SQL to execute
is the following:
<query-mapping package='Atlas.Reviews.Models'>
...
<query name='list'>
<comment>Get the list of reviews</comment>
<sql>
SELECT
r.id,
r.title,
r.site,
r.create_date,
r.allow_comments,
r.reviewer_id,
a.name,
e.email,
r.text
FROM atlas_review AS r
INNER JOIN awa_user AS a ON r.reviewer_id = a.id
INNER JOIN awa_email AS e ON a.email_id = e.id
ORDER BY r.create_date DESC
LIMIT :first, :last
</sql>
<sql-count>
SELECT
count(r.id)
FROM atlas_review AS r
</sql-count>
</query>
</query-mapping>
The query has two named parameters represented by :first and :last.
These parameters allow to paginate the list of reviews.
The complete source can be seen in the file: db/reviews-list.xml.
Once the XML query is written, the Ada code is generated by Dynamo by reading the UML
model and all the XML query mapping defined for the application. Dynamo merges all the definitions into the target Ada packages and generates the Ada code in the src/model directory. You can use the generate
make target:
make generate
or run the following command manually:
dynamo generate db uml/atlas.zargo
From the List_Info class definition, Dynamo generates the
List_Info tagged record. The record contains all the data members described in
the class XML entity description. The List_Info represents one row returned
by the SQL query. The attributes of the List_Info can be accessed from the
XHTML files by using UEL expression and the property name defined for each attribute.
To describe the list of rows, Dynamo generates the List_Info_Beans package which instantiates the
Util.Beans.Basic.Lists generic package. This provides an Ada vector for the
List_Info type and an Ada bean that gives access to the list.
package Atlas.Reviews.Models is
...
type List_Info is new Util.Beans.Basic.Readonly_Bean with record
...
package List_Info_Beans is
new Util.Beans.Basic.Lists (Element_Type => List_Info);
package List_Info_Vectors renames List_Info_Beans.Vectors;
subtype List_Info_List_Bean is List_Info_Beans.List_Bean;
subtype List_Info_Vector is List_Info_Vectors.Vector;
Query_List : constant ADO.Queries.Query_Definition_Access;
...
end Atlas.Reviews.Models;
The generated code can be seen in src/model/atlas-reviews-models.ads.
Step 2: The review list bean
In order to access the list of reviews from the XHTML facelet file, we must create an Ada bean
that provides the list of reviews. This Ada bean is modelized in the UML model and we define:
- A set of attributes to manage the review list pagination (page, page_size, count)
- An Ada bean action that can be called from the XHTML facelet file (load)
The Review_List_Bean tagged record will hold the list of reviews for us:
package Atlas.Reviews.Beans is
...
type Review_List_Bean is new Atlas.Reviews.Models.Review_List_Bean with record
Module : Atlas.Reviews.Modules.Review_Module_Access := null;
Reviews : aliased Atlas.Reviews.Models.List_Info_List_Bean;
Reviews_Bean : Atlas.Reviews.Models.List_Info_List_Bean_Access;
end record;
type Review_List_Bean_Access is access all Review_List_Bean'Class;
end Atlas.Reviews.Beans;
We must now implement the Load operation that was described in the UML model
and we are going to use our list query. For this, we use the ADO.Queries.Context to setup the query to retrieve the list of reviews.
A call to Set_Query indicates the query that will be used. Since that query needs two
parameters (first and last), we use the Bind_Param operation to
give the two values. The list of reviews is then retrieved easily by calling the
Atlas.Reviews.Models.List operation that was generated by Dynamo.
package body Atlas.Reviews.Beans is
...
overriding
procedure Load (Into : in out Review_List_Bean;
Outcome : in out Ada.Strings.Unbounded.Unbounded_String) is
Session : ADO.Sessions.Session := Into.Module.Get_Session;
Query : ADO.Queries.Context;
Count_Query : ADO.Queries.Context;
First : constant Natural := (Into.Page - 1) * Into.Page_Size;
Last : constant Positive := First + Into.Page_Size;
begin
Query.Set_Query (Atlas.Reviews.Models.Query_List);
Count_Query.Set_Count_Query (Atlas.Reviews.Models.Query_List);
Query.Bind_Param (Name => "first", Value => First);
Query.Bind_Param (Name => "last", Value => Last);
Atlas.Reviews.Models.List (Into.Reviews, Session, Query);
Into.Count := ADO.Datasets.Get_Count (Session, Count_Query);
end Load;
end Atlas.Reviews.Beans;
Review list bean creation
The AWA framework must be able to create an instance of the Review_List_Bean type.
For this, we have to declare and implement a constructor function that allocates an instance of the
Review_List_Bean type and setup some pre-defined values. When the instance is returned,
the list of reviews is not loaded.
package body Atlas.Reviews.Beans is
...
function Create_Review_List_Bean (Module : in Atlas.Reviews.Modules.Review_Module_Access)
return Util.Beans.Basic.Readonly_Bean_Access is
Object : constant Review_List_Bean_Access := new Review_List_Bean;
begin
Object.Module := Module;
Object.Reviews_Bean := Object.Reviews'Access;
Object.Page_Size := 20;
Object.Page := 1;
Object.Count := 0;
return Object.all'Access;
end Create_Review_List_Bean;
end Atlas.Reviews.Beans;
The constructor function is then registered in the Atlas.Reviews.Modules package within
the Initialize procedure. This registration allows to give a name for this constructor
function and be able to specify it in the managed-bean bean declaration.
package body Atlas.Reviews.Modules is
...
overriding
procedure Initialize (Plugin : in out Review_Module;
App : in AWA.Modules.Application_Access;
Props : in ASF.Applications.Config) is
begin
...
Register.Register (Plugin => Plugin,
Name => "Atlas.Reviews.Beans.Review_List_Bean",
Handler => Atlas.Reviews.Beans.Create_Review_List_Bean'Access);
end Initialize;
end Atlas.Reviews.Modules;
Review list bean declaration
The managed-bean XML declaration associates a name to a constructor function that will be called when the name is needed.
The scope of the Ada bean is set to request so that a new instance is created for
each HTTP GET request.
<managed-bean>
<description>The list of reviews</description>
<managed-bean-name>reviewList</managed-bean-name>
<managed-bean-class>Atlas.Reviews.Beans.Review_List_Bean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
Step 3: Listing the reviews: the XHTML facelet presentation file
To load the reviews to be displayed we will use a JSF 2.2 view action.
The review list page has a parameter page that indicates the page number to be displayed.
The f:viewParam allows to retrieve that parameter and configure the reviewList
Ada bean with it. Then, the f:viewAction defines the action that will be executed
after the view parameters are extracted, validated and passed to the Ada bean. In our case, we will
call the load operation on our reviewList Ada bean.
<f:metadata>
<f:viewParam id='page' value='#{reviewList.page}' required="false"/>
<f:viewAction action="#{reviewList.load}"/>
</f:metadata>
To summarize, the reviewList Ada bean is created, then configured for the pagination
and filled with the current page content by running our SQL query.
The easy part is now to render the list of reviews. The XHTML file uses the
<h:list> component to iterate over the list items
and render each of them. At each iteration, the <h:list> component initializes the
Ada bean review to refer to the current row in the review list. We can then access
each attribute defined in the XML query mapping by using the property name of that attribute.
For example review.title returns the title property.
<h:list var="review" value="#{reviewList.reviews}">
<div
Add a comment
To add a comment, you must be connected. Login