Counters Module
The counters
module defines a general purpose counter service that
allows to associate counters to database entities. For example it
can be used to track the number of times a blog post or a wiki page
is accessed. The counters
module maintains the counters in a table
on a per-day and per-entity basis. It allows to update the full counter
in the target database entity table.
Integration
The Counter_Module
manages the counters associated with database entities.
To avoid having to update the database each time a counter is incremented,
counters are kept temporarily in a Counter_Table
protected type.
The table contains only the partial increments and not the real counter
values. Counters are flushed when the table reaches some limit, or,
when the table is oldest than some limit. Counters are associated with
a day so that it becomes possible to gather per-day counters.
The table is also flushed when a counter is incremented in a different day.
To be able to use the Counters
module, you will need to add the
following line in your GNAT project file:
with "awa_counters";
An instance of the Counter_Module
must be declared and registered in the
AWA application. The module instance can be defined as follows:
with AWA.Counters.Modules;
...
type Application is new AWA.Applications.Application with record
Counter_Module : aliased AWA.Counters.Modules.Counter_Module;
end record;
And registered in the Initialize_Modules
procedure by using:
Register (App => App.Self.all'Access,
Name => AWA.Counters.Modules.NAME,
Module => App.Counter_Module'Access);
Configuration
The counters
module defines the following configuration parameters:
Name | Description |
---|---|
counters.counter_age_limit | The maximum age limit in seconds for a pending counter increment to stay in the internal table. When a pending counter reaches this age limit, the pending counter increments are flushed and the table is cleared. The default is 5 minutes. |
300 | |
counters.counter_limit | The maximum number of different counters which can be stored in the internal table before flushing the pending increments to the database. When this limit is reached, the pending counter increments are flushed and the table is cleared. |
1000 |
Counter Declaration
Each counter must be declared by instantiating the Definition
package.
This instantiation serves as identification of the counter and it defines
the database table as well as the column in that table that will hold
the total counter. The following definition is used for the read counter
of a wiki page. The wiki page table contains a read_count
column
and it will be incremented each time the counter is incremented.
with AWA.Counters.Definition;
...
package Read_Counter is
new AWA.Counters.Definition
(AWA.Wikis.Models.WIKI_PAGE_TABLE, "read_count");
When the database table does not contain any counter column, the column field name is not given and the counter definition is defined as follows:
with AWA.Counters.Definition;
...
package Login_Counter is
new AWA.Counters.Definition (AWA.Users.Models.USER_PAGE_TABLE);
Sometimes a counter is not associated with any database entity. Such counters are global and they are assigned a unique name.
with AWA.Counters.Definition;
...
package Start_Counter is
new AWA.Counters.Definition (null, "startup_counter");
Incrementing the counter
Incrementing the counter is done by calling the Increment
operation.
When the counter is associated with a database entity, the entity
primary key must be given. The counter is not immediately incremented
in the database so that several calls to the Increment
operation will
not trigger a database update.
with AWA.Counters;
...
AWA.Counters.Increment (Counter => Read_Counter.Counter, Key => Id);
A global counter is also incremented by using the Increment
operation.
with AWA.Counters;
...
AWA.Counters.Increment (Counter => Start_Counter.Counter);
Ada Bean
The Counter_Bean
allows to represent a counter associated with some database
entity and allows its control by the <awa:counter>
HTML component.
To use it, an instance of the Counter_Bean
should be defined in a another
Ada bean declaration and configured. For example, it may be declared
as follows:
type Wiki_View_Bean is new AWA.Wikis.Models.Wiki_View_Info
with record
...
Counter : aliased Counter_Bean
(Of_Type => ADO.Objects.KEY_INTEGER,
Of_Class => AWA.Wikis.Models.WIKI_PAGE_TABLE);
end record;
The counter value is held by the Value
member of Counter_Bean
and
it should be initialized programatically when the Ada bean instance
is loaded (for example through a load
action).
The Counter_Bean
needs to know the database entity to which it
is associated and its Object
member must be initialized.
This is necessary for the <awa:counter>
HTML component to increment
the associated counter when the page is displayed.
Below is an extract of such initialization:
procedure Load
(Bean : in out Wiki_View_Bean;
Outcome : in out Ada.Strings.Unbounded.Unbounded_String) is
begin
...
Bean.Counter.Value := Bean.Get_Read_Count;
ADO.Objects.Set_Value (Bean.Counter.Object, Bean.Get_Id);
end Load;
The Stat_List_Bean
allows to retrieve the list of counters per day for
a given database entity. It needs a special managed bean configuration
that describes the database entity type, the counter name and
SQL query name.
The example below from the Wikis Module declares the bean
wikiPageStats
. The database entity is awa_wiki_page
which is the
name of the database table that holds wiki page. The SQL query
to retrieve the result is page-access-stats
.
<description>The counter statistics for a wiki page</description>
<managed-bean-name>wikiPageStats</managed-bean-name>
<managed-bean-class>AWA.Counters.Beans.Stat_List_Bean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>entity_type</property-name>
<property-class>String</property-class>
<value>awa_wiki_page</value>
</managed-property>
<managed-property>
<property-name>counter_name</property-name>
<property-class>String</property-class>
<value>read_count</value>
</managed-property>
<managed-property>
<property-name>query_name</property-name>
<property-class>String</property-class>
<value>page-access-stats</value>
</managed-property>
</managed-bean>
A typical XHTML view that wants to use such bean, should call the load
action at beginning to load the counter statistics by running the SQL
query.
<f:view contentType="application/json; charset=UTF-8"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<f:metadata>
<f:viewAction action='#{wikiPageStats.load}'/>
</f:metadata>
{"data":[<h:list value="#{wikiPageStats.stats}"
var="stat">["#{stat.date}", #{stat.count}],</h:list>[0,0]]}
</f:view>
HTML components
The <awa:counter>
component is an Ada Server Faces component that
allows to increment and display easily the counter. The component
works by using the Counter_Bean
Ada bean object which describes
the counter in terms of counter definition, the
associated database entity, and the current counter value.
<awa:counter value="#{wikiPage.counter}"/>
When the component is included in a page the Counter_Bean
instance
associated with the EL value
attribute is used to increment the counter.
This is similar to calling the AWA.Counters.Increment
operation
from the Ada code.
Data model
The counters
module has a simple database model which needs two tables.
The Counter_Definition
table is used to keep track of the different
counters used by the application. A row in that table is created for
each counter declared by instantiating the Definition
package.
The Counter
table holds the counters for each database entity
and for each day. By looking at that table, it becomes possible
to look at the daily access or usage of the counter.