Friday, 15 April 2011

Optimization Issues - AJAX Tutorial


The Jaxcent documentation has some pointers on optimization. These should be reviewed by serious programmers.

We won't get into the technical details of those here. Instead, this tutorial will point out some practical issues.
In practical programming, there are various levels of optimization.
We have already covered JavaScript and how to improve response and decrease server load by doing things in JavaScript.
But given today's servers' power and network speeds, if you are developing an intranet application for a thousand users on a local area network, such optimization would be a waste of time. You may as well not spend any time and effort with JavaScript. JavaScript, as of now, is much harder and clumsier to write, organize and debug than Java. Therefore any development you do in Java instead of JavaScript, improves the application reliability, reduces development time, and so on.
On the other hand, if you are developing an application that could be potentially used by hundreds of thousands of users, some of whom may be located half way around the world at fifty network hops, you should put as much the of the work in JavaScipt as possible, so the application does not have any client-server trips that are not absolutely necessary, and so that the clients are distributing as much workload as possible.
If you don't know ahead of time how much the application needs to scale, a good stragety is to start with an all Java version. As the application grows in clientele, you can start moving selected pieces to JavaScript if/when needed.
There are other optimization issues as well, but in Java AJAX programming at the time, Java/JavaScript balancing is the big optimization issue. Servers hava become very fast and powerful. The network can still be a bottleneck.
Of course, optimization trade-offs change every few years with advances in technology and hardware. It is possible that a few years later, entirely other issues may dominate optimization instead! 

Dragging and Dropping - AJAX Tutorial


Dragging and Dropping

Using AJAX, user interfaces such as dragging and dropping become possible. We will briefly cover that here.
Dragging and dropping involves two items
  1. A dragging source, and
  2. A drop target.
The source is something that you click on, and drag around. The target is where you release the source, and which potentially does something useful with it!
In Jaxcent, you set an element draggable just by calling setDraggable on it.
To make something a drop-target, you just override the OnDragDrop method. This method takes on argument, which is the source that has been dragged.
The OnDragDrop takes the specific actions related to the drag and drop action. For instance, if the drop target is an icon of a trash-can, it would make sense for it to delete the dragged item, and to record in the session that the dragged item has been removed.
Drag and drop will typically change your underlying data structure, and therefore will often involve working with the session. For this example, we are not going to save anything in the session, because we just want to see how drag and drop can be made to work.
We will have two paragraphs, both of which serve as both a source as well as a target. They can be dragged and dropped on each other. The dropped one gets moved before the other one!
Exercise:  Write an HTML file with two paras and a matching Java file. Each of the two paras overrides the OnDragDrop method, and inserts the dropped source before it. In the constructor, make both paras draggable.

Saving Your Own Data in the Session - AJAX Tutorial

Earlier we learned about the session, and how to use it for form elements.
The session is a useful concept, and in addition to form elements, it can also be helpful to save your own data for a user.
The Jaxcent "session" matches the "session" of a Java Application Server if Jaxcent is running in a Java Application Server. Therefore if you have a legacy web application using a Java Application Server, you can use the session from both Jaxcent pages and the Application Server servlets/JSPs, and they can co-operate.
In general, however, it is not guaranteed that you are running inside a Java Application Server. Jaxcent could be directly connected to an IIS or Apache web server, or there may be some other configuration.
You will still have some kind of a session available, but it may not be an Application Server's session.
Therefore, to save any data for your own use, it's best to store it in the java.util.Map that Jaxcent makes available. If you enable AutoSessionData this Map will be available to you. The Map just stores the values of all form field using names/ids as keys (keys are lowercased, retrieval is case independent) in this map. So as long as you don't collide with these names, you can store your own values in the same Map, and it will be available to you for the duration of the user's session.
The Map is available by calling getAllSessionData( false ); from any page with AutoSessionData enabled. If the parameter is true, the session will first be updated with any data on the current page.
Exercise:  Earlier we made a small shopping-cart type application. Modify the MyPage.java as follows:
  1. Add AutoSessionData in the mapping for MyPage.html.
  2. Add a "bookList" field of class Vector (can't be ArrayList without adding synchronization, therefore Vector is convenient.)
  3. In the constructor, retrieve the data Map (parameter must be false, there is no page connected in the constructor) and get the element bookList from this map at key "MyPage.shoppingCart". If the element is null, create the Vector bookList, and store it at that key. If the element is not null, it must be the Vector you have saved earlier. Populate the table from it.
  4. As you save data in the table, also store the book names in the "bookList" Vector.
  5. Now everytime you visit the page again (without having timed out the session), you should see the shopping cart populated from any earlier choices.
  6. You will notice a slight problem -- the AutoSessionData will keep the previous SELECT selection highlighted even though in this case we don't want this data to be saved! This can be fixed by putting the mySelect.setSelectedIndex( -1 ); in an onLoad override instead of the constructor, so this can be reset at page load. 

Creating New Elements and Moving Existing Elements - AJAX Tutorial


Creating New Elements and Moving Existing Elements

Sometimes just hiding and showing page elements is appropriate. At other times, it is more appropriate to create new elements and insert them in the page. Sometimes, elements just need to be moved around!
In AJAX programming, great effects can be achieved using all these techniques.
The hierarchy of elements on the HTML page is known as the "Document Object Model" or "DOM" for short.
The DOM is a nicely organized tree structure. Elements are arranged as child and parent nodes.
If you want to create a new HTML node (say, a new <IMG> element), you need to decide where to insert it in the tree structure. There are some convenient options:
  • Insert in as the first or last child of a known node. The BODY node (corresponding to the HtmlBody class) is a convenient node for this, because many times it is convenient to add a new element at the top or bottom of the page.
  • Insert it right before or right after some other known node. For instance, you could create a new image element, and insert it right after a para node.
Same consideration apply if you want to move an element -- you need to decide where to move it to!
In Jaxcent, new elements are created using the same constructors we have been using, e.g. HtmlPara, HtmlDiv and so on. But the constructors take a SearchTypeparameter -- to create new elements, this parameter is specified as SearchType.createNew to tell Jaxcent that instead of searching for an existing element, it should create a new element on the page.
The new element must then be inserted in the page. Element insertion is same for new or existing elements, with the difference that when you create a new element, it will not be visible on the page until the first time you have inserted it somewhere in the DOM hierarchy.
These concepts should become clear with an example:
    HtmlBody body = new HtmlBody();               // The BODY of the page
    HtmlPara p = new HtmlPara( this, "myPara" );  // A P tag with id "myPara"
 
 
    // Insert the new para at the end, move the old para
    // the new para (from wherever it was)
 
    void newPara() throws Jaxception
    {
        // Make a new para some text.
        HtmlPara newp = new HtmlPara( this, SearchType.createNew,
                        "P", "My New Para, created at " + new java.util.Date());
 
        newPara.insertAtEnd( body );  // New para goes at end of BODY
 
        p.insertBefore( newPara );    // "myPara" goes jsut before new para
     }
The methods insertAtEnd and insertAtBeginning insert (or move) a child node in another node, such as the body. The methods insertBefore andinsertAfter insert (or move) a node immediately before or after a sibling node.
Exercise:  Write an HTML file DOM.html that has a para containing some text, and after that a form with id "form". In the form, add a checkbox with name "insertAtEnd" followed by a label "Insert at End". After this, put a button with id "insertTime" and text "Insert Time".
When the button is clicked, if the checkbox has not been checked, insert a new para at the beginning of the BODY giving the current time. If the checkbox has been checked, insert a new para giving the current time at the end of the body. Sleep 2 seconds, then move the new para just before the form. (Note: in the data map, the value for the checkbox will be an empty string if the checkbox has not been checked.) 

Hiding and Showing Page Elements - AJAX Tutorial


A powerful UI technique is to hide and show elements to the user as needed.

Any individual elements can be hidden or shown by calling the "hide()" or "show()" methods.
But instead of hiding and showing various blocks of elements individually, in many cases it is much simpler to put them inside DIV tags, and then to hide/show entire DIVs.
Using this technique, even an entire web application can be placed on a single HTML page without requiring any navigation -- instead of navigation, just show the DIV containing the next block of input!
We have a very rudimentary application involving Name, Address, a Message and a Summary page.
The exercise in this page is to pack them in a single page MyApplication.html.
Though one may not want to pack an entire application in one page for aesthetics and/or manageability reasons, the choice is there! And this exercise will show you the capabilities available.
Note that the AutoSessionData continues to work.
Exercise: 
  1. Create an HTML file MyApplication.html, with the Jaxcent JavaScript include statement in it. Add four DIV elements in it, with ids "nameDiv", "addrDiv", "messageDiv" and "summaryDiv". In these DIVs, copy the HTML forms and tables from the earlier Name.html, Address.html, Message.html and Summary.html files, but not the "Reset" or "Next" buttons.
  2. On all the DIVs, set the attribute style="display: none;" to start them off hidden at first.
  3. After all the DIVs, add a single Reset and a single Next button.
  4. Write the Java file for the HTML. In the constructor, show the "nameDiv" DIV. When connecting to the HTML file, don't forget to set the AutoSessionData!
  5. Add a Reset handler as earlier.
  6. Maintain a "currentPage" variable. Add a handler for the Next button. Because the user is not leaving the page, session data will not be automatically saved on the Next button. The saving must be done manually. In the handler, call the method "getAllSessionData( true )" to save the form data into the session (you can ignore the result of the method.) Then hide the current DIV being shown, and show the next DIV. If the next DIV is the summary DIV, also populate the summary table, and hide the Reset and Next buttons.
  7. Similarly, add a Back button. Use the "setEnabled" method in the constructor to start it off disabled. Enable it in the Next button's handler. Hide it when the user gets to the summary. Add code to show the previous DIV, and if on the first DIV, to disable itself. 

Working with JavaScript - AJAX Tutorial


We have already seen how to add JavaScript verifiers to click handlers in order to reduce client-server round trips and server load.

Jaxcent also allows other mechanisms for working with JavaScript.
The method execJavaScriptCode in JaxcentPage class executes JavaScript code. It can either be given just some chunk of code, or a function name that takes 1 or more arguments. For example:
    execJavaScriptCode( "alert( \"Hello\" );", false, null );
or
    execJavaScriptCode( "alert", false, new Object[]{ "Hello" } );
The arguments are specified as an Object-array, and besides the primitive type-wrappers (Integer, Boolean etc) and String, can also include Jaxcent HTML element objects e.g. HtmlInputText, HtmlPara etc.
The second parameter is false in these examples. If it is specified as true, the arguments are passed as a single parameter to the JavaScript function. The single parameter is a JavaScript array containing the actual list of arguments.
In addition to execJavaScriptCode, there is an evalJavaScriptCode method, which is similar but waits for the result from the JavaScript evaluation, and returns the result.

Client-driven Jaxcent Framework

In Jaxcent, normally the program logic is driven from the server side. But parts of, or the entire framework, can be client-side driven as well.
Jaxcent provides the JavaScript function JaxcentServerRequest to the client side. This function can be called with 0 or more parameters by JavaScript code on the client side.
Jaxcent provides the corresponding Java method onJavaScriptRequest in the JaxcentPage class. This method can be over-ridden to process messages coming from JaxcentServerRequest. It will be called with two null parameters, or a command and optionally an array of arguments.
Exercise:  Create an HTML file Jstest.html, with the Jaxcent JavaScript include statement in it. Also add the following JavaScript code in the HEAD section.


<SCRIPT LANGUAGE="JavaScript">
<!--
 
function identify( element )
{
    oldColor = element.style.backgroundColor;
    elementToReset = element;
    element.style.backgroundColor = "red";
    setTimeout( "resetIdentified()", 1000 );
}
 
function factorial( n )
{
    if ( n < 2 ) {
        return 1;
    }
    return n * factorial( n-1 );
}
 
var oldColor;
var elementToReset;
 
function resetIdentified()
{
    elementToReset.style.backgroundColor = oldColor;
}
 
var iter = 1;
 
function callServer()
{
    JaxcentServerRequest( "Test", "Calling", "Server", iter++ );
    setTimeout( "callServer()", 10000 );
}
 
setTimeout( "callServer()", 10000 );
 
-->
</SCRIPT>


Add a FORM, add an INPUT TEXT field to the form, and two BUTTONs with an id each. The text for the first button is "Identify Text Field", the text for the second button is "Compute Factorial 5".
  1. The JavaScript function "identify" can be called with an HTML element as argument. It will identify the element on the page by briefly turning it red. Add a Java handler to the first button. In the handler, call the JavaScript "identify" function to identify the INPUT TEXT field.
  2. Add a handler to the second button. In the handler, call the JavaScript function "factorial" with the argument "5" and print out the result in the output window.
  3. The JavaScript code given above, calls "JaxcentServerRequest" every 10 seconds. Retrieve the command and its arguments from the Java side, and print them out to System.out.

Data Verification- AJAX Tutorial

Often, it is desirable to verify the data on a page on a "Submit" button, before allowing the user to move to the next page.
This is very straightforward if there is a Java button click handler for the "Submit" button. The click handlers can be defined to have a single argument of type "java.util.Map" and they will receive all the page data along with the click notification. The page data can then be checked. E.g.
    protected void onClick( java.util.Map pageData )
    {
        String firstName = (String) pageData.get( "firstName" );
        if ( firstName.equals( "" )) {
            showMessageDialog( "Please enter first name" );
            return;
        }
        // ... Other checks
 
        // .. If everythihng ok, navigate
    }
If there is no Java server-side button handler, the verification needs to be handled using JavaScript mechanisms.
There is a third situation -- where there is indeed a click handler on the button, but the verification can be done on the client-side in JavaScript, and does not need any server information.
In such cases, a client-server round trip can be saved by only invoking the server-side handler if the client-side verification succeeded. This is desirable, because it increases responsiveness, and decreases server load. The trade-off is that it requires some JavaScript coding.
In Jaxcent, button click handlers (as well as any other event handlers) can have a JavaScript "verifier" attached to them. If the verifier fails, the button handler is never called by the client side.
The verifier can be a chunk of JavaScript code, though it will usually be more convenient to write a JavaScript function on the client, and just give Jaxcent the name of the verifier function.
An example of adding such a verifier is:
    myButton.addJavaScriptVerification(
        "click",    // Name of event
        "verify()", // Javascript code
        null        // Optional args.  If specifying args, do
                    // do not include "()" in the code.
    );
Now when "myButton" is clicked, the client-side JavaScript function "verify()" will be called first, all without involving the server. If this function returns false, no further processing will occur, and there will be no client-server communication. If this function returns true, only then server-side Java will be called.
An example of a verify function is:
<SCRIPT LANGUAGE="JavaScript">
<!--
function verify()
{
    var form = document.getElementsByTagName( "FORM" )[0];
    if ( form.firstName.value == "" ) {
               alert( '"First Name" is required.' );
               return false;
    }
    if ( form.lastName.value == "" ) {
               alert( '"Last Name" is required.' );
               return false;
    }
    return true;  // Proceed to server-side click handler.
}
-->
</SCRIPT>
Exercise: 
  1. Add first name field verification from the server-side to http://localhost/Name.html page.
  2. Add last name field verification from the client-side to http://localhost/Name.html page, using addJavaScriptVerifiation.

Navigation in Web Applications - AJAX Tutorial

You may have noticed that in the previous application, all navigation between pages was done using normal A HREF links.
Using Ajax, this is quite sufficient to save and load data. In fact, the data will be saved even if the user navigates away somewhere else using the browser buttons or the browser address bar, or even just closes the browser. But this behavior is different from the traditional behaviors of forms with buttons called Reset and Submit. As such, this behavior may be unsettling to users who expect that nothing is saved anywhere until they click a nice solid-looking button.
The Reset button is useful in any case, as users may want to undo the changes they have made on the page.

Adding a RESET Button

The old-style RESET feature available with FORM tags, is likely to only conflict with Ajax. Therefore a RESET button needs to be added and handled in Java.
To implement a RESET feature, do not use the INPUT TYPE=RESET, simply create a BUTTON (can be outside FORM) or INPUT TYPE=BUTTON (has to be inside FORM) element, label it "Reset", and add a variable and handler for it on the Java side.
    HtmlButton /* or HtmlInputButton */
      resetButton = new HtmlButton( this, "reset" ) {
        protected void onClick() {
            resetFromSession();
        }
    };
That's enough, this will reset the items on the page from session, getting rid of any changes the user has made.

Navigating using a button

To navigate using a button instead of using A HREF links, there are several approaches:
  • Make the button a SUBMIT button, and specify the new URL as the ACTION of the FORM. (This will cause form data to show up in the browser address bar, unless the METHOD is POST.) Of course, the new URL will just ignore the form data if it is the URL is an HTML file, so there will be no harm from the data.
  • Add JavaScript code on the button to do the navigation.
  • Add a handler on the Java side, and do the navigation in this handler.

SUBMIT button behavior

The contract of the old-style SUBMIT button is -- any changes the user makes are not sent to the website until the user clicks the SUBMIT button. If instead of clicking SUBMIT, the user navigates away from the form, the data will never been seen.
This behavior can also be enabled in Jaxcent. To duplicate this behavior:
  1. Add a button with the desired label, but it must not be an INPUT TYPE=SUBMIT button. It can be a BUTTON or INPUT TYPE=BUTTON element.
  2. In the page constructor, call
3.                 setFormSaveEnabled( false );
  1. In the button click handler, call
5.                 setFormSaveEnabled( true );
6.                 navigate( "/NewUrl.html" );
This will stop Jaxcent from saving the form data in the session, until the user clicks the button.
Before calling the setFormSaveEnabled and the navigation, the button handler can also check if any required form data is missing/wrong etc.
Exercise:  Add Reset and Submit (for old-style submit) buttons to the http://localhost/Name.html page. This will require changing the XML map for Name.html, and adding a Java class. 

Retrieving Session Data - AJAX Tutorial

It takes no additional work beyond specifying AutoSessionData to true, to get all the user's input into session. Of course, in a real-life situation, we may want to add more code to each page to verify data etc. But the saving/restoring into forms itself is free.
To get the data from the session, the method getAllSessionData provided in JaxcentPage class is used. It takes a single parameter, which we will specify as false. If the parameter is true, it tells Jaxcent that we also want to get the most recent information from the current page. But our summary page has no form input, so we don't need that.
The method returns a map, so all we have to do is retrieve values from the map.
Note that there is a distinction between the values stored in the map: if the user has never visited a page, the values for that page are not in the map. So retrieving any data from that page will return a null. But if the user has visited a page and has just not entered data in a field, retrieving data from that field will return an empty string instead of a null.
We can now build a small utiltiy to write out a row to our output table.
    void writeValue( HtmlTable table,    // Table to output to
                     String description, // Description of key (to output)
                     Object value        // Value of the key
                   ) throws Jaxception
    {
        if ( value == null ) // Page containing the item was never visited.
            value = "<I>Not Visited</I>";
        table.insertRow( -1, new String[]{ description, (String) value } );
    }
Using this utility, we can modify the constructor of Summary.java to write out all items.
    public Summary()
    {
        try {
            HtmlTable table = new HtmlTable( this, "summary" );
            java.util.Map data = getAllSessionData( false );
 
            // Add a header.
            table.insertRow( -1, new String[]{ "<B>Item</B>", "<B>Value</B>" } );
 
            // Add items.
            writeValue( table, "First Name",   data.get( "firstName" ));
            writeValue( table, "Last Name",    data.get( "lastName" ));
            writeValue( table, "Address",      data.get( "address" ));
            writeValue( table, "City",         data.get( "city" ));
            writeValue( table, "State",        JaxcentObject.getSelectedValue( data.get( "state" )));
            writeValue( table, "User Message", data.get( "message" ));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
Exercise:  Get all this to work. Starting from http://localhost/Contents.html in the browser, verify that the summary page shows all data entered, and that every time you visit it, it changes to reflect any changes you made on the pages. 

The Session -AJAX Tutorial

Web Programming uses the concept of a "session" to keep track of user data.
When a user visits your website, he/she may visit more than one pages. Usually for user interface reasons, it is a good idea to separate data entry into several pages. But we would like to be able to connect all these pieces of data with the same user. While he/she is visiting some pages, other users may be visiting other pages using the save web server. So the problem is, how to keep track of the data for each user.
The "session" concept is useful for this. A "session" is an object that is associated with a visitor. Each visitor gets his/her own session object. We can store all data related to the visitor in that session object. But that we don't know if the visitor is going to visit more pages, or has gone away to another site or has closed the browser. So sessions have a "timeout". If the user has not visited any pages within, say, 10 minutes, we assume the user has gone away and delete the session object.
Sessions are provided by web-server back ends, such as Java Application Servers.
Jaxcent uses the session object to provide a java.util.Map that is stored in the session.
Jaxcent can automatically save and restore data from forms in this Map. This data is saved by using the id or name as keys. The data stored consists of INPUT, SELECT and TEXTAREA items. You can also save your own items in this Map, as long as your keys do not collide with the form names/id.
To enable this automatic saving/restoring, the <Page> node in the config XML file needs to have one more entry:
  <AutoSessionData>true</AutoSessionData>
This tells Jaxcent to load form items with session data when the page is loaded, and to save any changes in the session when the page is unloaded.
To try this out, we will create some HTML pages with input elements on them:
  1. An HTML page Name.html with a FORM with INPUT TEXT fields for user first name and user last name (use values firstName and lastName for the NAME attribute.)
  2. An HTML page Address.html with a FORM with INPUT TEXT fields for user street address ("address"), city ("city") and a SELECT for state ("state") containing a few state names.
  3. An HTML page Message.html with a FORM containing a TEXTAREA with NAME "message" for entering a message.
  4. A summary page Summary.html with an empty TABLE with ID "summary".
  5. An index page Contents.html that has A HREF links to all these pages.
  6. Also put in links from Name.html to Address.html, from Address.html to Message.html, and from Message.html to Summary.html, and from all of them to the Contents.html.
  7. Add the JavaScript include statement in the header of all the html files except Contents.html.
Also write an empty Java framework for the Summary.html, using the package "tutorial" and class "Summary". Add a constructor and write some output to System.out, to verify that the page is connected.
Now add entries for all of these except Contents.html, in the config XML file. Note that we only have one Java class. Use jaxcent.JaxcentPage directly for all other HTML pages. In all the new Page node entries in the XML file, also add
  <AutoSessionData>true</AutoSessionData>
Now visit all of these, entering data in the forms. Any data you enter in the forms, should be staying in those forms when you visit the form again. Even if you refresh the page or visit the URL without going back, the data will stay on the page. However, if you leave the browser for more than 10 minutes and then come back, any pages you revisit will have lost their data, because the session would have been lost. (Except that if you have left your browser on one of these pages during those 10 minutes, that page's existing data would get loaded again.)
If you start another browser (not "New Window" from the same browser, but a new instance or a new type of browser, e.g. IE and Mozilla), you will not see your changes. You could enter a fresh set of data using that browser, and it will be kept track of. (Here are you acting as a second user.)
This is happening because of the AutoSession setting.
Exercise:  Get all this to work and verify that it is working by visiting http://localhost/Contents.html in the browser. Verify that all the data sticks. 

Receiving Input from the Page - AJAX Tutorial

The idea now is to add a checkbox to the table rows, and to change the text on the button to say "Order" instead of "MyButton". This is shaping up to be a very rudimentary shopping cart.
But checkboxes can only be inside FORM tags. The new BUTTON tag allows buttons outside forms, but all other input elements are still required to be inside FORMs, because they are designed for the page submit protocol. With AJAX, the page submit protocol is not really required, but still we have to put the checkboxes inside forms.
So the first task is to put the TABLE inside a FORM tag (open and close.) The FORM tag does not need any attributes, e.g. ACTION, METHOD etc.
Now we can change the contents of the table so that in addition to the name of the book, there is also a checkbox next to it. The user can select books, click the button, and we will display the results in a shopping cart table.
The onChange handler needs to be modified, to supply String arrays of 2 elements to the insertRow method.
        // Add a header, using B tags
        myTable.insertRow( -1,
            new String[]{ "<B>" + header + "</B>",
                          "<B>Order</B>" } );
 
        // Fill up the table with the books.
        for ( int i = 0; i < books.length; i++ ) {
           myTable.insertRow( -1,
              new String[]{ books[i],
                            "<INPUT TYPE=CHECKBOX NAME=checkbox_" + i + ">" } );
        }
Notice that we are supplying a unique NAME to each checkbox. Supplying a unique name or id lets us identify the checkbox, either to attach an object (of classHtmlInputCheckbox in the case of checkboxes) to it, or to identify whether it has been checked or not.
In this sample, we will simply be checking which checkboxes have been checked, and putting the corresponding books in the shopping cart. Change the button text to "Order" and add another empty TABLE with ID "shoppingCart" to the page. Also add and initialize a variable "shoppingCart" of class "HtmlTable" connected to the new TABLE.
The button should already have a click handler, now change the click handler so it has a single parameter of java.util.Map class.
In the handler,check for all names that start with "checkbox_" and add the corresponding books in the "shoppingCart" tables.
    protected void onClick( java.util.Map pageData )
    {
        try {
            int selectedIndex = getSelectedIndex( pageData.get( "mySelect" ));
            String[] books;
            switch ( selectedIndex ) {
                case 0:  books = scienceBooks; break;
                case 1:  books = mathBooks;    break;
                default: books = geographyBooks;
            }
            java.util.Iterator names = pageData.keySet().iterator();
            while ( names.hasNext()) {
                String name = (String) names.next();
                String value = (String) pageData.get( name );
                if ( name.startsWith( "checkbox_" ) && ! value.equals( "" )) {
                    // Checkbox, selected
                    int index = Integer.parseInt( name.substring( "checkbox_".length()));
                    String book = books[ index ];
                    shoppingCart.insertRow( -1, new String[]{ book } );
                }
            }
        } catch (Jaxception jax) {
            jax.printStackTrace();
        }
    }
Exercise:  Get all this to work at http://localhost/MyPage.html in the browser. Clicking the "Order" button should fill the shopping cart table with any checked books. Also add a variable that is initialized to false, and that keeps track of whet her anything has been added to the shopping cart yet. Use it to add a header to the shopping cart, the first time something is added to the shopping cart.