15-Aug-03 (Created: 15-Aug-03) | More in 'Howto'

05.02 Painting web pages using Aspire : A step by step approach

Summary

This document will describe the process of painting a web page using Aspire/J2EE. The document will start with a simple web page and proceeds to present web pages of increased complexity. I will talk about property files, aspire tags among other things

Pre-requisities

Read the 30 minute guide available in the howto section. The 30 minute guide has introduced the reader to the following

1. Download and Install tomcat
2. Download and install aspire
3. Point tomcat to aspire template
4. Define a sample database
5. Create a simple page definition
6. Create a simple html page
7. Retrieve the html page through aspire

Looking back at what is installed

If you had installed and configured Aspire with Tomcat you will have a web application that is ready to run. A single tomcat can run multiple web applications on the same port. For example on "machine1:port" (localhost:8080) Tomcat can run a number of applications. These applications are identified with what is called an application prefix. For example in the URL "http://localhost:8080/aspire", The word "aspire" is identifying an application. For instance "http://localhost:8080/app2" is pointing to an application called "app2" on port 8080. As per the instructions in the 30 minute guide our focus here is the web application called "aspire" identified by "http://localhost:8080/aspire".

When you fire off the above url, Tomcat will automatically load a file called "index.html" that is kept in the directory identified by "aspire". This web application prefix to the physical application directory mapping is done in the tomcat configuration file called server.xml.

Retrieving a simple static html page from the aspire web app

Assume that in a sub-directory called "apptemplate\mydir" there is an html file called "plain-page.html". You can retrieve this page as following

http://localhost:8080/mydir/plain-page.html

This will load the plain-page.html. This page is displayed directly by tomcat. Aspire does not play any role in the display of this page. This html page is called "plain" because it does not have any dynamic content. The page is not a jsp page, the page does not have any aspire tags, etc.

These kinds of static pages are useful for testing or providing static links to other parts of the web application. These pages does not go through the Aspire security model.

Retrieving a simple dynamic html page from the aspire web app

Let us raise the bar a little and let us retrieve a dynamic html page that can have a few tags. Let us call this dynamic-page1.html. Also assume that this is available in "apptemplate\mydir".

To retrieve this page you need the following three lines in a properties file

	# 0). Create a URL name for your dynamic page
	MyPageURL=aspire:\\mydir\\dynamic-page1.html

	# 1). Identify a data definition for your page
	MyPageURL.formhandlerName=MyPageDataDef

	#Identify a pre-built java part that knows how to get the data for that definition
	request.MyPageDataDef.form_handler.class_request.className=com.ai.htmlgen.DBHashTableFormHandler1

By removing the comments from the above your definition will look like

	MyPageURL=aspire:\\mydir\\dynamic-page1.html
	MyPageURL.formhandlerName=MyPageDataDef
	request.MyPageDataDef.form_handler.class_request.className=com.ai.htmlgen.DBHashTableFormHandler1
These three lines will allow you retrieve the dynamic page. Here is how you retrieve it from a browser
	http://localhost:8080/servlet/DisplayServlet?url=MyPageURL

Let us now consider what is in the "dynamic-page1.html". I am excluding html tags in this file because of editing reasons.

	Sample html page
	Demonstrates simple tags
You will see this page as it is. Because Aspire has processed the page and wrote it back as it is as it does not contain any dynamic content so far. Let us introduce a dynamic element in the page.
	Sample html page
	Demonstrates simple tags
	{{arg1}} is my name
The dynamic element in this page is identified between {{ and }}. In this example the dynamic element is "arg1". Now if you fire off the above url you will see
	Sample html page
	Demonstrates simple tags
	 is my name
You will see arg1 disappear from the page. This is because there is no value for arg1 available on the server side to Aspire. Let us make this available using the following URL
	http://localhost:8080/servlet/DisplayServlet?url=MyPageURL&arg1=satya
Now you will see the html page displayed as follows
	Sample html page
	Demonstrates simple tags
	satya is my name

Aspire's data sources for dynamic content

Because the above page is obtained through Aspire, Aspire uses varieties of data sources to look for the specified element "arg1". These sources include

  • URL arguments
  • Properties file(s)
  • User Session
  • Database, Files, Java Classes etc.

You have already seen how URL arguments are used as a datasource. Let us see how we can extend the html page to demonstrate an argument substitution from a properties file

	Sample html page
	Demonstrates simple tags
	{{arg1}} is my name
	{{arg2}} is my occupation

We know that "arg1" came from the URL. Now to make "arg2" available, make an entry like the following in one of your properties files

	arg2=Software

Now when you fire off http://localhost:8080/servlet/DisplayServlet?url=MyPageURL&arg1=satya you will see the html page turn to

	Sample html page
	Demonstrates simple tags
	satya is my name
	Software is my occupation

The URL arguments are also called "request" level variables. Because these variables exist for the life of a single web page request. The variables like "arg2" kept in the properties files are called applciation level variables. Because these variables do not change for the life of an application. Another type of variable is called a session variable. These variables exist for the amount of time a user logs in and uses a website. Sessions are slightly advanced for this document. So I will not discuss session variables in detail here. Before explaining how we can substitute arguments from a database let me take a detour and revisit the three lines in the properties file that allowed us to retrieve this dynamic page.

Revisiting the 3 lines

How does Aspire know to search these datasources to satisfy these dynamic arguments. The java class responsible for doing this search is "com.ai.htmlgen.DBHashTableFormHandler1". This is the class that is mentioned in the third line. This class implements an interface called "IFormHandler". This interface answers questions like "Given a key called 'arg1', give me back its value".

The above class is one implementation. If you were to write your own java class then you can interprest "arg1" as you choose. But the default implementation will search the URL arguments, followed by the properties files.

	# 1). Create a URL name for your dynamic page
	MyPageURL=aspire:\\mydir\\dynamic-page1.html

	# 2). Identify a data definition for your page
	MyPageURL.formhandlerName=MyPageDataDef

	# 3). Identify a pre-built java part that knows how to get the data for that definition
	request.MyPageDataDef.form_handler.class_request.className=com.ai.htmlgen.DBHashTableFormHandler1

The first line tells Aspire what html file to load in response to an incoming url called "MyPageURL". The second line tells where to find the data for the url "MyPageURL" by looking for a key "MyPageURL.formHandlerName". This key in the exampel is pointing to "MyPageDataDef". Line three specifies "MyPageDataDef" pointing to a java class that implements IFormHandler.

These lines typically do not change. So it is largely a cut/paste exercise as you define new pages to aspire. This basic definition gets increasingly sophisticated as you start gathering data declratively from multiple sources with a variety of options.

How to get data from a database

Let us modify the html page one more time so that we can introduce some arguments from a database

	Sample html page
	Demonstrates simple tags
	{{arg1}} is my name
	{{arg2}} is my occupation
	{{arg3}} is my preferred language
	{{arg4}} is another language I work in

Our goal is to get "arg3" and "arg4" from a database table. To make this happen we need to modify the properties file as follows

	# Basic page definition
	MyPageURL=aspire:\\mydir\\dynamic-page1.html
	MyPageURL.formhandlerName=MyPageDataDef
	request.MyPageDataDef.form_handler.class_request.className=com.ai.htmlgen.DBHashTableFormHandler1

	# Maindata request (Key/value pairs outside of loops)
	request.MyPageDataDef.maindatarequest.classname=com.ai.db.DBRequestExecutor2
	request.MyPageDataDef.maindatarequest.db=
	request.MyPageDataDef.maindatarequest.stmt=\
		select preferred_language as arg3 \
			,alternate_language as arg4 \
		from languages_table \
		where coder="satya"

We have added three more lines. These three additional lines are identified by "MyPageDataDef.maindatarequest". This data set is called "maindatarequest" because the values coming out of this select statement will populate the dynamic keys on the web page that are outside of any loop structures such as tables and list boxes. The keys inside loop structures are called loop data sets. Essentially every web page as a result has one main data set and 'n' number of loop data sets. I will cover the loop data sets in the next section

With this change if you now issue the URL http://localhost:8080/servlet/DisplayServlet?url=MyPageURL&arg1=satya, you will see the html page changed to the following:

	Sample html page
	Demonstrates simple tags
	satya is my name
	Software is my occupation
	java is my preferred language
	c# is another language I work in

Where "java" and "c#" are retrieved as a single row from the database table "languages_table".

Introducing database parts

So far we have introduced two java classes as part of our page processing these are

com.ai.htmlgen.DBHashTableFormHandler1
com.ai.db.DBRequestExecutor2

These classes in Aspire are called "requestexecutors" or "parts". These are reusable assets that are pre-coded into aspire. The number at the end of the classname indicates the version number of the part. The previous releases of the parts are usually available in the same jar file to provide backward compatibility.

In the "maindatarequest" above "DBRequestExecutor2" will execute the select statement and return the row to Aspire as an object of type "IDataCollection. This means any java class that is capable of returning this interface can take the place of "DBRequestExecutor2" in this properties file. This is important because you can have parts that can read from flat files (com.ai.data.RowFileReader) and parts that can read using stored procedures (com.ai.db.StoredProcExecutor2), and so on and so forth. These are called compaitble part list. The compatible part list for DBRequestExecutor2 are

  • com.ai.db.DBRequestExecutor2 (basic database read/write)
  • com.ai.db.StoredProcExecutor2 (basic stored proc read/write)
  • com.ai.data.RowFileReader (file reads)
  • com.ai.db.DBPreTranslateArgsMultiRequestExecutor (sequential transactionally multi statement execution)
  • com.ai.db.DBLoopRequestExecutor (transactional execution in a loop of database statements)

Depending on the part the part specification may change. To know the specification of a part you have to refer to the documentation for that part. As an example let us see how the above page definition changes when we use a file reader part as opposed to a database part

	# Basic page definition
	MyPageURL=aspire:\\mydir\\dynamic-page1.html
	MyPageURL.formhandlerName=MyPageDataDef
	request.MyPageDataDef.form_handler.class_request.className=com.ai.htmlgen.DBHashTableFormHandler1

	# Maindata request (Key/value pairs outside of loops)
	request.MyPageDataDef.maindatarequest.classname=com.ai.data.RowFileReader
	request.MyPageDataDef.maindatarequest.filename=aspire:\\mydir\\mydata.txt

See how the file reader takes ".filename" as part of its specification as opposed to a database alias name and a database statement.

On the web site of Aspire a good bit of sample properties files are included. If you look in these files for these part names you will see how to specify these parts.

How to use multiple selects for your web page

Let us continue with our focus on painting the page. This time let us assume the "arg3" and "arg4" comes from two select statements. Let us also assume we need another argument called "arg5" (just for the demo). Let us now see how we can use three select statements to satisfy the "maindatarequest".

	# Basic page definition
	MyPageURL=aspire:\\mydir\\dynamic-page1.html
	MyPageURL.formhandlerName=MyPageDataDef
	request.MyPageDataDef.form_handler.class_request.className=com.ai.htmlgen.DBHashTableFormHandler1

	# Maindata request (Key/value pairs outside of loops)
	request.MyPageDataDef.maindatarequest.classname=com.ai.db.DBPreTranslateArgsMultiRequestExecutor
	request.MyPageDataDef.maindatarequest.db=
	request.MyPageDataDef.maindatarequest.request.1=Arg3SelectStmt
	request.MyPageDataDef.maindatarequest.request.2=Arg4SelectStmt
	request.MyPageDataDef.maindatarequest.request=Arg5SelectStmt

	# Individual request definitions
	request.Arg3SelectStmt.classname=com.ai.db.DBRequestExecutor2
	request.Arg3SelectStmt.db=
	request.Arg3SelectStmt.stmt=\
		select preferred_language as arg3 \
		from languages_table \
		where coder="satya"

	request.Arg4SelectStmt.classname=com.ai.db.DBRequestExecutor2
	request.Arg4SelectStmt.db=
	request.Arg4SelectStmt.stmt=\
		select alternate_language as arg4 \
		from languages_table \
		where coder="satya"

	request.Arg5SelectStmt.classname=com.ai.db.DBRequestExecutor2
	request.Arg5SelectStmt.db=
	request.Arg5SelectStmt.stmt=\
		select alternate_language as arg5 \
		from languages_table \
		where coder="satya"

Now the "maindatarequest" points to a "multirequestexecutor" part as opposed to a simple "dbrequestexecutor". The specification of "multirequestexecutor" allows to specify a series of individual requests that gets executed in sequence. The output from the select statements are combined to form a single row and delivered to the web page. Both "dbrequestexecutor" and "multirequestexecutors" are compatible and completely interchangeable. This is the flexibity of Asprie in general. And you will see this pattern reused lot of places.

How to use stored procedures on your page

For MicrosoftSQLserver issuing a stored proc is no different than issuing a SQL statement. But this is quite different for Oracle. For this reason a new part is created just for Oracle called "StoredProcExecutor2". This is how you invoke a stored proc instead of an sql statement on your page.

	# Basic page definition
	MyPageURL=aspire:\\mydir\\dynamic-page1.html
	MyPageURL.formhandlerName=MyPageDataDef
	request.MyPageDataDef.form_handler.class_request.className=com.ai.htmlgen.DBHashTableFormHandler1

	# Maindata request (Key/value pairs outside of loops)
	request.MyPageDataDef.maindatarequest.classname=com.ai.db.StoredProcExecutor2
	request.MyPageDataDef.maindatarequest.db=
	request.MyPageDataDef.maindatarequest.stmt=\
		{call oracle-pkg.storedproc1(?,'satya')}

The convention is that the first argument to the proc should be a question mark identifying an oracle result set. The subsequent arguments will be input arguments. The only output from the stored proc that is allowed is a single result set coming back as the first argument. If you need more sophistication you can write your own part. For SQL Server the above definition will look like

	# Basic page definition
	MyPageURL=aspire:\\mydir\\dynamic-page1.html
	MyPageURL.formhandlerName=MyPageDataDef
	request.MyPageDataDef.form_handler.class_request.className=com.ai.htmlgen.DBHashTableFormHandler1

	# Maindata request (Key/value pairs outside of loops)
	request.MyPageDataDef.maindatarequest.classname=com.ai.db.DBRequestExecutor2
	request.MyPageDataDef.maindatarequest.db=
	request.MyPageDataDef.maindatarequest.stmt=\
		{call oracle-pkg.storedproc1('satya')}

DBRequestExecutor2 is used for stored procedures for sql server, because calling a proc is same as calling SQL. The stored proc in this case should return a single result set as part of an internal select.

How to use loop structures on a page

Let us continue the story by introducing two loops in to the page. One loop will tell us the benefits of java. This loop is called "java-benefits-loop". One loop will tell us the benefits of c#. This loop is called "c#-benefits-loop".

	Sample html page
	Demonstrates simple tags
	{{arg1}} is my name
	{{arg2}} is my occupation
	{{arg3}} is my preferred language
	{{arg4}} is another language I work in

	Here are the benefits of java
	<!--RLF_TAG BGN_LOOP java-benefits-loop -->
	{{benefit}}
	<!--RLF_TAG END_LOOP java-benefits-loop -->

	Here are the benefits of c#
	<!--RLF_TAG BGN_LOOP c#-benefits-loop -->
	{{benefit}}
	<!--RLF_TAG END_LOOP c#-benefits-loop -->

Let us begin by going into the meaning of the "java-benefits-loop" tag. The tag is saying to look for a collection of rows (These can come from a select statement or a file etc.) and for each row repeat the html segment that is between the start and end tags. Let us now see how this loop name "java-benefits-loop" is tied to a select statement in the properties file.


	# 1). Basic page definition
	MyPageURL=aspire:\\mydir\\dynamic-page1.html
	MyPageURL.formhandlerName=MyPageDataDef
	request.MyPageDataDef.form_handler.class_request.className=com.ai.htmlgen.DBHashTableFormHandler1

	# 2). Maindata request (Key/value pairs outside of loops)
	request.MyPageDataDef.maindatarequest.classname=com.ai.db.DBRequestExecutor2
	request.MyPageDataDef.maindatarequest.db=
	request.MyPageDataDef.maindatarequest.stmt=\
		{call oracle-pkg.storedproc1('satya')}

	# 3). java-benefits-loop representing a select statement
	request.MyPageDataDef.java-benefits-loop.class_request.className=com.ai.htmlgen.GenericTableHandler6
	request.MyPageDataDef.java-benefits-loop.query_request.className=com.ai.db.DBRequestExecutor2
	request.MyPageDataDef.java-benefits-loop.query_request.db=as400dbAlias
	request.MyPageDataDef.java-benefits-loop.query_request.stmt=\
		select benefit 
		from language_benefits_table
		where language="java"

	# 4). java-benefits-loop representing a select statement
	request.MyPageDataDef.c#-benefits-loop.class_request.className=com.ai.htmlgen.GenericTableHandler6
	request.MyPageDataDef.c#-benefits-loop.query_request.className=com.ai.db.DBRequestExecutor2
	request.MyPageDataDef.c#-benefits-loop.query_request.db=as400dbAlias
	request.MyPageDataDef.c#-benefits-loop.query_request.stmt=\
		select benefit 
		from language_benefits_table
		where language="c#"

	# 5). The following line is useful if you directly retrieve the data
	MyPageDataDef.loopNames=c#-benefits-loop,java-benefits-loop

The loop name is tied to the data definition first. Together their combination points to a select statement through the database part called DBRequestExecutor2. There is an additional class that gets mentioned in case of a loop. Typically this additional class is called a table handler. The role of this class is a bit subtle. This class is responsible for adapting the output of a select statement to the needs of an html page. For example you want to do totals on some columns. Some times you want to remove columns and sometimes add columns. Sometimes you want to page through data 5 rows at a time. Sometimes you want to read through rows one by one. Some times you want to read them all into memory. These variations are provided by these adapter/strategy classes. The compatible table handlers at the time of build 18 are

  • com.ai.htmlgen.GenericTableHandler6 (Sequential onetime topdown display)
  • com.ai.htmlgen.RandomTableHandler6 (Random access, paging )

Eitherway as you go designing page after page, it is a question of copying these definitions and changing the appropriate page related names. The java classes gets changed very infrequently. So the page defintion for each page looks very similar except for the select statements.

How to populate html tables

Using the loop structures above it is not difficult to imagine how one can create an html table quite easily. Here is an example.

(table)
	(tr)
		(th)col1(/th)
		(th)col2(/th)
		(th)col3(/th)
	(/tr)
(!--RLF_TAG BGN_LOOP loopname --)
	(tr)
		(td){{column1}}(/td)
		(td){{column2}}(/td)
		(td){{column3}}(/td)
	(/tr)
(!--RLF_TAG END_LOOP loopname --)
(/table)

I have replaced typical html element tags with brackets for the sake of this writing. Please use the angular brackets when you use the code.

How to populate list boxes

Same thing applies to list boxes. Just put one of the option tags in a loop element.

How to pass arguments to a select statements

	# Basic page definition
	MyPageURL=aspire:\\mydir\\dynamic-page1.html
	MyPageURL.formhandlerName=MyPageDataDef
	request.MyPageDataDef.form_handler.class_request.className=com.ai.htmlgen.DBHashTableFormHandler1

	# Maindata request (Key/value pairs outside of loops)
	request.MyPageDataDef.maindatarequest.classname=com.ai.db.DBRequestExecutor2
	request.MyPageDataDef.maindatarequest.db=
	request.MyPageDataDef.maindatarequest.stmt=\
		select preferred_language as arg3 \
			,alternate_language as arg4 \
		from languages_table \
		where coder={string-arg1.quote}
		and anotherarg={int-arg2}

The argument names are kept inside of { and }. If the type of argument is string or date then append the arg name with ".quote" to indicate that the value needs to be placed inside of quotes. If the value is missing then this instruction will subtitute a null value without quotes. If the arg type is an integer or a non-string type then use it as it is. These arguments will be taken from the incoming url.

In addition the columns from the maindatarequest can be used in loop requests as argument names.

Commonly used java classes in a page definition

  • com.ai.htmlgen.DBHashTableFormHandler1 (Represents the data definition for an entire page)
  • com.ai.htmlgen.GenericTableHandler6(Adapts database loop output to a web loop)
  • com.ai.db.DBRequestExecutor2 (basic database read/write)
  • com.ai.db.StoredProcExecutor2 (basic stored proc read/write)
  • com.ai.htmlgen.AITransform6 (Class responsible for parsing/processing the page)

As you create page definitions you will be specifying these java classes to accomplish your work. How do you know which versions go together. Take a look at the release notes that comes with each build. The release notes have recommendations as to what is the compatible set of these java classes.

For instance all of the above classes are compatible as of build 18.

Other useful java classes in a page definition

  • com.ai.data.RowFileReader (file reads)
  • com.ai.htmlgen.RandomTableHandler6(Provides paging)
  • com.ai.db.DBPreTranslateArgsMultiRequestExecutor (sequential transactionally multi statement execution)
  • com.ai.db.DBLoopRequestExecutor (transactional execution of a loop of database statements)

Where to go next

  • Working with databases in Aspire
  • Individual part documentation
  • How to use if tags on your page
  • How to use JSP to paint your page
  • How to use XSLT to paint your page
  • How can I set content headers for a page
  • How to can I get a page as Excel
  • How to page data using Aspire
  • How to work with arguments to select statements