Review Web Application: Creating a review

By Stephane Carrez

In previous tutorials we have seen how to create and setup the project, design the UML model to generate the Ada implementation and the database schema. In this tutorial we will see how to design the page to create a review, implement the operations to create and populate the database with the new review.

Adding the review creation form

We will start with the presentation layer by adding two pages in our web application. A first page will contain the list of reviews and the second page will contain a form to create or update a review.

AWA uses the Facelets technology to allow developers write and design the presentation layer of the web application. This technology is commonly used in J2EE applications. A page is represented by an XML file that contains HTML code, includes some stylesheets, Javascript files and makes the link between the presentation and the web application.

Adding pages

Dynamo provides at least two commands that help in adding presentation files. The add-page command adds a simple page that can be edited and filled with real content. We will use it for the creation of the page to display the list of reviews.

dynamo add-page reviews/list

The add-form command creates another template of page that includes an HTML form to let a user submit some data to the web application.

dynamo add-form reviews/edit-review

These two commands will create the following files and they can now be modified.

./web/reviews/list.xhtml
./web/reviews/edit-review.xhtml
./web/reviews/forms/edit-review-form.xhtml

The create review form

In Facelets, an HTML form is created by using the <h:form> component from the HTML JSF namespace. This component will generate the HTML form tag and it will also manage the form submission.

The ASF framework provides a set of widget components that facilitate the design of web application. The <w:inputText> component renders a title field with an HTML <label> and an HTML <input> text. We will use it to let the user enter the review title and the site URL being reviewed. The HTML <textarea> is provided by the JSF component <h:inputTextArea>. The review submit form is defined by the following XML extract:

<h:form xmlns:h="http://java.sun.com/jsf/html
  xmlns:w="http://code.google.com/p/ada-asf/widget">
  <h:inputHidden id='entity-id' value='#{review.id}' required='false'/>
  <w:inputText title='Title' value='#{review.title}'/>
  <w:inputText title='Site' value='#{review.site}'/>
  <h:inputTextArea rows='20' value='#{review.text}'/>
  <h:commandButton value='Save'
     action='#{review.save}'/>
</h:form>

Before closing the <h:form> component, we will put a <h:commandButton> that will render the form submit button.

How it works

Before going further, let's see how all this works. The principle below is exactly the same for a Java Server Faces application.

First, when the page is rendered the UEL expressions that it contains are evaluated. The #{review.title}, #{review.site} and #{review.text} are replaced by the content provided by the review object which is an Ada Bean provided by the Review_Bean tagged record.

When the page is submitted by the user, the input values submitted in the form are saved in the review bean, again by using the UEL expression. The <h:commandButton> action is then executed. This is also an UEL that indicates a method to invoke on the bean.

To sum up, the UEL makes the binding between the presentation layer in Facelets files and the Ada or Java beans.

The Ada Bean layer provides getter and setter to allow the UEL to retrieve and set values. For this, the Review_Bean tagged record implements two operations that are defined in the Bean interface:

overriding
function Get_Value (From : in Review_Bean;
                    Name : in String) return Util.Beans.Objects.Object;

overriding
procedure Set_Value (From : in out Review_Bean;
                    Name : in String;
                    Value : in Util.Beans.Objects.Object);

The Get_Value operation is called to retrieve one of the Ada Bean member attribute and the Set_Value operation is called during form submission to set the member attribute.

demo-awa-request-flow.png

Then the form button is pressed, the HTML form is submitted and received by the server. The <h:form> component identifies the form submission and each input component will validate the input fields. When everything has been validated, the <h:commandButton> component invokes the Save procedure that is declared as follows in the Review_Bean tagged record:

overriding
procedure Save (Bean : in out Review_Bean;
                Outcome : in out Ada.Strings.Unbounded.Unbounded_String);

In the Ada Bean layer, we have to call the business logic to perform the save operation.

The business logic part is provided by the Ada module whose initial skeleton was generated by Dynamo. That layer is responsible for defining how the data is created, retrieved and modified. As far as we are concerned, this is rather simple since we only have to verify the permission and save the review object within some transaction. In other modules, several objects may be envolved and more complex rules may be defined for the integrity and validity of these objects.

The last part of the architecture is the data model layer that was in fact generated by Dynamo from the UML model. It is responsible for loading and saving Ada objects into the database.

The Review_Bean type declaration

When we designed our UML model, we have created the Review_Bean UML class and gave that class the Bean stereotype. We also declared two operations (save and delete) on that class. With this definition, Dynamo has generated in the Atlas.Reviews.Models package the Review_Bean abstract type. This type is abstract because we have to implement the Save and Delete operations. These are the two operations that can be called by an action such as used by the <h:commandButton> component.

The Atlas.Reviews.Models package is a generated package and it must not be modified. To implement our Ada Bean, we will add the Review_Bean type in our own package: the Atlas.Reviews.Beans package.

For this the Review_Bean type will inherit from the Atlas.Reviews.Models.Review_Bean type and it will implement the required operations. The type declaration looks like this:

package Atlas.Reviews.Beans is
...
type Review_Bean is new Atlas.Reviews.Models.Review_Bean with record
   Module : Atlas.Reviews.Modules.Review_Module_Access := null;
end record;
...

The Review_Bean implementation

The Save and Delete procedure must be implemented and since the whole business logic is managed by the module layer, we just have to call the associated module procedure as follows:

overriding
procedure Save (Bean : in out Review_Bean;
                Outcome : in out Ada.Strings.Unbounded.Unbounded_String);
begin
   Bean.Module.Save (Bean);
end Save;

overriding
procedure Delete (Bean : in out Review_Bean;
                Outcome : in out Ada.Strings.Unbounded.Unbounded_String);
begin
   Bean.Module.Delete (Bean);
end Delete;

The Review_Bean creation

The AWA framework must be able to create the review bean instance when a page is processed. For this, there are three steps that are necessary:

  • we must define a create function whose role is to allocate the Review_Bean instance and return it. At the same time, the function can setup some pre-defined values for the object. The Dynamo tool has generated for us an example of such function so that there is nothing to do.
function Create_Review_Bean (Module : in Atlas.Reviews.Modules.Review_Module_Access)
   return Util.Beans.Basic.Readonly_Bean_Access is
   Object : constant Review_Bean_Access := new Review_Bean;
begin
   Object.Module := Module;
   return Object.all'Access;
end Create_Review_Bean;
  • the creation function must be registered in the AWA framework under a name that identifies the create function. Again, an example of this registration has been generated by Dynamo and we are going to use it as is.
Register.Register (Plugin => Plugin,
                 Name   => "Atlas.Reviews.Beans.Reviews_Bean",
                 Handler => Atlas.Reviews.Beans.Create_Review_Bean'Access);
  • the last step is the configuration step. In the module XML configuration file, we must declare the Ada bean name and indicate what create function must be called to create it. We will use the managed-bean XML declaration that comes from Java Server Faces. We can declare as many Ada beans as we want each of them with a different name.
  <managed-bean>
    <description>An example of a bean (change description and bean name)</description>
    <managed-bean-name>review</managed-bean-name>
    <managed-bean-class>Atlas.Reviews.Beans.Reviews_Bean</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
  </managed-bean>

When the UEL expression #{review.title} is used, the AWA framework looks for the Ada bean represented by review and identified by the managed-bean-name entry. It then calls the create function defined by the managed-bean-class. The Ada bean object is then stored either in the request context, a session context or an application context. This is defined by the managed-bean-scope entry. The request scope means that the Ada bean object is created once for each request. Concurrent page accesses will use their own Ada bean object instance. The session scope means that the Ada bean object is shared between requests on the same session. The application scope means that the Ada bean object is global to the application, shared by every request and every user.

Adding the module operations

Now, we must add two operations on the business logic to save a review and delete a review. The Dynamo code generator provides the add-module-operation command that will help us in this task. Let's run it:

dynamo add-module-operation reviews review Save
dynamo add-module-operation reviews review Delete

The first parameter is the name of the module where the new operation is added. This is the name of the module that was created by using the add-module operation. In our case, this is the reviews module.

The second parameter is the name of the database entity or database table if you prefer.

The add-module-operation command modifies the Ada module specification and body to define and implement the following operation:

package Atlas.Reviews.Modules is
...
procedure Save (Model  : in Review_Module;
                Entity : in out Atlas.Reviews.Models.Review_Ref'Class);
...

The object to save in the Review table is passed as parameter to the Save operation. The procedure body that was generated is rather simple but functional: it just saves the object in the database within a transaction. In many cases it is ready to use but you may also need to modify the operation to either change the implementation or even add new parameters.

Saving our review

Before saving our review entity object, we want to associate it with the current user

Add a comment

To add a comment, you must be connected. Login