FeaturesPluginsDocs & SupportCommunityPartners

Building an Ajax Chat Room with the Ajax Transaction Dynamic Faces Component


In this tutorial, you build an Ajax chat room web application with components that are themselves Ajax-unaware, also known as POJC (Plain Old JavaServer Faces Components). You achieve this using Dynamic Faces technology, an extension to JavaServer Faces technology that lets you easily implement Ajax functionality. In particular, you use the Ajax Transaction component included with the Dynamic Faces component library (0.2), which lets you visually configure Ajax functionality at design time. The chat room application comprises one rendered page, shown below, and is entirely Ajax-based. No conventional page submissions occur. Specifically, an Ajax request is sent when the user sends a comment by typing text in the text field and clicking the Send button or pressing the Enter key. The page also uses Ajax requests to poll the server continually in order to update the transcript of comments.


Contents

Content on this page applies to NetBeans IDE 6.0

To follow this tutorial, you need the following software and resources.

Software or Resource Version Required
NetBeans IDE Web & Java EE version 6.1 or 6.0
Java Developer Kit (JDK) Version 6 or
version 5
JavaServer Faces Components/
Java EE Platform
1.1 with J2EE 1.4
GlassFish Application Server V2
Travel Database Not required

Tutorial Requirements

Before you begin, you need to install the following software on your computer:

  • NetBeans IDE 6.0 or 6.1 with Web and Java EE functionality (included in the Web and Java EE download and the All download). (download)
  • Visual Web Samples Plugin. This plugin includes the Dynamic Faces Component Library (0.2). Follow the instructions in the section entitled "Installing the Plugin" in Installing the Visual Web Samples Plugin. Be sure to select the entries for both the Visual Web JSF Post Release Samples and the Visual Web JSF Backwards Compatibility Kit.

Ajax Chat Room Web Application

Creating the Project

  1. From the main menu, choose File > New Project.
  2. In the New Project wizard, select Web from the Categories list, select Web Application from the Projects list, and click Next.
  3. Name the project AjaxChatRoom and choose a location for the project.
  4. Select Sun Java System Application Server for the Server, J2EE 1.4 for the Java EE Version, and ensure that the checkboxes for Set Source Level to 1.4 and Set as Main Project are selected. Then click Next.

    New Web Application Wizard
  5. Select Visual Web JavaServer Faces and click Finish.

Configuring the Deployment Descriptor

Dynamic Faces technology requires an initialization parameter in the web application's deployment descriptor.

  1. In the Projects window, expand the Web Pages > WEB-INF node.
  2. Double-click web.xml to open it in the Editor.
  3. In the Editor toolbar, click Servlets, and then click the Add button that appears under Initialization Parameters as shown in the following figure.

    web.xml Servlet Initialization Parameters
  4. In the Add Initialization Parameter dialog box, type javax.faces.LIFECYCLE_ID in the Param Name text box, type com.sun.faces.lifecycle.PARTIAL in the Param Value text box, and click OK.

    Add Initialization Parameter Dialog Box
    The new parameter appears in the editor.
  5. Click XML in the editing toolbar and search for lifecycle to see the changes in the raw XML.
  6. Click Ctrl-S to save your changes to the web.xml file and close the filetab to close the file.

Adding the Dynamic Faces Component Library to the Project

  1. In the Projects window, right-click the Component Libraries node and choose Add Component Library, as shown below.

    Adding the Component Library
  2. In the Add Components Library dialog box, select Dynamic Faces Components (0.2), and click Add Component Library to close the dialog box.

    A new Dynamic Faces section appears in the Palette window.

Adding Code to the Application Bean

  1. In the Projects window, expand the Source Packages > ajaxchatroom node and double-click ApplicationBean1.java to open the file in the source editor.
  2. Scroll to the bottom of ApplicationBean1.java and add the following code before the final end brace.


    Code Sample 1: Application Bean Code To Store Comments and Generate Anonymous Usernames
        private int nextAnonIndex;
        private final int MAX_ENTRIES = 100;
        //list of String arrays, with each array of length 2
        private List entryList = new LinkedList();
        //array containing same contents as entryList
        private String[][] entries;
        public String[][] getEntries() {
            synchronized(this.entryList) {
                return this.entries;
            }
        }
        public void addEntry(String username, String comment) {
            if (comment == null || comment.length() < 1) {
                return;
            }
            if (username == null) {
                username = "anonymous";
            }
            synchronized(this.entryList) {
                if (this.entryList.size() == MAX_ENTRIES) {
                    this.entryList.remove(0);
                }
                String[] entry = new String[]{username, comment};
                this.entryList.add(entry);
                this.entries =
                  (String[][])this.entryList.toArray(
                  new String[this.entryList.size()][entry.length]);
            }
        }
        public synchronized String getNextAnonUsername() {
            nextAnonIndex++;
            return "anonymous" + nextAnonIndex;
        }

    In this code, you store each comment and its associated username as a transcript entry, that is, a String[] object of length 2. You store the entries in both the entryList variable and the entries variable. When adding a comment via the addEntry method or obtaining the transcript entries via the getEntries method, client code must obtain the monitor (that is, synchronize) on this.entryList, necessarily impacting performance but preserving data integrity. You maintain a maximum number of entries in the transcript, as determined by MAX_ENTRIES.

    Users will be able to access the application via a URL in the format http://server-ip-address:8080/AjaxChatRoom?username=someuser. The username specified will appear along with any comments sent by the user. If a user accesses the application via a URL that does not specify a username, an anonymous username will appear along with the user's comments. In such a case, an invocation of the getNextAnonUsername method will generate an anonymous username that is unique across the application.
  3. Right-click in the source and choose Fix Imports.

    Because there is more then one choice for the List class, the Fix Imports dialog box appears.

    Fix Imports Dialog Box
  4. Click OK to accept java.util.List as the fully qualified name for List.
  5. Close and save the file.

Adding Code to Store the Username

  1. In the Projects window, double-click Source Packages > ajaxchatroom > SessionBean1.java to open the file in the source editor.
  2. Scroll to the bottom of SessionBean1.java and add the following code before the final end brace.

    Code Sample 2: Session Bean Code to Store the Username
        private String username;
        public synchronized String getUsername() {
            return username;
        }
        public synchronized void setUsername(String username) {
            this.username = username;
        }

    As you will see in Code Sample 5, when the user sends a comment, an action method retrieves the username from session scope to record along with the comment. Since multiple threads associated with the same session might access the getter and setter methods, the methods are synchronized.
  3. Close and save the file.
  4. In the Projects window, double-click Source Packages > ajaxchatroom > Page1.java and then open the Java source code for the page.
  5. In the Navigator window, double-click prerender() to navigate to that method in the source editor.

    Navigating to the prerender Method
  6. Add the following code shown in bold to the prerender method:

    Code Sample 3: Page1 Code to Store the Username
        public void prerender() {
            String username = (String)getExternalContext().getRequestParameterMap().get("username");
            if (username != null) {
                getSessionBean1().setUsername(username);
            }
            else if (getSessionBean1().getUsername() == null) {
                getSessionBean1().setUsername(getApplicationBean1().getNextAnonUsername());
            }
        }

    If the page is initially rendering during the current request and the user specified a username in the URL, the request parameter map will contain that username. In such a case, you store the username in the session bean. If the request parameter map does not contain a username, there are two possibilities: either the page is initially rendering and the user did not specify a username in the URL or, alternatively, the current request is an Ajax request. In the former case, if you have not previously stored a username in the session bean, you generate and store an anonymous username. In the case of an Ajax request, you will already have stored a username in the session bean when the page initially rendered.

Adding the transcript Property to Page1.java

  1. Scroll to the bottom of Page1.java, and add the following code before the final end brace.

    Code Sample 4: transcript Property Code
        public String getTranscript() {
            String[][] entries =
                getApplicationBean1().getEntries();
            if (entries == null) {
                return null;
            }
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < entries.length; i++) {
                String entryUsername = entries[i][0];
                String comment = entries[i][1];
                String color = "purple";
                String username = getSessionBean1().getUsername();
                if (username.equals(entryUsername)) {
                    color = "blue";
                }
                sb.append(
                    "<div><span style=\"font-weight:bold;color:");
                sb.append(color);
                sb.append("\">[");
                sb.append(entryUsername);
                sb.append("]</span> ");
                sb.append(comment);
                sb.append("</div>");
            }
            return sb.toString();
        }


    This method supplies the transcript contents as a String object, formatting the username of an entry in blue if it is the same as the username stored in SessionBean1, and in purple otherwise. This has the effect of displaying the user's own entries in blue, while displaying the entries of other chat room participants in purple.
  2. (Optional) Right-click and choose Format Code, and then click the Save All button in the main toolbar to save your changes.

Creating the User Interface

  1. Return to the Visual Designer for Page1.
  2. Drag a Layout Panel from the Layout section of the palette.
  3. In the Visual Designer, select the Layout Panel component, and drag the bottom right sizing box down and to the right until the component is approximately 14 squares wide and 10 squares tall.

    Resizing the Layout Panel
  4. In the Properties window, change the id property of the Layout Panel component to transcriptPanel.
  5. Click the ellipsis (...) button for the style property, append the following rules to the CSS Style, and click OK.

    ; overflow: auto; border: 2px solid black;

    Setting the overflow style property to auto causes the chat room transcript to display a scrollbar if necessary. Note that the panelLayout property of the transcriptPanel Layout Panel is set to flow, which is the default. In steps 6 through 10 below, we add a Static Text component as a child of the transcriptPanel Layout Panel and bind the text property of the Static Text to the transcript property of Page1.
  6. Drag a Static Text component from the Basic section of the Palette onto the transcriptPanel Layout Panel. Make sure the outline of the transcriptPanel is highlighted in blue before releasing the mouse button, so that the transcriptPanel component contains the Static Text component.
  7. In the Properties window, change the id property of the Static Text component to transcriptText, and clear the checkbox for escape to set that property to false.

    Setting the escape property to false causes the browser to evaluate any markup tags embedded in the text. This ensures that the transcript entries appear with the desired formatting, such as blue or purple text color.
  8. In the Visual Designer, right-click the transcriptText Static Text component and select Bind to Data.
  9. In the Bind to Data dialog box, click the Bind to an Object tab.
  10. Select Page1 > transcript, and click OK.

    Binding the transcriptText Component to #{Page1.transcript}
  11. Drag a Text Field component from the Basic section of the Palette, and drop it below the transcriptPanel component.
  12. In the Properties window, change the id property of the Text Field component to comment.
  13. Change the comment Text Field's columns property to 60.
  14. Drag and drop a Button component from the Basic section of the Palette to the right of the comment Text Field, type Send, and press Enter.
  15. Click the ellipsis (...) button for the style property, append the following rules to the CSS Style, and click OK.

    ; width: 70px
  16. In the Properties window, change the id property of the Button component to send.

    Page1 should look similar to the following figure.

    Chat Room User Interface in the Visual Designer
  17. In the Visual Designer, double-click the send Button component.

    The IDE creates an action method for the send Button and displays the method in the source code editor.
  18. Add the following code shown in bold to the method.

    Code Sample 5: Button Action Handler
        public String send_action() {
          String comment = (String)getComment().getText();
          String username = getSessionBean1().getUsername();
          getApplicationBean1().addEntry(username, comment);
          return null;
        }

    This code retrieves any text the user has entered in the comment Text Field and supplies that text to the addEntry method, effectively adding the user's comment to the chat room transcript.

Configuring Ajax Transactions for Sending Comments and Polling

The Ajax Transaction component included with the Dynamic Faces component library (0.2) lets you visually configure Ajax functionality at design time, displaying various components with color-coded borders in the Visual Designer. At a minimum, you specify the components that send input to the server when the Ajax Transaction fires as well as the components that re-render when the client receives the Ajax response. The components that send input to the server are displayed with a solid border in the Visual Designer; the components that re-render are displayed with a dashed border. In addition, you must code a line of JavaScript to fire the Ajax Transaction.

In this section, you configure two Ajax Transactions, one for sending comments and another for polling the server. The commentTx Ajax Transaction fires in response to the user clicking the send Button or pressing the Enter key. When commentTx fires, the comment Text Field and send Button send their input to the server via an Ajax request, and the transcriptText StaticText re-renders when the client receives the Ajax response. The pollTx Ajax Transaction fires initially when the page loads. Subsequently, when the client receives an Ajax response associated with the pollTx Ajax Transaction, pollTx fires again after a brief delay. In this way, you poll the server continually. When pollTx fires, no components send their input via the Ajax request (since you are merely fetching, and not modifying, the transcript data), and the transcriptText StaticText re-renders when the client receives the Ajax response. As a result, the transcript of comments updates continually in the browser.

  1. Return to the Visual Designer for Page1.
  2. In the Visual Designer toolbar, click the Show Virtual Forms button Show Virtual Forms Button.

    The Ajax Transaction component provides Ajax functionality similar to the functionality provided by virtual forms for conventional submissions. Clicking the Show Virtual Forms button shows both the virtual forms and Ajax Transactions configured for the page.

  3. Expand the Dynamic Faces section in the Palette and drag an Ajax Transaction component from the Dynamic Faces section onto the Visual Designer.

    At the bottom of the Visual Designer, an Ajax Transaction Legend appears and shows the ajaxTransaction1 Ajax Transaction associated with the color blue.

    Ajax Transaction Legend
  4. In the Visual Designer, select both the comment Text Field and the send Button. Then right-click and select Configure Ajax Transactions.

    The Configure Ajax Transaction dialog box appears. At the top of the dialog box, send and comment appear, indicating that you are configuring Ajax Transactions for the send Button and comment Text Field.
  5. Within the Configure Ajax Transactions dialog box, double-click within the Name field, type commentTx to rename the Ajax Transaction, and press Enter.
  6. Set the Send Input field to Yes, and click OK to close the dialog box.

    Configuring Ajax Transactions for the send and comment Components

    The comment Text Field and send Button appear with a solid blue border in the Visual Designer, indicating that these components send their input to the server via an Ajax request when the commentTx Ajax Transaction fires. The send Button must send its input to the server in order to inform the server that the Button was clicked. In the Adding JavaScript section below, you will add a line of JavaScript code that fires the commentTx Ajax Transaction in response to the user's clicking the Send button or pressing the Enter key.

    The Visual Designer After Configuring Ajax Transactions for the send and comment Components
  7. Select the transcriptText StaticText in the designer. Right-click and select Configure Ajax Transactions.

    The Configure Ajax Transaction dialog box appears. At the top of the dialog box, transcriptText appears, indicating that you are configuring Ajax Transactions for the transcriptText Static Text.
  8. Within the Configure Ajax Transactions dialog box, click New to configure a new Ajax Transaction.
  9. Select red as the color associated with the new Ajax Transaction.
  10. Double-click in the Name field of the new Ajax Transaction, type pollTx, and press Enter.
  11. Set the Re-Render field of both the commentTx and pollTx Ajax Transactions to Yes, and click OK to close the dialog box.

    Configuring Ajax Transactions for the transcriptText Component

    The transcriptText Static Text appears with both a blue dashed border and a red dashed border, indicating that the component re-renders whenever the client receives an Ajax response associated with the commentTx or pollTx Ajax Transactions. In the Adding JavaScript section below, you will add the JavaScript logic that polls the server by firing the pollTx Ajax Transaction.

    The Visual Designer After Configuring Ajax Transactions for the transcriptText Component
  12. In the Navigator window, expand the Page1 > page1 > html1 > head1 node and select the commentTx Ajax Transaction.
  13. In the Properties window, set the postReplace property of the commentTx Ajax Transaction component to customPostReplaceForCommentTx.

    Here you specify that the client should invoke the customPostReplaceForCommentTx JavaScript function after receiving an Ajax response associated with the commentTx Ajax Transaction and re-rendering appropriate components (namely, just the transcriptText Static Text). You will see this JavaScript function in the Adding JavaScript section below.
  14. In the Navigator window, beneath the Page1 > page1 > html1 > head1 node, select the pollTx Ajax Transaction.
  15. In the Properties window, set both the inputs and execute properties of the pollTx Ajax Transaction component to none.

    This step explicitly configures the pollTx Ajax Transaction such that when the Ajax Transaction fires, no components will send their input in the Ajax request or undergo processing on the server.
  16. Set the postReplace property of the pollTx Ajax Transaction to customPostReplaceForPollTx and the replaceElement property to customReplaceForPollTx.

    Here you specify that the customReplaceForPollTx JavaScript function implements custom re-rendering logic when the client receives an Ajax response associated with the pollTx Ajax Transaction. You also specify that the client should invoke the customPostReplaceForPollTx JavaScript function after re-rendering appropriate components (namely, just the transcriptText Static Text). You will see these JavaScript functions in the Adding JavaScript section below.

Setting JavaScript Properties of the Body and Form Components

  1. In the Navigator window, beneath the Page1 > page1 > html1 node, select the body1 Body component.
  2. In the Properties window, set the onLoad property of the body1 Body component to handleOnLoad() and the onUnload property to handleOnUnload().

    You will see these JavaScript functions in the Adding JavaScript section below.
  3. In the Navigator window, beneath the Page1 > page1 > html1 > body1 node, select the form1 Form component.
  4. In the Properties window, set the onSubmit property of the form1 Form component to return interceptFormSubmit().

    You will see the interceptFormSubmit JavaScript function in the Adding JavaScript section below. When the user clicks the send Button or presses the Enter key, this JavaScript function prevents the conventional form submission and instead fires the commentTx Ajax Transaction.

Adding JavaScript

Now you create the ajaxchatroom.js JavaScript file.

  1. In the Project window, expand the Web Pages node, right-click on the resources folder, and select New > Other.

    Creating a File in the resources Folder
  2. In the New File wizard, select Other from the Categories list, and select Empty File from the Projects list, and click Next.
  3. Name the file ajaxchatroom.js and click Finish.
  4. Add the following JavaScript code to the file:

    Code Sample 6: ajaxchatroom.js
    var pollDelay = 1000;
    var continuePolling = false;
    var mouseDownOnTranscript = false;  //whether the user has performed a mousedown on the transcript (including any scrollbar)
                                        //and not yet performed a mouseup
    
    function customPostReplaceForCommentTx(element, markup) {
        //scroll to the bottom of the transcript
        var transcriptPanel = document.getElementById('form1:transcriptPanel');
        transcriptPanel.scrollTop = transcriptPanel.scrollHeight;
    
        //clear the text field
        var commentTextField = document.getElementById('form1:comment');
        commentTextField.value = '';
    
        //place focus in the text field
        commentTextField.focus();
    }
    
    function handleOnLoad() {
        //handle mousedown on the transcript
        var transcriptPanel = document.getElementById('form1:transcriptPanel');
        transcriptPanel.onmousedown = handleMouseDown;
    
        //handle mouseup anywhere on the page
        document.onmouseup = handleMouseUp;
    
        //turn autocomplete off for the text field
        document.getElementById('form1:comment').setAttribute('autocomplete','off');
    
        //start polling
        continuePolling = true;
        poll();
    }
    
    function handleOnUnload() {
        //stop polling
        continuePolling = false;
    }
    
    function poll() {
        //fire the pollTx Ajax Transaction
        DynaFaces.Tx.fire('pollTx');
    }
    
    function customReplaceForPollTx(element, markup) {
        //provided that the user is not performing an operation such as selecting transcript text or scrolling the transcript,
        //perform replacement (re-rendering) for this poll request,
        //and scroll the transcript as appropriate after the replacement
        if (!mouseDownOnTranscript) {
            var transcriptPanel = document.getElementById('form1:transcriptPanel');
    
            //scrollTop: distance between top of transcript and top of the portion currently visible
            //scrollHeight: total height of transcript, including any portion not visible due to scrolling
            //clientHeight: height of the visible portion of the transcript
    
            //capture whether scrollbar exists before replacement.
            //scrollbar exists if the scrollHeight exceeds the clientHeight
            var scrollbarExistsBeforeReplacement = transcriptPanel.scrollHeight > transcriptPanel.clientHeight;
    
            //capture whether the transcript is scrolled to the bottom before replacement
            var scrolledToBottomBeforeReplacement = false;
            if (scrollbarExistsBeforeReplacement) {
                //transcript is scrolled to the bottom if the sum of scrollTop and clientHeight equals scrollHeight
                if (transcriptPanel.scrollTop + transcriptPanel.clientHeight == transcriptPanel.scrollHeight) {
                    scrolledToBottomBeforeReplacement = true;
                }
            }
    
            //capture the scrollTop before replacement
            var scrollTopBeforeReplacement = transcriptPanel.scrollTop;
    
            //invoke default replacement function to perform actual replacement of transcript content
            DynaFaces.replace(element, markup);
    
            //capture whether scrollbar exists after replacement
            var scrollbarExistsAfterReplacement = transcriptPanel.scrollHeight > transcriptPanel.clientHeight;
    
            //scroll to the bottom of the transcript if it was scrolled to the bottom before replacement
            //or if the scrollbar did not exist before replacement and it now exists after replacement.
            //otherwise, scroll the transcript to the same place it was before replacement
            if (scrolledToBottomBeforeReplacement || (!scrollbarExistsBeforeReplacement && scrollbarExistsAfterReplacement)) {
                transcriptPanel.scrollTop = transcriptPanel.scrollHeight;  //scroll to the bottom of the transcript
            }
            else {
                transcriptPanel.scrollTop = scrollTopBeforeReplacement; //scroll transcript to the place it was before replacement
            }
        }
    }
    
    function handleMouseDown() {
        //if the mousedown occurs on the transcript's scrollbar,
        //IE will not invoke the corresponding mouseup event handler when the mouse is released.
        //in such a case, do not set mouseDownOnTranscript to true, since nothing will set it back to false.
        //instead, just perform replacement in customReplaceForPollTx even though the user is scrolling the transcript,
        //as this does not seem to cause a problem on IE anyway
        if (document.all) {
            var transcriptPanel = document.getElementById('form1:transcriptPanel');
            var scrollBarExists = transcriptPanel.scrollHeight > transcriptPanel.clientHeight;
            if (scrollBarExists) {
                if (window.event.offsetX > transcriptPanel.clientWidth) {
                    //mousedown occurred on the scrollbar
                    return;
                }
            }
        }
        mouseDownOnTranscript = true;
    }
    
    function handleMouseUp() {
        mouseDownOnTranscript = false;
    }
    
    function customPostReplaceForPollTx(element, markup) {
        //send the next poll request
        if (continuePolling) {
            setTimeout(poll, pollDelay);
        }
    }
    
    function interceptFormSubmit() {
        //if the text field is not blank, fire commentTx
        if (document.getElementById('form1:comment').value != '') {
            DynaFaces.Tx.fire('commentTx');
        }
    
        //prevent conventional form submission
        return false;
    }

    Because you set the postReplace property of the commentTx Ajax Transaction to customPostReplaceForCommentTx, the client invokes the customPostReplaceForCommentTx function after receiving an Ajax response associated with commentTx and re-rendering appropriate components (namely, just the transcriptText Static Text). Dynamic Faces uses the terms "replacement" and "re-rendering" more or less interchangeably. When the user sends a comment by clicking the send Button or pressing the Enter key, the customPostReplaceForCommentTx function scrolls to the bottom of the transcript, clears the comment Text Field, and places focus within the comment Text Field.

    Because you set the onLoad property of the page1 Page component to handleOnLoad, the client invokes the handleOnLoad function just after initially loading the page. This function assigns a handleMouseDown function reference to the transcriptPanel.onmousedown event handler and a handleMouseUp function reference to the document.onmouseup event handler. The handleMouseDown function reacts to mousedown events on the transcriptPanel element; the handleMouseUp function reacts to mouseup events anywhere on the page. Both of these functions, along with the customReplaceForPollTx function, work together to ensure that re-rendering in response to poll requests does not occur if the user has depressed the mouse on the transcript (including any scrollbar) and not yet released it. At such a moment, if the user is in the process of selecting some of the transcript text (for example, in preparation for a copy operation), re-rendering the transcript will effectively prevent the selection; similarly, if the user is scrolling the transcript on Firefox, re-rendering the transcript will cause the scrollbar to freeze. The handleMouseUp function handles mouseup events on the document object and not merely on the transcriptPanel element since the user could potentially depress the mouse on the transcript's scrollbar, drag the mouse out of the transcript area, and then release the mouse. In such a case, the logic needs to react to the mouseup event even though it is not on the transcript itself. Aside from these function assignments, the handleOnLoad function also turns off the browser's autocomplete feature for the comment Text Field and initiates polling of the server.

    The poll function fires the pollTx Ajax Transaction, which sends an Ajax request to the server. The corresponding Ajax response will trigger re-rendering of the transcriptText Static Text.

    Because you set the replaceElement property of the pollTx Ajax Transaction to customReplaceForPollTx, the client invokes the customReplaceForPollTx function instead of the default replacement function (namely, DynaFaces.replace) whenever the client receives an Ajax response associated with pollTx. Providing the customReplaceForPollTx custom replacement function allows you to invoke the default replacement function conditionally based on whether the user is performing certain operations on the transcript (namely, selecting transcript text or scrolling the transcript) as well as to capture the scroll position before the replacement and programmatically scroll as appropriate after the replacement. Prior to the replacement, you use the scrollTop, scrollHeight, and clientHeight properties of the transcriptPanel element to determine whether a scrollbar exists, and, if so, whether the transcript is scrolled to the bottom. Then you delegate the actual replacement of the transcript content to the default replacement function (DynaFaces.replace). Following the replacement, you scroll the transcript to the same position it was in before the replacement, except under certain conditions. Specifically, you scroll the transcript all the way to the bottom either if the transcript was scrolled to the bottom before the replacement or if a scrollbar now exists and did not exist before the replacement. In the former case, if you do not programmatically scroll the transcript to the bottom, any new transcript entries will not be in view. In the latter case, if you do not programmatically scroll the transcript to the bottom, the transcript will remain scrolled to the top, effectively obliging the user to scroll the transcript to the bottom manually.

    In the handleMouseDown function, Internet Explorer requires some additional logic. If the user depresses the mouse on the transcript's scrollbar, Internet Explorer will not invoke the event handler assigned to document.onmouseup when the user releases the mouse. As a result, you cannot set mouseDownOnTranscript to true, because the user's releasing the mouse will not invoke handleMouseUp, and therefore mouseDownOnTranscript will remain true. Under such circumstances, the customReplaceForPollTx function will forego re-rendering the transcript until some subsequent mouse operation triggers the handleMouseup function. A better approach is simply to return from the handleMouseDown function without setting mouseDownOnTranscript to true. As a result, the customReplaceForPollTx function will re-render the transcript on Internet Explorer even when the user is scrolling the transcript. However, this does not pose any problem, since re-rendering the transcript while the user is scrolling causes the scrollbar to freeze on Firefox only, not Internet Explorer.

    Because you set the postReplace property of the pollTx Ajax Transaction to customPostReplaceForPollTx, the client invokes the customPostReplaceForPollTx function after receiving an Ajax response associated with pollTx and invoking the customReplaceForPollTx function. Provided that the continuePolling variable is true (such that the page is not unloading), the customPostReplaceForPollTx function sends the next poll request following a delay specified by the pollDelay variable.

    Because you set the onSubmit property of the form1 Form component to return interceptFormSubmit, the client invokes the interceptFormSubmit function when the user clicks the send Button or presses the Enter key. Provided that the comment Text Field is not blank, the interceptFormSubmit function fires the commentTx Ajax Transaction (which sends an Ajax request) and returns false in order to prevent the conventional form submission.
  5. Expand the Advanced section in the Palette and drag a Script component from the Advanced section onto the Visual Designer.
  6. In the Navigator window, beneath the Page1 > page1 > html1 > head1 node, select the script1 Script component.
  7. In the Properties window, click the ellipsis (...) button for the url property of the script1 Script component. In the dialog box, select resources > ajaxchatroom.js such that /resources/ajaxchatroom.js appears in the URL field, and click OK.

    Setting the url Property of the script1 Component
    You have configured the script1 Script component to emit a <script> tag for the ajaxchatroom.js JavaScript file you created earlier.

Deploying the Project

This web application has been tested with Firefox and Internet Explorer 7.

  1. In the Projects window, right-click the project node and choose Run to build, deploy, and launch the web application.

    Your browser navigates to http://localhost:8080/AjaxChatRoom/.
  2. Enter a comment into the comment Text Field and press the Enter key or click the send Button.

    The entry appears in the transcript in blue, and your username is anonymous1.
  3. In the browser's location bar, change the URL to http://localhost:8080/AjaxChatRoom/?username=jack.

    Your previous entry now appears in purple, since you are now recognized as jack rather than anonymous1.
  4. Enter another comment into the comment Text Field and press the Enter key or click the send Button. The new entry appears in the transcript in blue, and your username is jack.
  5. Determine your IP address by opening a terminal (or command) window and doing one of the following:
    • Solaris/Linux/MAC. Type ifconfig -a, and press Enter.
    • Windows. Type ipconfig and press Enter.
  6. Open a second browser window or tab and type http://your-ip-address:8080/AjaxChatRoom?username=jill.

    The entries sent by anonymous1 and jack appear in purple.
  7. Enter another comment into the comment Text Field and press the Enter key or click the send Button.

    The new entry appears in the transcript in blue, and your username is jill.
  8. Switch back to the original browser window or tab.

    The entry sent by jill appears in purple.
  9. Continue the conversation between jack and jill until the transcript displays a scrollbar.
  10. Scroll up to the beginning of the transcript contents, keeping the mouse down, as if to re-read the beginning of the conversation. Wait a few seconds before releasing the mouse.

    The behavior differs slightly depending on the browser. On Firefox, the transcript does not re-render in response to poll requests while the mouse is depressed. On Internet Explorer, the transcript continues to re-render while the mouse is depressed. This is because the handleMouseDown function foregoes setting the mouseDownOnTranscript variable to true on Internet Explorer. This subtle difference in behavior is undectable while testing the application alone, as described in this section. In order to observe the difference in behavior, you must enlist an additional person to chat with you, sending comments while you perform this step.
  11. Select some of the text in the transcript, but do so without releasing the mouse yet. Use a keyboard shortcut to copy the text to the clipboard. Then release the mouse, noticing that the text deselects after you do so.

    While you are depressing the mouse, the transcript foregoes re-rendering in response to poll requests. However, once you release the mouse, subsequent poll requests cause the transcript to re-render. Re-rendering the transcript causes any selection made to be lost. The application could be enhanced by adding logic in the customReplaceForPollTx function to capture information about the selection before the replacement and reestablish the selection after the replacement. In such a case, once the user selected some text, you could deselect the text in response to the next mousedown operation anywhere on the document. You could achieve this by adding a function that deselected all text in the transcript and assigning a reference to the function to the document.onmousedown event handler.

See Also



Bookmark this page

del.icio.us furl simpy slashdot technorati digg
Companion
Projects:
MySQL Database Server   Open JDK: an Open SourceJDK   GlassFish Community: an Open Source Application Server    Mobile & Embedded Community    Open Solaris   java.net - The Source for Java Technology Collaboration   Virtual Box - full virtualizer  Open ESB - The Open Enterprise Service Bus Powered by