This page describes the site DTD. In actuality, the master document for the site schema (DTD is just one way of defining an XML schema) is site.qjml, which is a Quick 3 document. But for the purposes of writing a site XML document, that detail is not important.
Site tests are written as documents that conform to the test DTD. A site test can be run simply and easily by invoking the helper class agonism.dovetail.test.Suite
String userID = request.getParameter("userID");Now, suppose it is necessary to change the name of the userID parameter to userHandle. The JSP page will still compile; only at run-time will the error be manifested. And, even more troublesome, there is no easy way to write automated tests that exercise JSP pages so it takes a lot of effort to write automated tests that will catch a problem like this one. Using Dovetail, the userID parameter would be declared in the site XML document as:
<page name="getUserInfo"> <parameter name="userID" required="true"> <type><string/></type> </parameter> ... </page>And it would be accessed in the JSP page like this:
GetUserInfoPage pg = UserSite.instance(request, response, out).getUserInfoPage(); // We know that the cast to String is safe b/c userID is declared as a string in // the site document. String userID = (String)pg.getUserIDParameter().getValue();In this case, if userID is changed to userHandle, the JSP page will no longer compile! The Java compiler has caught this error for us.
There are many other similar examples that could be given. They would all serve to demonstrate the 2 primary reasons for writing JSP pages against generated Java classes:
agonism.dovetail.util.DoveTail2Java
utility. Here is an example:
c:\myproject> java agonism.dovetail.util.DoveTail2Java my-site.xml c:\java\dovetail\schema\site.qjml my-schema.xml c:\java\dovetail\schema\schema.qjml c:\java\dovetail\xsl outputYou can run
DoveTail2Java
with no arguments to get simple usage information.
[package].[name]Site
, where the [package] and [name] strings are provided by the package and name attributes of the site element in the site XML document. For the Survey Site, the Site Class is named dovetail.example.survey.site.SurveySite
.
The Site class has methods for accessing each of the pages that are defined on it.
Each generated Site class is a subclass of agonism.dovetail.site.Site.
Each generated Site class is a subclass of agonism.dovetail.site.Page.
get[query-result@name]
, and it returns the appropriate Java data type according to the data type of the query-result. For example:
value of query-result@name attribute | Method name | Dovetail data type | Method return type |
---|---|---|---|
id | getId | int | int |
ID | getID | int | int |
name | getName | string | java.lang.String |
price | getPrice | float | double |
firstName | getFirstName | string | java.lang.String |
first-name | getFirstName | string | java.lang.String |
creationDate | getCreationDate | date | java.util.Date |
data | getData | blob | byte[] |
Each generated Query class is a subclass of agonism.dovetail.site.Query. Each time the Query#next() method is invoked, the underlying result set is advanced, and the values returned by the
get
methods are updated. The following example illustrates various behaviors of the Query class:
SurveyAskPage pg = SurveySite.instance(request, response, out).getSurveyAskPage(); SurveyQuery qrySurvey = pg.getSurveyQuery(); if ( !qrySurvey.next() ) { System.err.println("No survey was found"); return; } String id = qrySurvey.getId(); // Free the query. If a result from this query is accessed later in the code // execution, the query will be re-evaluated qyrSurvey.free(); // Execute the query again // Here we are using the fact that the Query is automatically advanced to the first // record if we call a 'get' method without first calling 'next()' String sameID = qrySurvey.getId(); if ( !id.equals(sameID) ) System.err.println("Dovetail bug! Ids should be the same"); ResponsesQuery qryResponses = pg.getResponsesQuery(); // The following loop will print out each 'responseText' result which was found by the // 'responses' query for ( int i = 0;qryResponses.next(); ++i ) { System.out.println(i + ") " + qryResponses.getResponseText()); }
c:\temp\gen-java\> java org.apache.jasper.JspC -d . -die -uriroot path/to/your/jsp/root -webapp path/to/your/jsp/root
site
site
element is the root of any Dovetail site document (hereafter referred to as the 'site document'). The purpose of the site document is to capture as much declarative information about the web site as possible, so that the Java and JSP code for the site can be made as simple as possible, and so that the site is as robust and testable as possible.
Name | Description | Required | Default Value |
---|---|---|---|
name |
name .
| true | |
package |
agonism.dovetail.example.survey . The Java code generator will append '.site' to the package attribute. | true |
Name | Description | MinOccurs | MaxOccurs |
---|---|---|---|
location |
| 0 | * |
page |
Each page represents a page (typically a JSP or HTML page) in the web site. Each page should correspond to a file on the filesystem. That page, if it has dynamic content (e.g. it is a JSP page) will reference the objects defined in the corresponding page element in the site document. | 0 | * |
location
location
s and page
s. Each location is like a directory, and each page corresponds to a web page. Locations do not have much special behavior other than organizing pages. The Java code generator appends the location to the package name of each class which is generated for the site, so grouping pages into locations helps to organize the generated Java classes and avoid naming conflicts. For example, you cannot have more than one query
element with the same name
, unless they are in different locations.
Name | Description | Required | Default Value |
---|---|---|---|
name |
location must be the same as the directory on the file system.
| true |
Name | Description | MinOccurs | MaxOccurs |
---|---|---|---|
location |
| 0 | * |
page |
| 0 | * |
page
Name | Description | Required | Default Value |
---|---|---|---|
name |
page must be the same as the file on the file system. All the pages in the site document must have unique names. The reason for this rule is that all the links between pages are declared using link elements, which must be able to uniquely reference the pages.
| true | |
content-type |
content-type of the page must be the same as the extension of the file on disk, otherwise the URLs generated by Dovetail will not be correct.
| true |
Name | Description | MinOccurs | MaxOccurs |
---|---|---|---|
title |
| 1 | 1 |
comment |
| 0 | 1 |
parameter |
| 0 | * |
query |
| 0 | * |
link |
| 0 | * |
form |
form element is used to declare all HTML input elements that will be part of the form, and to define where their values will come from. Dovetail makes it easy to link form inputs to database columns, and automatically handles rendering required inputs differently from optional ones, use of type="PASSWORD" inputs where appropriate, and saving user input when there is a form validation error and the form is presented to the user again to be corrected. Dovetail also automatically writes out hidden values, and warns the page author in the Dovetail log file when a form element is declared in the site document but not used on the JSP page.
| 0 | * |
parameter
parameter
element. Declaring the page parameters has many benefits:
A parameter may be supplied to a page either in the page request URL as a query parameter, or by an HTML form submission.
Name | Description | Required | Default Value |
---|---|---|---|
name |
| true | |
isRequired |
isRequired is true, Dovetail ensures that all link s that reference this page have provided a value for the parameter. | true |
Name | Description | MinOccurs | MaxOccurs |
---|---|---|---|
type |
| 0 | 1 |
default |
| 0 | 1 |
type
literal-value
, session-value
, form-element
, and parameter
elements. These values are returned as their java.lang.Object
values (Integer
, Double
), rather than as primitive values (int
, double
). Some generated methods, such as for Query objects, convert them to primitive types.
Name | Description | MinOccurs | MaxOccurs |
---|---|---|---|
int |
| 0 | 1 |
float |
| 0 | 1 |
string |
| 0 | 1 |
date |
java.util.Date
| 0 | 1 |
boolean |
| 0 | 1 |
blob |
| 0 | 1 |
url |
url data type would have special behavior, but as of the time of this writing it behaves just like a string
| 0 | 1 |
default
parameter
or query-result
. One or more child elements may be included. The first one which returns a non-empty value is used as the default.
Name | Description | MinOccurs | MaxOccurs |
---|---|---|---|
link |
| 0 | * |
literal-value |
| 0 | * |
session-value |
| 0 | * |
query-value |
query-result defined somewhere on the same page. | 0 | * |
parameter-value |
parameter . | 0 | * |
link
page
s referred to by links are valid. It also checks to ensure that the link specifies a value for each required parameter
on the page.
Here's an example of a link to a page which shows the results of a survey.
<link name="results"> <page-ref page="survey-results"/> <url-parameter name="surveyID"> <parameter-value>surveyID</parameter-value> </url-parameter> </link>This link would be used in a JSP page according to the following example:
<% SurveyAskPage pg = SurveySite.instance(request, response, out).getSurveyAskPage(); %> Click <a href="<%= pg.getResultsLink() %> to see the results of this survey.Links can also be used along with the
redirect
and link-value
elements to automatically redirect a user to a new page after a form has finished processing. The following fragement will automatically send the user to the survey results page.
<form name="survey-respond"> ... < form-element in which the user enters her choice --> <submit name="Submit"> ... < the user's action should be sent to a server-side bean for processing. <action> <redirect><link-value ref="results"/></redirect></action> </submit> </form>These examples are taken from the Survey Site example.
Name | Description | Required | Default Value |
---|---|---|---|
name |
page according to the pattern get<Name>Link(). For instance, the accessor for the link named results will be getResultsLink(). The name of a link must be unique to its page.
| true | |
toSelf |
page on which it is declared. This is an easy way to define a link which returns the user back to the page they are already on, including all the query parameters that were passed to the page. For instance, suppose that a page requires that the user be logged in. If the user is not logged in, they should be sent to a "login" page, and after that they should return. This may be implemented by defining a parameter on the "login" page named "returnToURL", and passing the value of a "toSelf" link as the value of that parameter. After the user logs in, they are return to the URL as specified by the "returnToURL" parameter. Here is an example of the declaration of such a link:
<link name="login"> <page-ref page="login"/> <url-parameter name="returnToURL"> <link toSelf="true"/> </url-parameter> </link> | false |
query
query
element is the fundamental way of supplying data to a page. The source of the data returned by a query may currently be either a SQL query-string
or a custom-query
Java object.
The interface to a
query
is similar to a JDBC ResultSet : there are 0 or more records, each of which has a number of accessible fields. The records are accessed by scrolling a cursor through the query
. The values which are accessible on each record are defined by query-result
s.
When Dovetail is evalutaing a
query
, it starts by instantiating the object which will execute the query. SQL queries are implemented with the class agonism.dovetail.site.QueryString. You may also implement your own custom-query
objects. The query object is then used to instantiate a agonism.dovetail.site.IQueryStatement. Then, Dovetail iterates through each of the query-parameter
elements in order. For each query-parameter
, Dovetail invokes the IQueryStatement#setParameter
method. Once all of the parameters have been set, the query is executed.
Name | Description | Required | Default Value |
---|---|---|---|
name |
query within its location . In the context of different locations, query names may be re-used. This requirement is to ensure that the Java classes generated for the queries do not have any naming conflicts. | true | |
defaultTableName |
query-result s. Each query-result may have an explicit type , or it may be bound to a column in a database table. When a query-result is bound to a database column, its data type is the same as the data type of the column. The column to which a query-result is bound is determined by its tableName and columnName attributes. The defaultTableName attribute can be used to provide a default value of the tableName attribute for all the query-result s in the query .
| false | empty string (no table) |
Name | Description | MinOccurs | MaxOccurs |
---|---|---|---|
query-result |
query . Successive values for the query-result are made available as the cursor advances through the records.
| 0 | * |
query-parameter |
| 0 | * |
body |
| 1 | 1 |
query-result
query
.
Each
query-result
may have an explicit data type
, or it may be bound to a column in a database table. When it is bound to a database column, its data type is the same as the data type of that column. The column to which a query-result
is bound is determined by its tableName and columnName attributes. If the tableName is not specified, the defaultTableName attribute of the query
is used instead. If no type
is specified, and the query-result
is not bound to a table column, or the table column does not exist, Dovetail will generate an error message when the Java code for the query is being generated.
Name | Description | Required | Default Value |
---|---|---|---|
name |
query-value to refer to the query-result .
| true | |
tableName |
query-result is bound. The defaultTableName attribute on query can be used to specify a default table name that applies to all its query-result s.
| false | |
columnName |
query 's defaultTableName) to which this query-result is bound. If a value for this attribute is specified, then either the tableName or defaultTableName must also be specified. If the tableName is specified, and no columnName is given, then the name of the query-result is used as the column name. Thus, if the name of the column is the same as the name of the query-result , it is not necessary to specify the columnName.
| false |
Name | Description | MinOccurs | MaxOccurs |
---|---|---|---|
type |
query-result . If this element is not present, the query-result should be bound to a table column with the columnName, tableName, and query .defaultTableName attributes.
| 0 | 1 |
body
query
. The body
of the query determines how the query results will actually be obtained. Exactly one of the query-string
and custom-query
child elements must be specified.
Name | Description | MinOccurs | MaxOccurs |
---|---|---|---|
query-string |
query-parameter s will then be used to set the values of the query parameters (if any), and the query will then be passed to the database and used to create a ResultSet. See the PreparedStatement JavaDoc for a description of how to write a SQL query with query parameters. If you do use query parameters, you must have exactly the same number of query-parameter elements in the query as there are question marks ('?') in the query string.
The content of the query-string should be the SQL string. Be aware that you will need to use XML entities in place of characters which have special meaning in XML. In particular:
SELECT * FROM Employees WHERE salary < 10000Should be written as: <body> <query-string>SELECT * FROM Employees WHERE salary < 10000</query-string> </body> | 0 | 1 |
custom-query |
| 0 | 1 |
custom-query
body
of a query
with a custom Java object. The Java class named in the className attribute should implement the interface agonism.dovetail.site.ICustomQuery.
Name | Description | Required | Default Value |
---|---|---|---|
className |
query is executed, an instance of this class will be created and used.
| true |
query-parameter
query
parameter. If the query is implemented with a query-string
, there must be the same number of query-parameter
elements as there are query parameters in the SQL string. If the query is a custom-query
, the behavior of extra or undefined query-parameter
s is up to the implementation.
Exactly one child element which defines the value bound to the
query-parameter
must be specified.
Name | Description | MinOccurs | MaxOccurs |
---|---|---|---|
literal-value |
| 0 | 1 |
session-value |
| 0 | 1 |
query-value |
query-result defined somewhere on the same page. | 0 | 1 |
parameter-value |
parameter . | 0 | 1 |
query-value
query-result
. As the page loops through a query
, the query-value in bound to the current value of the query-result. For instance, suppose a page contains the following:
<query name="ids"> <query-result name="id"/> <query-result name="name"/> ... [ query body ] ... </query> <form name="choose-id" <form-element name="id"> <options><query-value query="ids" result="id"/></options> </form-element> </form>The following JSP page fragement would render a
radio
input for each id/name pair returned by the query:
<%@ taglib uri="/dovetail-taglibs" prefix="dt" %> <% ChooseIdForm form = pg.getChooseIdForm(); IdsQuery query = pg.getIdsQuery(); while ( query.next() ) { %> <dt:radio formElement="<%= form.getIdElement() %>" /><%= query.getName() %><br> <% } %>This example makes use of the Dovetail
dt:radio
JSP Tag.
Name | Description | Required | Default Value |
---|---|---|---|
query |
query which will provide the value | true | |
result |
query-result which will provide the value | true |
form
form
consists of form-element
s which describe the form inputs, and submit
elements which are rendered as submit buttons in the HTML page. Each submit
can have a different series of action
s.
Name | Description | Required | Default Value |
---|---|---|---|
name |
form-value . The name of a form must be unique within its location .
| true | |
defaultTableName |
query-result s, each form-element may be bound to a database column in order to specify its data type. The defaultTableName attribute provides a default value for the tableName attribute of the form-element s in the form.
| false |
Name | Description | MinOccurs | MaxOccurs |
---|---|---|---|
form-element |
form-element is rendered into the HTML page as a FORM INPUT. | 0 | * |
submit |
submit buttons. Each of these will typically be rendered in the JSP page as a button that the user can push.
|
form-element
form-element
describes a form input that will be rendered into the HTML page. For example, an input to register a new user might be declared as:
<form name="register" defaultTableName="RegisteredUser"> <form-element name="firstName"/> ... <!-- other form-element declarations --> </form>And it would be rendered by the Dovetail Text taglib as:
<input type="text" name="firstName">The
form-element
may have an explicit type
, or it may be bound to a column in a database table. When a form-element
is bound to a database column, its data type is the same as the data type of the column. The column to which a form-element
is bound is determined by its tableName and columnName attributes. The defaultTableName attribute can be used to provide a default value of the tableName attribute for all the form-element
s in the form
. If the tableName is specified, and no columnName is given, then the name of the form-element
is used as the column name. Thus, if the name of the column is the same as the name of the form-element
, it is not necessary to specify the columnName.
Only one of the
link
, literal-value
, session-value
, query-value
, or parameter-value
child elements may be present. If present, it specifies a default value for the form element that will be rendered to the HTML page. The combination of one of these elements with the hidden="true" attribute renders a specific value into the HTML page as a hidden form input.
Name | Description | Required | Default Value |
---|---|---|---|
name |
form-value to refer to the form-element .
| true | |
hidden |
| false | false |
password |
form-element will be rendered as type="password". The form-element is also rendered as a password input if it is bound to a database column whose @isPassword attribute is 'true'.
| false | false |
tableName |
form-element is bound. The defaultTableName attribute on form can be used to specify a default table name that applies to all its form-element s.
| false | |
columnName |
form-element is bound.
| false |
Name | Description | MinOccurs | MaxOccurs |
---|---|---|---|
type |
type is required.
| 0 | 1 |
link |
| 0 | 1 |
literal-value |
| 0 | 1 |
session-value |
| 0 | 1 |
query-value |
query-result defined somewhere on the same page. | 0 | 1 |
parameter-value |
parameter . | 0 | 1 |
options |
options element can be used to define a query-value which provides this list of options.
| 0 | 1 |
submit
submit
element in a form
will typically be rendered in the JSP page as a button that the user can push. The generated Java class for the Form will have a method named get<Name>Submit
, which returns a agonism.dovetail.site.Submit object, whose render method may be used to render the HTML submit button. This process will look something like the following example:
SurveyRespondForm form = pg.getSurveyRespondForm(); ... // render form elements <% form.getSubmitSubmit().render("Submit"); %> <% form.renderEnd(); %>
Name | Description | Required | Default Value |
---|---|---|---|
name |
form . | true |
Name | Description | MinOccurs | MaxOccurs |
---|---|---|---|
action |
action s which result when the submit button is pushed. A submit button which has no action s will POST back to the same JSP page which rendered the form. If there is more than one action, they are each executed sequentially as long as they are successful. Success is determined by the return value from the IAction#process method. |
action
action
element implements some action that is taken by an application when the user pushes a submit
button. An action is typically a Java object which implements the IAction interface. This interface contains only one method, process, which must be implemeted by the action
implementor.
Each
action
element must contain either a action-bean
or submit
element as its only child.
Name | Description | MinOccurs | MaxOccurs |
---|---|---|---|
action-bean |
action-bean identifies a Java object that implements the action . | ||
redirect |
redirect action is a built-in action that redirects the user to a new page. The redirect element is also interesting in that it suggests how additional stock actions can be built into Dovetail which can reduce the amount of Java coding necessary to build a JSP web site. |
action-bean
action
. The Java class identified by the class-name attribute must implement the IAction interface.
Name | Description | Required | Default Value |
---|---|---|---|
class-name |
action . | true |
redirect
redirect
element must have exactly one child element which identifies the page to which the user will be redirected.
Name | Description | MinOccurs | MaxOccurs |
---|---|---|---|
link-value |
| 0 | 1 |
literal-value |
| 0 | 1 |
session-value |
| 0 | 1 |
query-value |
query-result defined somewhere on the same page. | 0 | 1 |
parameter-value |
parameter . | 0 | 1 |
options
choice
element may be used for the form-element
that will be used to record the user's choice.
<query name="responses" defaultTableName="SurveyResponse"> <query-result name="id"/> <query-result name="responseText"/> <query-parameter> <parameter-value>surveyID</parameter-value> </query-parameter> <body> <query-string>SELECT * FROM SurveyResponse WHERE surveyID = ? ORDER BY id ASC</query-string> </body> </query> <form name="survey-respond"> <form-element name="responseID" tableName="SurveyResponse" columnName="id"> <options> <query-value query="responses" result="id"/> </options> </form-element> ... <!-- submit elements and form actions --> </form>Then, the JSP page would render this form element according to the following example:
<%@ taglib uri="/dovetail-taglibs" prefix="dt" %> <% SurveyAskPage pg = SurveySite.instance(request, response, out).getSurveyAskPage(); SurveyRespondForm form = pg.getSurveyRespondForm(); ResponsesQuery qryResponses = pg.getResponsesQuery(); %> ... <% while ( qryResponses.next() ) { %> <dt:radio formElement="<%= form.getResponseIDElement() %>" /><%= qryResponses.getResponseText() %><br> <% } %>
Name | Description | MinOccurs | MaxOccurs |
---|---|---|---|
query-value |
query-result which will supply the valid choices for the form-input .
| 1 | 1 |
parameter-value
parameter
. If a value for the parameter has been explicitly provided via the URL query string or a form input element, that value will be bound to the parameter-value. Otherwise, the default value of the parameter will be used.
parameter-value
s are very useful for:
query-parameter
sform-element
surl-parameter
s<parameter name="surveyID" required="true"> <type><int/></type> </parameter> <query name="survey" defaultTableName="Survey"> <query-result name="requireLogin"/> <query-result name="questionText"/> <query-parameter> <parameter-value>surveyID</parameter-value> </query-parameter> <body> <query-string>SELECT * FROM Survey Where id = ?</query-string> </body> </query> <link name="results"> <page-ref page="survey-results"/> <url-parameter name="surveyID"> <parameter-value>surveyID</parameter-value> </url-parameter> </link>In this example, which is taken from the SurveySite example, the
surveyID
parameter serves 2 funtions: it is used as a query parameter to return the text of the survey question, and it is also used to populate the surveyID url-parameter
in a link to the survey-results page.
The body content (#PCDATA, in DTD terms) should be the name of a
parameter
of the same page
.
session-value
session-value
can be used to retrieve this value and use it in a page
. For example, the user may be using a form to store a user preference on the server database. A session-value
can be used to make sure that the userID value is available to the server-side bean which processes the form:
<form name="update-preference" defaultTableName="Preferences"> <form-element name="key"/> <form-element name="value"/> <form-element name="userID" hidden="true"> <session-value key="userID"/> </form-element> </form>
Name | Description | Required | Default Value |
---|---|---|---|
key |
| true |
Name | Description | MinOccurs | MaxOccurs |
---|---|---|---|
type |
type is ommitted, the session-value will be treated as a String.
| 0 | 1 |
form-value
form-element
. Note that the form-value element will only produce sensible results if it is not used until after the form has been submitted. The most useful scenario for using a form-value is:
link
which has a url-parameter whose value is a form-value
form-value
is used to populate the surveyID url-parameter
for the survey-results page. Assume there is a query called surveys which returns the surveyIDs.
<link name="survey-results"> <page-ref page="survey-results"/> <url-parameter name="surveyID"> <form-value form="choose-survey" element="surveyID"/> </url-parameter> </link> <form name="choose-survey"> <form-element name="surveyID"> <options> <query-value query="surveys" result="surveyID"/> </options> </form-element> <submit name="submit"> <action> <redirect> <link-value ref="survey-results"/> </redirect> </action> </submit> </form>
Name | Description | Required | Default Value |
---|---|---|---|
form |
form which will provide the value | true | |
element |
form-element which will provide the value | true |
literal-value
literal-value
elements are typically used to provide values for hidden form-element
s, and to supply default values for page parameter
s.
For example, a default surveyID of
'0'
could be specified for the survey-ask page:
<parameter name="surveyID" required="true"> <type><int/></type> <default> <literal-value><literal-text>0</literal-text></literal-value> </default> </parameter>Note that even though the default data type of the
literal-value
is string
, the parameter
will coerce the value to an int
.
Name | Description | MinOccurs | MaxOccurs |
---|---|---|---|
type |
type of the value. The default type is string . | 0 | 1 |
literal-text |
| 1 | 1 |
link-value
link
.
Name | Description | Required | Default Value |
---|---|---|---|
ref |
link defined on the same page . | true |