Exercise 1: Connecting a Script.aculo.us Autocomplete Field to a MySQL Database (40 minutes)
This exercise provides you with a hand-coded AJAX autocomplete
example that returns names from a HashMap which match the characters typed into
a text field.
You begin by sampling the NetBeans JavaScript debugger on the
application. Then, you create a MySQL database, add data to it, and port it
to the application. Finally, you add the bundled Script.aculo.us toolkit to the
project in order to apply the Ajax
Autocompleter.
In order to complete this exercise, perform the following activities:
- Experimenting with the JavaScript debugger.
- Porting data to a MySQL database.
- Applying Script.aculo.us' Ajax Autocompleter.
Background Information
The NetBeans IDE provides a database viewer from its Services window (Ctrl-5;
⌘-5 on Mac). If you have a MySQL database installed on your computer,
you can interface with it from this window.
The IDE provides you with version 1.8.1 of the Script.aculo.us toolkit.
Steps to Follow
- In the Projects window, right-click the
Ex1_Scriptaculous_Start
project node and choose Set as Main Project.
- Click the Run Main Project (
) button in the IDE's toolbar to run
the project.
In the browser, experiment with the autocomplete field to see how it works.
This auto-completion is performed using an asynchronous call to the
server-side. The JavaScript used in the application prepares and sends an
XMLHttpRequest object. The server processes the request and
returns data in XML format, which the client then uses to update the DOM
(Document Object Model) on the fly. The following diagram illustrates the
process flow of the communication that takes place between the client and
server:

- In the Projects window, right-click the project node and choose Properties.
Then, in the left column of the Project Properties window, select Debug.
We are going to run the JavaScript debugger on the application to get a sense
of how communication takes place. First we need to activate the JavaScript debugger
and deactivate the Java debugger for the project.
- Deselect the Java debugger option and select the JavaScript debugger option.
(If you are not using Internet Explorer, the Firefox option is selected by default).

- Click OK to save changes and exit the Project Properties window.
- Run the debugger by pressing the Debug Main Project button (
) in the IDE's toolbar.
If you are running the IDE's JavaScript debugger for the first time, the IDE detects
whether you have the Firebug add-on and NetBeans Firefox extension installed in Firefox.
If these have not been installed, the IDE attempts to install them.
Warning: If you see this dialog, shut down Firefox, then
click OK. If not, the browser cannot load with the add-on and extension
activated, and you will need to restart the browser manually, then rerun the debugger.
A debugging session is initiated in the IDE, and the autocomplete example opens
in the browser.
Tip: Note that in the IDE, the index.jsp page opens
in the editor in read-only ([r/o] ) mode. The debugger retrieves all
files from the browser cache, and as such it is not possible to edit these files
directly. To do so, you would need to open these files from the Projects window,
edit and save changes, then rerun the debugger.
- In the IDE, make sure you have the following windows open:
- Local Variables
- Sessions
- Watches
- Call Stack
- Http Client Monitor
Many of these windows open by default when you run the debugger. If you
need to open any of these, choose Window > Debugging from the main menu
and select the window.
- Examine the
index.jsp file in the editor. Note that the
doCompletion() function is called when a user types in the
text input field:
<td><strong>Composer Name:</strong></td>
<td>
<input type="text"
size="30"
id="completeField"
onkeyup="doCompletion()">
</td>
- Open the
javascript.js file in the editor (double-click the file
from the Projects window). This JavaScript file contains all of the client-side
functionality required to produce the autocomplete dropdown list.
- Set a breakpoint on line 15 by clicking the line number in the left margin of
the editor. This will prompt the debugger to suspend on this line the next time
the
doCompletion() function is called.
Tip: If line numbers are not displayed, right-click in the left
margin and choose Show Line Numbers.
At this point, you may want to position the IDE side-by-side with your browser.
That way, it is possible to see any updates that take place in the IDE when you
trigger actions in the browser.
- In the browser, type in the letter "a" in the text field. Note that the
debugger suspends on line 16, where you previously set the breakpoint.

- Examine the Sessions window. The window lists debugging sessions that currently
exist in the IDE. Here you can see that the JavaScript debugging session
has started, and verify the current state it is in if it is active.
Tip: It is possible to run multiple sessions simultaneously (e.g., Java
and JavaScript). If this were the case, you could toggle the session you wanted
to use by clicking the entry listed in the Sessions window. (The
" "
icon signifies an active session.)
- Examine the HTTP Client Monitor. The HTTP Client Monitor provides an interface that
displays all messages being sent to and from the client.
Tip: Double-click the HTTP Client Monitor title bar to maximize it
in the IDE.
- Return focus to
javascript.js . The doCompletion() function
contains the code necessary to prepare and send an XMLHttpObject to
the server.
function doCompletion() {
var url = "doComposers?input=" + escape(completeField.value);
req = initRequest();
req.open("GET", url, true);
req.onreadystatechange = callback;
req.send(null);
}
- Hover your mouse over "
value " on line 15. You can see the
value that the expression "completeField.value " currently
holds.
Generally speaking, if the debugger session maintains a variable or expression
value, you can hover over, or highlight, the variable or expression in the editor
to determine the value.
- Click the Step Into button (
)
in the debugger toolbar. This steps the debugger to the next line (i.e., debugging frame).
Line 16 calls the initRequest() function, and as you can see by examining
the function (lines 22 - 32), it returns an ActiveXObject if browser
recognizes ActiveXObject s, or it returns an XMLHttpRequest
object if it understands XMLHttpRequest s.
- Hover the mouse over the
req variable in line 16. Notice that the variable
currently equates to void .
The debugger still needs to step through the initRequest() function and
attach a value to req .
- Click the Step Into button (
)
to step to line 16, and hover the mouse over the req variable again. Note
that although the debugger is suspended on line 16, the req variable's value
is still void . This is because the line on which the debugger is suspended
represents the code that the debugger is about to process.
- Click the Step Over button (
)
to skip stepping through the initRequest() function.
- Now hover the mouse over the
req variable in line 17. Notice that the variable
now equates to the value returned from initRequest() - an XMLHttpRequest
object.

- On line 17, position your cursor after the "
n " in req.open ,
then press Ctrl-Space to invoke code completion and documentation. Scroll down to view
the open() function signature that uses 3 arguments:
The XMLHttpRequest object requires these three arguments to be set before
it can be sent. These are:
- the URL, which contains set parameters and user input (i.e., the character "
a "),
- the method of HTTP interaction (i.e.,
GET or POST ), and
- a boolean value to signify whether communication is asynchronous or not.
If asynchronous communication is specified (if the third parameter is set
to true ), a callback function must also be specified to
indicate how the browser is to proceed once it retrieves the requested data.
This is essentially done in line 18, which prompts the callback()
function to handle the event when the response is complete:
req.onreadystatechange = callback;
- Step the debugger to line 19. The
send()
function is what initiates the request to the server.
- Return focus to the Http Client Monitor, then click the Step Into button
(
)
to step the debugger beyond line 19. Two events take place:
- The debugger jumps to line 35, the first line within the
callback() function, and
- The Http Client Monitor lists a new record.
- In the Http Client Monitor, click the new record to select it,
then under HTTP Request, click the Parameters tab and under
HTTP Response, click the Body tag.
Note that the parameters sent with the request match those contained
in the URL of the request, and that the response includes XML content.
If you examine the XML content contained in the HTTP Response, you basically
see 4 <composer> entries:
<composers>
<composer>
<id>12</id>
<firstName>Antonin</firstName>
<lastName>Dvorak</lastName>
</composer>
<composer>
<id>45</id>
<firstName>Aaron</firstName>
<lastName>Copland</lastName>
</composer>
<composer>
<id>7</id>
<firstName>Antonio</firstName>
<lastName>Vivaldi</lastName>
</composer>
<composer>
<id>2</id>
<firstName>Arcangelo</firstName>
<lastName>Corelli</lastName>
</composer>
</composers>
- Click the Continue button (
) in the debugger toolbar, then return
to the browser to examine the result. You should find that the
returned names match the XML <composer> entries
found in the HTTP Response.

- Click the Finish Debugger Session (
) button to terminate the session.
This step demonstrates how it is possible to create a database instance and
database tables, then add data to tables from the IDE. It also describes how
to hook the newly-created database up to the sample application.
In order to complete this step, there are 3 tasks that you'll need to accomplish:
- Create a database and populate it with data,
- Create a data source and connection pool,
- Modify the application to use a data access object.
Create a database and populate it with data
- In the Services window (Ctrl-5; ⌘-5 on Mac), right-click the
MySQL Server node and choose Create Database.
- In the window that displays, type in
composer to name
the new database, then click OK. (The "Grant Full Access To"
option will allow any user from the localhost domain access
to the database.)
Upon clicking OK, the new composer database displays in the Services
window.
Also note that a connection to the composer database is automatically
established and displays lower down in the Services window.
Note: If for any reason the IDE displays a broken connection
( )
node, right-click the node and choose Connect.
- Expand the composer connection node, then right-click the Tables folder
(
) node and choose
Create Table. The Create Table dialog displays.
Now, we want to use the Create Table dialog to define the database table that
will maintain composer records for our application. So far, the application
maintains these records in a simple HashMap which resides in the
ComposerData class.
- In the Projects window, expand the Source Packages >
com.ajax
node, then double-click ComposerData to open the class in the
editor. The composers HashMap is defined as follows:
private HashMap composers = new HashMap();
public ComposerData() {
composers.put("1", new Composer("1", "Johann Sebastian", "Bach", "Baroque"));
composers.put("2", new Composer("2", "Arcangelo", "Corelli", "Baroque"));
composers.put("3", new Composer("3", "George Frideric", "Handel", "Baroque"));
composers.put("4", new Composer("4", "Henry", "Purcell", "Baroque"));
composers.put("5", new Composer("5", "Jean-Philippe", "Rameau", "Baroque"));
composers.put("6", new Composer("6", "Domenico", "Scarlatti", "Baroque"));
composers.put("7", new Composer("7", "Antonio", "Vivaldi", "Baroque"));
...
As you can see, each Composer object contains 4 pieces of information:
an ID, first name, last name, and the period to which each belongs. To mirror this
when we create the database table, we'll apply the id as a primary key, and add
3 other columns of type VARCHAR to capture the rest of the information.
- Enter the following data into the Create Table dialog. (Click the Add column button
to create a new row in the dialog.)

- Click OK. Notice that a new
composer table now displays in the Services
window. Expand the composer table node, and you'll see the columns that we
just defined.
- Right-click the
composer table node and choose View Data.
The SQL editor displays with the query, "select * from composer.composer ".
Beneath the query, a visual representation of the table data is displayed.
- Let's add some data to the table. We effectively want to fill the table with all of
the data contained in the
HashMap from the ComposerData class.
Click the Insert Records icon ( ) located in the upper left corner of the table that
displays the data, then in the Insert Records dialog, copy over the details from the
HashMap . For example:
Tip: Click the Show SQL button to view to SQL code that will
be applied.
- Click the Add Row button to add space for new rows. Note that while you certainly
do not have to add all 50 composer records to the table, in order to demonstrate
a "filter effect" later when typing characters into the autocomplete field,
you'll naturally need enough names for this to seem effective.
Note: This lab includes an SQL script containing all 50 records,
which you can run on your connection to the composer database.
If you'd like to run the script, open composer.sql
in the IDE (Choose File > Open File from the IDE's main menu, and select the script).
When the script contents display in the editor, select the composer
connection from the Connection drop-down list above the editor, then click the Run
SQL ( ) button.
Click OK when you are finished. The table updates to display all the records you
just added.
At this stage, we've successfully managed to create a new database and database table
that contains the data needed for the sample application. The following steps outline
what is necessary to link the database table to the application. Specifically, we
need to create a data source and connection pool on the server, reference the data source
from the application's web.xml deployment descriptor, and create a data
access object within the application that is capable of performing queries on the
database.
Create a data source and connection pool
Using Java EE 5 technology, the accepted way to implement communication between a relational
database and application server is to create a data source (i.e., a DataSource
object), and initiate connection pooling on the server. Creating a new, physical connection
for each client request can be resource-expensive, especially for applications that continuously
receive a large number of requests. To remedy this, a group of reusable connections are created
and maintained in a connection pool. When the application requires access to the data
layer, it uses its DataSource object to obtain a connection from the connection
pool (using the getConnection() method). Likewise, when the application eventually
closes the connection (using the close() method on the connection object), the
connection is returned to the connection pool.
- Add definitions to the application for creating a new data source and connection pool.
When using GlassFish, you can do this by adding definitions to a
sun-resources.xml
file. This will instruct the server to create the artifacts when the project gets deployed.
The IDE enables you to do this with the help of a wizard.
Tip: You could also create the data source and connection pool directly
within the GlassFish Admin Console. Exercise 3 explores this
method in more detail.
Click the New File icon ( ).
- In the New File wizard, select the GlassFish category, then select JDBC Resource and click
Next.
- In Step 2 - General Attributes, select the Create New JDBC Connection Pool option.
Doing so has the effect of adding 3 new steps to the wizard.
For JNDI Name, type in jdbc/composers . This will be the name used by
the data source. Optionally, add a description for the data source.

- Click Next, and click Next again to move onto Step 4 - Choose Database Connection.
Type in composerPool for JDBC Connection Pool Name. Then, under the
Extract from Existing Connection option, select the database connection that we
created previously. This is: jdbc:mysql://localhost:3306/composer .

- Click Next, and in Step 5 - Add Connection Pool Properties, accept all default
settings. Note that required properties include a valid username and password
(i.e.,
root and nbuser ) to enable the data source access
the database.

- Click Finish. A
sun-resources.xml file is added to your project
containing the data source and connection pool declarations you specified in
the wizard.
Expand the Server Resources node and double-click the sun-resources.xml
file to view its contents in the editor. You'll see the two definitions we just
specified in the wizard (formatted differently for readability).
<resources>
<jdbc-resource enabled="true"
jndi-name="jdbc/composers"
object-type="user"
pool-name="composerPool">
<description>data source used to connect to the composer database</description>
</jdbc-resource>
<jdbc-connection-pool allow-non-component-callers="false"
associate-with-thread="false"
connection-creation-retry-attempts="0"
connection-creation-retry-interval-in-seconds="10"
connection-leak-reclaim="false"
connection-leak-timeout-in-seconds="0"
connection-validation-method="auto-commit"
datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlDataSource"
fail-all-connections="false"
idle-timeout-in-seconds="300"
is-connection-validation-required="false"
is-isolation-level-guaranteed="true"
lazy-connection-association="false"
lazy-connection-enlistment="false"
match-connections="false"
max-connection-usage-count="0"
max-pool-size="32"
max-wait-time-in-millis="60000"
name="composerPool"
non-transactional-connections="false"
pool-resize-quantity="2"
res-type="javax.sql.DataSource"
statement-timeout-in-seconds="-1"
steady-pool-size="8"
validate-atmost-once-period-in-seconds="0"
wrap-jdbc-objects="false">
<property name="URL" value="jdbc:mysql://localhost:3306/composer"/>
<property name="User" value="root"/>
<property name="Password" value="nbuser"/>
</jdbc-connection-pool>
</resources>
Creating definitions for the data source and connection pool in the
sun-resources.xml file is a way to create these resources without
needing to interact directly with the server - it is a handy approach that
ensures these resources will exist whenever the application is deployed.
However, just because you've created references in a file that exists within your
project does not mean that the application will know about these resources
when it is running. To make the application aware of the data source that
will exist on the server, we need to add a reference to its web.xml
deployment descriptor.
- Double-click the WEB-INF >
web.xml file to open it in the editor.
- Click the References tab along the top of the editor. Expand the Resource References
heading if it is not already expanded, then click the Add button.
- Enter the details for the
jdbc/composers data source which you
specified earlier.

- Click OK, then click the XML tab. Notice that the following reference has
been added to
web.xml .
<resource-ref>
<description>data source used to connect to the composer database</description>
<res-ref-name>jdbc/composers</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
Modify the application to use a data access object
The only remaining tasks are to code in a data access object that performs specific
queries for the application, then swap the data access object for the ComposerData
class in the servlet.
The data access object - ComposerDBAO - has already been prepared for
you and can be found in the com.ajax package. Open it in the editor and
examine its contents. Specifically, you'll see that it contains two data access
methods that perform an SQL query on the composer database. These are:
getComposers(String userInput) : Returns a list of Composer
objects whose names begin with userInput
getComposerDetails(String id) : Returns a Composer
object whose id matches the id argument
Perform the following steps:
- Replace the
ComposerDBAO object for the ComposerData object
in the ComposerServlet . To do this, we are going to take advantage of
the IDE's Diff Viewer.
You already have a modified version of ComposerServlet that uses the
ComposerDBAO class. It can be found in the Ex1_Scriptaculous_MySQL
project, which you should have opened in the Projects window. If not, you should use the
IDE's Open Project wizard now to open it in the IDE. (Instructions on how to do this were
provided in Exercise 0).
In the Projects window, click the ComposerServlet class in the
Ex1_Scriptaculous_MySQL project, then Ctrl-click (⌘-click on Mac) the
ComposerServlet class in the Ex1_Scriptaculous_Start project.
When both nodes are highlighted, right-click the latter node and choose Tools > Diff.
A Diff viewer displays in the editor, comparing the two files side-by-side.

- Scroll through the two files and note the changes required to make use of the
ComposerDBAO object instead of the ComposerData object.
Note: Make sure you have the Ex1_Scriptaculous_Start version of
the class on the right side of the Diff Viewer. If not, click the Swap button located
in the toolbar above the Diff. Any changes you commit in the Diff Viewer are made from the
file on the left to the file on the right, so in the next step you will be overwriting the
Ex1_Scriptaculous_Start version with the Ex1_Scriptaculous_MySQL version.
- When you are finished examining the two files, click the arrow (
) located between the two files at the
top of the viewer. All differences found between the two versions are eliminated, and your
Ex1_Scriptaculous_Start version now appears identical to the Ex1_Scriptaculous_MySQL
version.
Note: Now that you have successfully replaced references to the ComposerData
class for those of the ComposerDBAO class, your project no longer needs ComposerData .
At this point, you can safely delete it. To do so, right-click the ComposerData node in
the Projects window and choose Delete.
- Click the Run Main Project (
) button in the IDE's toolbar to run
the project.
When you type in characters in the autocomplete field, the application now queries
the database for matches.
Note: The contents of your project should now be identical to those of the
Ex1_Scriptaculous_MySQL project. This exercise continues by asking you to set
Ex1_Scriptaculous_MySQL as your main project. If for any reason you have not
gotten your project to run properly, you can use the IDE's Diff Viewer as you did above to
compare individual files and locate the problem.
Troubleshooting:
- You receive the error:
No object bound to name java:comp/env/jdbc/composers
StandardWrapperValve[ComposerServlet]: PWC1406:
Servlet.service() for servlet ComposerServlet threw exception java.lang.NullPointerException
This can be caused if you did not set a password for root access to the MySQL database.
If this is the case, see instructions for setting
the root password for the MySQL database server.
- You receive a "No suitable driver" error. While it is true that this exercise has
not discussed adding the MySQL Connector/J driver's JAR file to the project's classpath, the
IDE's interoperability with GlassFish provides a feature that detects at deployment whether
the driver JAR file has been added - and if not, it does so automatically. Make sure this
feature is activated; Choose Tools > Servers, and in the Servers window, select GlassFish
V2 in the left panel. Then, in the right panel, select the Options tab and ensure that the
"JDBC Driver(s) Deployment Enabled" option is selected. If not, select it, then
restart the server.
As Script.aculo.us' official documentation wiki states, the Ajax
Autocompleter allows for server-powered autocompleting text fields. It makes use of
Prototype's Ajax core options to
enable asynchronous communication between client and server.
This step explores how it is possible to apply the widget to the project in place of the
hand-coded autocomplete functionality that currently exists. This is not simply a
matter of "swapping" one for the other however, since the server currently
responds with composer elements in XML format while the Ajax Autocompleter
expects data in the form of an HTML list. We'll therefore need to modify the servlet
to accomodate these requirements.
In order to integrate the widget into the project, you need to perform the following tasks:
- Add the Script.aculo.us toolkit to your project
- Link to the toolkit's resources from a web page
- Add and configure the widget
- Modify server-side resources to output data in the appropriate format
Note: If you were unable to get the project to run properly after completing
the previous step, you should work from the Ex1_Scriptaculous_MySQL project from
this point onward. If you want to use the Ex1_Scriptaculous_MySQL project, right-click
its node in the Projects window and choose Set as Main Project.
Add the Script.aculo.us toolkit to your project
- In the Projects window, right-click the
Ex1_Scriptaculous_MySQL project
node and choose Set as Main Project.
- Right-click your project node and choose Properties.
- In the Project Properties window, select the JavaScript Libraries category, then
click the Add button.
The Add JavaScript Libraries dialog displays, listing all JavaScript libraries
registered with the IDE (excluding any libraries already added to the project).
- Select the Script.aculo.us library and note that the path where the library will be
extracted into your project is displayed.
Note: By default, the IDE extracts JavaScript libraries
to a web/resources folder within your project. You could modify the
path by clicking the ellipsis button ( )
in the above dialog, then navigating to a new path.
- Click OK. When you do so, the library is extracted to the specified location in your
project.
- Click OK to exit the Project Properties window. In your Projects window, note that a
scriptaculous_1.8.1 node is now listed under Web Pages > resources. If
you drill down into the node, you can see the Script.aculo.us core libraries, as well
as the Prototype framework script, which the
Script.aculo.us toolkit is based on.
The Script.aculo.us library is now added to your project.
Link to the toolkit's resources from a web page
- Open the
index.jsp file in the editor (Use Ctrl-Tab if the file is already
opened, otherwise double-click the node from the Projects window).
- Add the following
<script> tags between the page's <head>
tags:
<script src="resources/scriptaculous_js_1.8.1/lib/prototype.js" type="text/javascript"></script>
<script src="resources/scriptaculous_js_1.8.1/src/scriptaculous.js?load=effects,controls"
type="text/javascript"></script>
The references to scriptaculous.js and prototype.js are
generally required whenever you plan to apply Script.aculo.us widgets and functionality to
a page. The scriptaculous.js file is a loader script which by default loads
all other javascript files from the library. Here, we are limiting the files that are loaded
by specifying load=effects,controls .
Add and configure the widget
- Start by modifying the HTML markup. Make the following changes between the page's
<body> tags (changes in red):
<body onload="init()">
<h1>Auto-Completion using AJAX</h1>
<h3>Script.aculo.us Autocompleter with a MySQL Database</h3>
<p>This example shows how you can perform real time auto-completion using the
<a href="http://wiki.github.com/madrobby/scriptaculous/ajax-autocompleter">Script.aculo.us
Ajax Autocompleter</a>.</p>
<p>In the form below enter a name. Possible names that will be completed are displayed
below the form. For example, try typing in "Bach," "Mozart," or
"Stravinsky," then click on one of the selections to see composer details.</p>
<form action="doComposers">
<strong>Composer Name:</strong>
<input type="text" id="completeField" size="30" name="input">
<div id="composerMatches" class="composerList"></div>
<input type="hidden" name="action" value="getComposer">
</form>
</body>
Note that you need to delete the call to init() within
the opening <body> tag (i.e., onload="init()" ).
There are some fundamental differences between the two versions. Namely,
because you are relying on Script.aculo.us' functionality, you will no longer need
the javascript.js file (which implements the hand-coded autocomplete
field). Previously, the javascript.js file provided a way to differentiate
between requests for composer name matches and those for composer details by
programmatically attaching a lookup parameter and corresponding composer
id to the hyperlink reference of each composer name from the autocomplete
list (e.g.,
Ex1_Scriptaculous_Start/doComposers?action=lookup&id=7 ).
Because we are no longer relying on javascript.js , we need some other way
to differentiate between the two possible requests coming from the index.jsp
page. Here, we have accomplished this by adding a hidden field, which passes an
action=getComposer parameter when the form is submitted.
<input type="hidden" name="action" value="getComposer">
Later, on the server-side, you will see how the servlet responds when an incoming
request contains the action=getComposer attribute.
- Add a function call for the Ajax.Autocompleter widget. You can do so by typing in the
following code between
<script> tags (which you can also add between
the page's <head> tags). While you're at it, also modify the page's
title (changes in red):
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Script.aculo.us Autocompleter with a MySQL Database</title>
<link rel="stylesheet" href="css/demo.css" type="text/css" media="screen">
<link rel="stylesheet" href="css/stylesheet.css" type="text/css">
<script src="resources/scriptaculous_js_1.8.1/lib/prototype.js" type="text/javascript"></script>
<script src="resources/scriptaculous_js_1.8.1/src/scriptaculous.js?load=effects,controls"
type="text/javascript"></script>
<script type="text/javascript" src="javascript.js"></script>
<script type="text/javascript">
window.onload = function() {
new Ajax.Autocompleter("completeField", "composerMatches", "doComposers");
}
</script>
</head>
Tip: To reformat your code, right-click within the editor and choose
Format.
Note that the Ajax.Autocompleter constructor contains 3 arguments. By examining the
documentation
wiki, you can see that the constructor requires the following syntax:
new Ajax.Autocompleter(id_of_text_field, id_of_div_to_populate, url, options);
Looking at the markup, it is relatively easy to make the following associations:
completeField is the id of the autocomplete text field,
composerMatches is the id of the <div> element
that will be populated with composer matches, and
doComposers is the relative url that will be sent to the server.
- Finally, delete the reference to the
javascript.js file in your
index.jsp page, and delete the file from your project.
Specifically, remove:
<script type="text/javascript" src="javascript.js"></script>
from between the page's <head> tags, then in the Projects window,
right-click the javascript.js file and choose Delete.
Modify server-side resources to output data in the appropriate
format
Based on changes incorporated on the client-side, our servlet specification now requires the following:
- HTTP Response in HTML
- Composer names returned in the form of an unordered list (using
<ul> tags)
- For composer details requests, perform searches based on composer name, not id
To deal with these changes, modify the servlet and data access object as follows:
- Open the
ComposerDBAO class in the editor. In order to perform searches based on composer
name instead of id, make the following changes to the getComposerDetails() method (changes
in red):
public Composer getComposerDetails(String fullName) throws SQLException {
// create SQL query
String query = "SELECT * FROM composer WHERE '" + fullName +
"' = CONCAT(composer.first_name,' ',composer.last_name)";
...
- Also, delete the call to the
setString() method:
// prepare query
PreparedStatement ps = con.prepareStatement(query);
ps.setString(1, id);
The SQL query now uses MySQL's
CONCAT() String function to concatenate the composer table's first_name and
last_name columns, and compare them with the fullName argument.
- Open the
ComposerServlet class in the editor. Scroll down to the processRequest()
method and make the following changes (changes in red):
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out;
// declare variables for incoming parameters
String userInput = request.getParameter("input");
String userAction = request.getParameter("action");
// check if userInput parameter exists
if (userInput != null) {
userInput = userInput.trim().toLowerCase();
} else {
// flag an error if userInput == null
context.getRequestDispatcher("/error.jsp").forward(request, response);
}
// if user submits form, get composer and forward request
if (userAction != null && userAction.equals("getComposer")) {
if ((userInput != null)) {
ComposerDBAO dbao = new ComposerDBAO();
try {
// get target composer from data store
Composer c = dbao.getComposerDetails(userInput);
// put the target composer in the request scope
request.setAttribute("composer", c);
} catch (SQLException ex) {
Logger.getLogger(ComposerServlet.class.getName()).log(Level.SEVERE, null, ex);
}
context.getRequestDispatcher("/composer.jsp").forward(request, response);
}
}
// check if user sent empty string
if (userAction == null && !userInput.equals("")) {
out = response.getWriter();
try {
ComposerDBAO dbao = new ComposerDBAO();
List<Composer> composers = dbao.getComposers(userInput);
if (!composers.isEmpty()) {
out.println("<ul>");
for (Composer c : composers) {
Composer composer = c;
out.println("<li>" + composer.getFirstName() + " " + composer.getLastName() + "</li>");
}
out.println("</ul>");
}
} catch (SQLException ex) {
Logger.getLogger(ComposerServlet.class.getName()).log(Level.SEVERE, null, ex);
} finally {
out.close();
}
}
}
By calling getComposerDetails() , the servlet is
now able to retrieve the Composer object whose name
matches fullName , and place it into the request scope
so it can be accessed by the composer.jsp page.
When dealing with composer name requests (i.e., for the autocomplete
functionality), composer names are now returned in the form of an
unordered list:
List<Composer> composers = dbao.getComposers(userInput);
if (!composers.isEmpty()) {
out.println("<ul>");
for (Composer c : composers) {
Composer composer = c;
out.println("<li>" + composer.getFirstName() + " " + composer.getLastName() + "</li>");
}
out.println("</ul>");
}
And finally, because we have removed the line
response.setContentType("text/xml");
from our previous implementation, the HTTP Response will employ the text/html
content type by default.
- Click the Run Main Project (
) button in the IDE's toolbar to run
the project.
When you type in characters in the autocomplete field, Script.aculo.us' Ajax.Autocompleter
performs by making asynchronous calls to the server, in a manner similar to the previous
hand-coded implementation.

Summary
This exercise demonstrated how it is possible to experiment with client-server connectivity
in a simple web application. Specifically, you ported data from a Java HashMap
into a newly created MySQL database, then reconnected the application's front-end to the
database by configuring a data source and connection pool for the server, and by modifying
the servlet so that it utilized a data access object.
You then proceeded by integrating a Script.aculo.us Ajax.Autocompleter into the application,
and modified the back-end to accommodate client-side changes.
You also had a chance to explore some of the tools available to you when working in the NetBeans IDE,
such as the JavaScript debugger and HTTP Client Monitor, as well as the IDE's database support.
Back to top
Next exercise
|