2.2. The Model-View-Controller Design paradigm

DbForms implements the concepts of the Model-View-Controller design pattern [Gamma] which leads to the development of 3-tiered web-applications.

Figure 2.1. Architecture of DbForms

Architecture of DbForms

Like most applications and application frameworks, DbForms does not completely separate these three components. For instance, the Controller Servlet is the Controller Component of DbForms. Consider, however, the use of a hyperlink rendered by the user's web browser: clicking on it will trigger some operation hence implementing some controller functionality.

2.2.1. The Model: Database objects described by database metadata

The aim of DbForms is to perform operations on databases. The tables and views utilized by DbForms must be declared in an XML configuration file ( dbforms-config.xml), which will be parsed and evaluated at web application start-up time.

Example 2.1. Defining the model

  <?xml version="1.0" encoding="ISO-8859-1"?>
  <!DOCTYPE dbforms-config PUBLIC "http://www.dbforms.org/dtd/dbf_conf.dtd"> 
    
  <dbforms-config> 
    <table name="customer"> 1
      <field name="id" fieldType="int" isKey="true" /> 2 
      <field name="firstname" fieldType="char" /> 
      <field name="lastname"  fieldType="char" /> 
      <field name="address"   fieldType="char" />                    
    </table> 

    <table name="orders"> 3
      <field name="orderid" fieldType="int" isKey="true" /> 4 
      <field name="customerid" fieldType="int" isKey="true" autoInc="true" /> 
      <field name="date"       fieldType="char" /> 
      <field name="annotation" fieldType="char" /> 
      <field name="amount"     fieldType="int" /> 
    </table>
 
    <dbconnection name="jdbc/dbformstest" isJndi="true"/>
  </dbforms-config>
            
1 3

As shown, every table or view to be accessed by DbForms has to be declared inside a table element.

2 4

All relevant table fields need to be declared in field subelements nested in their respective table element. (There exists a tool for generating this XML data automatically by reading database metadata, see Chapter 5, DevGui for more information.)

2.2.1.1. Defining the logical model

In DbForms, it usually makes no difference whether a table element really represents a database table or a logical view defined in the RDBMS. The circumstances under which views may be used instead of simple tables depends entirely on the RDBMS being used. Before using views, check that the RDBMS supports them. When tables are mentioned in this document in connection to some capability, the same capability may also work with views or joined tables.

Please see the section on Chapter 13, Query Support for more information on how to use the DbForms configuration file dbforms-config.xml to specify flexible joins, dynamic queries (which are alternatives for views in the database) and aliases.

2.2.1.2. Defining physical access to the model

DbForms needs to be able to create connections to the database containing the tables and fields declared in the dbforms-config.xml file, The following subsections show three different examples of these connections.

Versions up to 1.1.2 restrict you to use only one database per Web-Application. 1.1.2pr1 and after support multiple databases in a single application. See Multiple Database Connections for more info on that.

2.2.1.2.1. Accessing the Application Servers connection pool via JNDI

Example 2.2. Defining the database connection (1)

                                                
  <dbconnection 
    name   = "java:comp/env/jdbc/dbformstest" 
    isJndi = "true"/>
              

In the example above DbForms assumes that the JNDI entry "jdbc/dbformstest" is configured correctly in the Application Server or Servlet engine's configuration (e.g. data-sources.xml) and takes all the connections it needs from that JNDI entry.

2.2.1.2.2. Using arbitrary connection pool

If you are not using JNDI in conjunction with a connection pool manager like Poolman, you have to tell DbForms which class it has to load in order to access the connection pool manager to be used. In addition, analogous to the JNDI key, you have to tell DbForms how the connection should be identified in the connection pool.

Example 2.3. Defining the database connection (2)

  <dbconnection
    name   = "jdbc:poolman://dbformstest"
    isJndi = "false"
    class  = "com.codestudio.sql.PoolMan"/>
              

In the example above DbForms assumes that the connection pool entry called jdbc:poolman://dbformstest is correctly configured in the associated connection pool managers properties file and takes all the connections it needs from that connection pool.

The full configuration of data sources (how many pooled instances to create, duration of invalidation timeouts, etc.) is not in the scope of this user's guide. Please refer to your Application Server or Servlet engine documentation for more details.

DbForms now makes is easier to use connection pooling with the Protomatter and Jakarta connection pools. See Chapter 20, Connection Support for more information on using these or other pools.

2.2.1.2.3. Using no connection pool

If you just want to test the functionality of DbForms and you do not care about speed (at the moment), then you might want to define a simple database connection as follows:

Example 2.4. Defining the database connection (3)

  <dbconnection
    name     = "jdbc:mysql://localhost/fashion"
    isJndi   = "false"
    conClass = "org.gjt.mm.mysql.Driver"
    username = "scott"
    password = "tiger"/>
              

Note: Many drivers pass in JDBC properties using the URL such as:

<dbconnection name = "jdbc:mysql://localhost/fashion?charSet=ISO-8859-1"

   ...
                            
                

If your driver does not allow this and you need to pass in a property, please see Section 20.1.4, “Setting JDBC Properties” for another way to set JDBC properties.

2.2.2. The View: JSP templates provided by the application developer

The view portion of a DbForms application is constructed using JSP technology. JSP files may contain static HTML elements as well as dynamic elements containing Java code (definitions, statements, expressions). For more information about JSP, refer to [JSP].

With release 1.1 of the JSP API, the concept of Custom tag libraries [Taglib] was introduced. Custom tags allow a developer to encapsulate even the most sophisticated Java code into an easy-to-use lightweight JSP tag. You may think of Tag libraries as a sort of advanced macro.

DbForms is, essentially, a collection of custom tags for placing data forms and data fields on JSP pages.

2.2.2.1. The structure of a DbForms View

Figure 2.2. Main components of a typical view

Main components of a typical view

2.2.2.2. The basic concepts of a DbForm

Each DbForms View JSP may have one or more root tags of the type dbform. Every dbform tag has to contain exactly 1 header tag, exactly 1 body tag and exactly 1 footer tag, in exactly that order.

Each of those tags may contain subelements such as data fields, input fields, action buttons, and, of course, plain HTML text and JSP code.

Header and footer tags are commonly used for titles of pages, for labeling tables, for placing action and navigation buttons, input fields to enter new data, etc.

Header and footer tags get evaluated only once.

The body tag is used for showing data rows coming from the database, and for providing the user with functionality to edit that data. How many times the body tag and its subelements get executed (i.e., evaluated and rendered) depends on the value of the maxRows attribute of the form element (and of course, on the number of rows actually stored in the table).

If maxRows is set to a number, the body gets executed up to that many times, limited by the number of rows in the database table.

If maxRows is set "*", the body gets executed an indefinite number of times, limited only by the number of rows in the database table. This should be used with caution lest the page become very large.

Nested forms

Every form may contain one or more nested subforms inside its body tag.

Figure 2.3. Example of a nested form

Example of a nested form

The orders form is nested within the body element of the customer form. The user will see one customer per page (because maxRows is set to 1) and all the orders (because maxRows = *) the customer has pending. The user may navigate through the list of customers by clicking the navigation buttons.

2.2.3. The Controller: Event parsing, dispatching and executing facilities

The Controller includes several components:

Controller-Servlet: this servlet is the single-point-of-entry for all incoming HTTP-requests.

EventEngine: a kind of assistant to the Controller-servlet. It focuses on filtering requests for WebEvents and instantiates them.

WebEvent Objects: all Objects derived from this abstract super-class have the ability to initialize themselves by reading a given request. These events get executed either by the controller directly or by the View.

The following example should give you a better picture of what the controller does and how it interacts with other components:

  1. A user presses the delete row button on a DbForms application.
  2. The user's browser submits data via an HTTP POST command to the Controller servlet.
  3. The Controller-Servlet delegates the incoming request to the EventEngine which determines the main or primary event, (the event the user explicitly triggered by clicking a button). Secondary implicit events may also be generated. For example, under some circumstances, automatic updates of all changed input fields of all data rows will be generated.

  4. The EventEngine component parses the request and determines the kind of action the user wants to execute.
  5. The EventEngine creates the appropriate WebEvent (in this case, a DeleteEvent) and delegates the Request object to this newly created WebEvent which initializes itself and returns the event back to the Controller-Servlet.
  6. The Controller tells the event, if it is a Database Event, to execute its built-in operation. Other types of event, e.g. Navigation Events, are delegated to the appropriate View component.
  7. The controller invokes EventEngine again to check if there are additional Secondary (implicit) events to be executed. If so, the appropriate WebEvents objects are created and executed in the same manner as the main event described above.
  8. The controller determines the View component to which the request should be forwarded.
  9. If the View component is a JSP page containing DbForms tags, those tags will search for Navigation Events to be executed. Once these are completed, a response will be generated.
  10. This response is then rendered by the user's web browser.