Skip to main content

End-to-end Ajax application development, Part 3: Integrate, test, and debug the application

Separate your application tiers to produce a clean and elegant Web app

Senthil Nathan (sen@us.ibm.com), Senior Software Engineer, IBM
Photo of Senthil Nathan
Senthil Nathan is a senior software engineer at the IBM T.J. Watson Research Center in Hawthorne, New York. He has 22 years of experience in building software for different kinds of enterprise applications. His current professional interests are fully buzzword-compliant -- including SOA, Web services, Java 2 Platform, Enterprise Edition (J2EE), PHP, Ruby On Rails, Web 2.0, and Ajax development.

Summary:  Ajax (Asynchronous JavaScript + XML) is quickly emerging as a modern way of bringing desktop quality software features to Web applications running on browser platforms. This article is the last of a three-part series where you can complete the development of an end-to-end Ajax application using technologies available from the open source community.

View more content in this series

Date:  19 Jul 2007
Level:  Intermediate
Activity:  2708 views

In the first two parts of this series, you set up a development environment consisting of a LAMP-like runtime and the Eclipse IDE. You defined a fictitious banking scenario architected to use important Ajax concepts. Then, you completed a portion of the scenario by creating the database, the mid-tier PHP logic, and a simple XHTML to provide a single page-based browser GUI along with CSS code for styling and a few XML HTTP Request (XHR) utility functions. In this third and final part of the series, you complete the remainder of the scenario by implementing the Ajax client-side logic in JavaScript. You also build a Representational State Transfer (REST) request dispatcher in PHP to fulfill the customer's banking tasks such as deposit, debit, and stock portfolio value. You'll also be exposed to the development of a SOAP (Simple Object Access Protocol) Web services client (in PHP) to access a real-life third-party (free of cost) Web service. You'll see how to integrate all the different components (XHTML, CSS, JavaScript, PHP, Web services client, and MySQL) developed in this article. Finally, this article explains the basics of deploying, testing, and debugging the developed scenario with the LAMP-like runtime. At this point, you will have built a non-trivial example of an end-to-end scenario demonstrating the powerful features of the Ajax client, the Apache-PHP-MySQL runtime, and the associated Eclipse-based IDE.

Introduction

Before getting started, ensure that the Bank scenario artifacts (developed in Part 2 of this series) for Bank DB, Bank Logic, and Bank Portal are intact in your Eclipse IDE. Those artifacts covered the SQL script to create and populate the database; the PHP code to provide database access; and the XHTML, CSS, and XHR to address the single-page browser application needs. The code written in those artifacts provides only part of the functions outlined in the Bank scenario shown in Figure 1. The components required to glue these already-developed artifacts are not there yet. If you preview the XHTML file as it stands, it displays a jumble of different application-specific UI screens in a single browser window. You still need to write the appropriate client-side JavaScript logic to integrate these different UI screens to give the effect of a single-page browser application. In the same client-side code, appropriate XHR asynchronous communication logic needs to be added. Without the client-side JavaScript code, bank teller functions cannot be performed.


Figure 1. Bank scenario
Bank scenario
Get ready for the final part of your journey into developing with Ajax technologies across all three tiers of your application. You'll build, deploy, and test your application using features of Zend Core and Eclipse IDE tools. You'll also explore MySQL, PHP, Ajax (XHTML, CSS, JavaScript, XHR), REST, JSON, XML, and a Web services client.

You might also notice that all the HTML forms that provide specific bank teller functions are configured to post the user data to a REST-based, mid-tier service. You will be developing this service as another PHP module that will receive the bank teller's browser requests for deposit, debit, and current stock portfolio value actions. This PHP module will act as a bank action request dispatcher that parses the bank teller's specific transaction request and calls the appropriate Bank Logic function that will process the request. You will learn how simple it is in PHP to do such REST-based services.

After developing a PHP service for REST request dispatching, our focus will turn to a .NET-based Web service that is available on the Internet. This Web service is made available for free to get the current price of a given stock ticker symbol. You will develop a Web service client in the PHP mid-tier to access this Web service remotely from the PHP-based bank action request dispatcher to get the stock quote. Then the bank action request dispatcher will call the Bank Logic PHP module with the current stock price to compute the current portfolio value for a given account holder. You will learn the techniques for Web service access using SOAP and you'll learn about XML and JavaScript Object Notation (JSON), two popular data interchange formats.

Client-side logic in JavaScript

In our single-page browser-based application, we need client-side logic to handle the Ajax-style user interface and interaction with the mid-tier service for data interchange. As described in Part 2 of this series, it is possible to use one of the many Ajax frameworks and libraries evolving at this time. However, so that you concentrate on a very selective set of Ajax features, you will write the client-side logic in raw JavaScript. Doing so will help you focus in on the following Ajax-related objectives of this article:

  • Single-page browser application
  • Browser DOM manipulation
  • Using XHR in the context of an application
  • Role of JSON in application data interchange
  • Client-side debugging of Ajax code

Once you understand the plain JavaScript code in our bank scenario, as an exercise, you can practice making the same client-side logic using a particular Ajax framework. In each approach, the Eclipse-based Aptana Web IDE is an ideal choice to do the client-side code development.

It is an open secret that JavaScript provides powerful language features. In a browser environment, JavaScript provides an event handling method to capture user actions and system actions such as timer and network events. In our bank scenario, you will use event handlers specifically for mouse actions (mouseover, mouseout, mouseclick, and so on), and for the server response asynchronous callbacks using the XML HTTP Request (XHR) object.

A single-page browser application requires all the user interface-related code artifacts (XHTML, CSS, and JavaScript) to be downloaded only once from the server, when the application URL is entered into the browser. That means that your client-side logic should have all the things needed to handle the client-side requirements of the bank scenario. The screen layout and the associated styling are delivered through XHTML and CSS files. Other client-side logic is delivered through the code written in JavaScript. The bulk of the logic involves splitting the different application screens and displaying them appropriately one screen at a time, depending on user navigation. Part 2 of this series showed you how to use the <DIV>, <SPAN> and <TABLE> HTML elements in arranging different screens to create a single XHTML page for the entire Web application. Using JavaScript, you will learn about manipulating the browser DOM to hide and show different user interface controls kept inside of <DIV> elements. In addition, you will learn about styling the different user interface controls through the dynamic assignment of CSS rules. Whenever the user of this application (usually the bank teller) performs an account-related task (such as a deposit, debit, or portfolio value update), the client-side JavaScript logic communicates with the mid-tier PHP service in a REST style. As a result of this interaction, application-specific data is exchanged between the browser client and the mid-tier PHP service. This is all done using the XHR object, as described in Part 2 of this series. JavaScript client logic uses the XHR object to send and receive application-specific data to and from the server. You'll get an introduction to the Ajax way of receiving server responses in an asynchronous mode.

Following the components of the Ajax acronym (Asynchronous JavaScript + XML), we have covered the asynchronous and JavaScript components. Now it's time to address one of the other important building blocks of Ajax development: data representation. Even though the Ajax acronym has XML as a main component, XML is not the only data interchange mechanism available for Web applications. Because XML is well known to the software development community, this article doesn't need to repeat what you probably already know about it. Instead, it introduces another upcoming and popular Web data interchange format called JSON. Like XML, JSON is also text-based and both human- and machine-readable. As far as machine-readability is concerned, JSON seems to be much easier to parse and use -- especially in browser applications -- because it follows the syntax of JavaScript object and array data structures. Consuming JSON in browser applications comes down to treating the entire data structure as a first-class JavaScript object. That is great news for many Web developers because it completely eliminates extra parsing work. The JSON format is also gaining acceptance in other popular programming languages such as the Java language, PHP, Ruby, and C++, among others. The following section gives you details about what JSON is made of. You can get links to complete details about JSON from the Resources section. There are also several efforts to convert existing XML data into JSON format wherever it makes sense. You can learn about one such technique through another IBM® developerWorks article referenced in the Resources section. The client-side logic for our bank scenario uses JSON as the data interchange format between the browser and the mid-tier PHP service. In the upcoming sections, you will be exposed to JSON processing both in JavaScript and PHP.

Structure of JSON

JSON is built on two structures:

  • A collection of name/value pairs.
    • In various languages, it is realized as one of the following:
      • Object
      • Record
      • Struct
      • Hash table
      • Associative array
  • An ordered list of values
    • In most languages, this is realized as an array.

These are universal data structures.

  • All modern programming languages support them.
  • A data format (JSON) made of structures that is interchangeable with programming languages makes perfect sense.

With these building blocks, as well as your experience and knowledge gained following Part 1 and Part 2 of this series, you should now have a good introduction to the concepts of Ajax development. While many of the technologies that make up Ajax have been around for awhile, the Ajax way of developing applications by mixing these technologies is only a recent phenomenon. This mixture of technologies makes debugging a Web application in an end-to-end fashion quite important. You'll get exposure to a debugging approach in the context of our bank scenario.

Implementing the client-side logic in JavaScript

As discussed in the previous section, JSON is the data interchange format of choice in our bank scenario. JSON can be directly consumed in browser applications using the JavaScript eval statement. However, directly evaluating arbitrary JSON code brings up a security concern. To alleviate such security concerns, Douglas Crockford, the creator of JSON, made a simple JavaScript library that does a safety check of JSON-formatted text before it can be converted into a JavaScript object. We rely on those library functions in processing JSON data.

First, create the client-side logic in JavaScript:

  1. Open a browser and go to the URL: http://www.json.org/json.js:
    1. In the resulting File Download dialog box, click Save.
    2. Save the json.js file in the c:\eclipse\workspace\BankTeller directory and close the browser.
  2. If not done already, change to the Aptana perspective in Eclipse: click Window->Open Perspective->Other->Aptana and click OK.
  3. In the Aptana perspective, in the bottom left frame, select the tabbed view titled Project.
  4. Right-click the BankTeller project and click Refresh. You should now see json.js file as part of the BankTeller project.
  5. Right-click the BankTeller project and select New->JavaScript File.
    1. In the File name field, enter BankTeller.js and click Finish.
  6. Replace the contents of this file by pasting in the source code in Listing 1 and then save the file. You can review the comments in these files to understand the code or refer to the next section, which provides a high-level description about the code logic.

Description of the client-side logic

The client-side logic in the BankTeller.js file covers important Ajax concepts behind creating a single-page browser application, communicating with the mid-tier service in an asynchronous fashion, and using REST/JSON for service access and data interchange. All of this is mostly done through JavaScript event handlers for user interactions and for XHR objects. The functions in this file also require other utility functions from two other JavaScript files (xhr.js and json.js). The code in this file starts off by defining application-specific variables and constants. Then the JavaScript functions follow in no particular order. The following ten functions are related to user interactions with the bank teller application:

  • initOnPageLoad
  • showFooterOptions
  • changeOptionsLinkStyleForSelection
  • changeOptionsLinkStyleToDefault
  • processTellerOperation
  • hideShowAndInsertAsTopElement
  • populateDropDownListWithAccountOwnersData
  • setElementFocusAndTextAndCursorPosition
  • processFooterOperation
  • updateTellerActionResultTextArea

The following nine functions are related to interchanging application-specific data with the mid-tier service.

  • getAllAccountsInformation_Async
  • http_response_async_callback_for_get_all_acounts_info
  • depositAmountAction_Async
  • http_response_async_callback_for_deposit_to_account
  • debitAmountAction_Async
  • http_response_async_callback_for_debit_from_account
  • portfolioAction_Async
  • http_response_async_callback_for_get_stock_portfolio_value
  • getHostnameFromThisURL

This file is sufficiently commented to help you understand the inner workings of this Ajax browser application. The following description gives high-level information about some of the client-side JavaScript functions. For more details, read the comments in the BankTeller.js file. When the user's browser is pointed at the URL for this application, the one and only XHTML page for this application is fetched from the server and loaded in the browser. During the initial page load, the initOnPageLoad function runs. As discussed earlier, this application's user interface contains a single XHTML page that is divided into different sections using the HTML <DIV> elements. There is a main <DIV> element called "mainPage," which is at the top level of the browser DOM tree structure. All the other <DIV> elements are children of the main <DIV> element. In this function, all the <DIV> elements in this application are stored in global variables. All the <DIV> elements except the main page and teller menu option are applied with a CSS rule for hiding them. Because all these <DIV> elements are stored away in global variables for later use, the hidden <DIV> elements are also removed from the parent <DIV> element to avoid the vertical scroll bar in the browser. Then an Ajax XHR call is made to get all the bank account information asynchronously.

The function named showFooterOption optionally shows or hides the page footer in a <DIV> section of the application. The page footer displays the current teller name and another option for getting back to the main menu from other screens of this application. It uses the CSS built-in styles such as "visible" or "hidden" to show or hide the page footer.

This application uses a menu to select a bank teller option to be performed. The main screen of this application displays the menu. This menu structure is coded in the XHTML file using regular HTML text embedded within the <SPAN> elements. These <SPAN> elements are assigned with mouseover, mouseout, and onclick mouse event handlers. Whenever mouseover and mouseout events occur, respective event handler functions (changeOptionsLinkStyleForSelection, changeOptionsLinkStyleToDefault) are called. Inside of these functions, the style of these <SPAN> elements are toggled using a CSS class rule with a background color or a CSS rule with no background color. This creates the effect of highlighting and unhighlighting the menu options.

The function named processTellerOperation is an event handler for the onclick event that gets triggered when the user clicks on a menuitem text in a <SPAN> element. This function is passed a parameter to indicate which menu option was selected by the user. Inside this function, the currently selected menu option is stored in a global variable to keep a record of the current bank teller option being performed by the teller. Then, the menu option <DIV> element is hidden and another <DIV> element holding the UI controls for the selected bank teller option is shown. In the newly shown screen (deposit, debit, or portfolio value), the list is filled with all the account holder names. From this list, the teller can select an account holder and perform the required bank teller operation.

The function named hideShowAndInsertAsTopElement is a utility function with several input parameters, such as element to be hidden, element to be shown, any particular element id to be set with focus, optional cursor position to be set on an input field, and an option to show or hide the page footer. Based on the input parameters, it shows and hides the corresponding elements. Optionally, it does other things as instructed through other input parameters.

The function named populateDropDownListWithAccountOwnersData inserts all the names of the account holders in the list box. This helps the bank teller select a particular customer name to perform the required teller function. This function also sorts the account holder names while inserting them into the list box.

The function named updateTellerActionResultTextArea updates the results of the bank teller actions in a text area. Another function named processFooterOperation does the processing for the options available in the page footer.

Now let's take a look at the XHR-related functions. Because our application is a true single-page Ajax application, presentation content is downloaded only once at the application startup time. Thereafter, the browser client goes to the server only for application-specific data exchanges. Data exchanges are done using an XHR object in an asynchronous fashion by accessing the mid-tier service in REST (Representational State Transfer) style. REST uses HTTP verbs such as PUT, GET, POST, and DELETE, referring to how to address remote server resources by exchanging data. (Learn more details about REST in Resources.)

In many Ajax applications, XHR works well with REST-style access mechanisms. As discussed in previous sections, the JSON format is used to encode the application data. In this application, there are four different data exchanges between the browser and the mid-tier service. The procedures involved in all four of those data exchanges are very similar. Hence, only the data interchange for the deposit bank teller function is described here. Each data interchange is done through two different JavaScript functions: one to send the request to the mid-tier service and the other to receive the server response data asynchronously. When the bank teller performs the deposit function, he or she enters the deposit amount and selects the account holder name before pressing the Deposit button. The onclick event for that button is set to an event handler named depositAmountAction_Async. Inside this function, a deposit amount is read from the input field and validated. Then, the account holder name is selected from the drop-down list, and the host name is obtained from the fully qualified document URL. At this time, a JSON object is created with two object properties: the account holder name and the deposit amount. This is the data that is sent to the mid-tier service using REST with the POST HTTP verb.

To convert the JSON object into a JSON formatted string, you will use a utility function from the json.js file (developed by Douglas Crockford). Then, an HTTP POST query string is created with two sets of key-value pairs. The first key-value pair tells the mid-tier service what resource or command needs to be performed -- in this case, it is the deposit command. The second key-value pair tells the mid-tier service about the request data that is being sent from the browser -- in this case, it is the JSON formatted data that consists of the account holder name and the deposit amount. Then the mid-tier service URL is assigned. After that, a new XHR object is created and, most importantly, the callback function http_response_async_callback_for_deposit_to_account is set. The utility function sendHttpRequest from xhr.js is called with the XHR object, callback function, mid-tier service URL, and post data as input parameters. That sends the data-interchange request for a deposit function to the mid-tier service.

Now let's take a look at the callback function. The callback function is called for every HTTP state transition. But the interesting HTTP states that are checked inside the callback function are HTTP_READYSTATE_LOADED and HTTP_STATUS_OK. These two states indicate that the server response data has been fully received and the HTTP internal status code is 200. When both these conditions are met, then the server response data is read from the XHR object's internal property called responseText. This JSON-formatted string data is safely and securely converted using the utility function parseJSON from json.js. This conversion results in a native JavaScript object. Inside of this object, application-specific status is checked to see if the deposit function was successful. On success, the returned account information object is stored in the global variable tellerActionResultInfo. Lastly, the current deposit operation screen is hidden, the bank teller function result display screen is made visible, and the result data is updated for the teller to see.

The previous paragraphs provided high-level descriptions of the internal logic of the client-side JavaScript code. To be concise, the client-side logic doesn't handle many runtime errors that are possible in real-life situations. Be sure to do a code walk-through of Listing 1 and read the embedded comments there.

The next sections focus on the mid-tier service and wrap up the remainder of the bank scenario implementation.

The Web services architectural model

A Web service is an interface that describes a collection of operations that are network-accessible through standardized message formats such as XML and JSON. Web services are mainly used to do program-to-program interactions. Web services allow applications to be integrated more rapidly, easily, and less expensively. Integration typically occurs at a higher level in the protocol stack based on messages centered around business service semantics. They enable loose integration of business functions using a common approach both inside and outside the enterprise.

The key to reaching a common Web services model is a set of standards. Some such important standards include HTTP, SOAP, REST, XML, JSON, and WSDL (Web Services Description Language).

Figure 2 describes the important roles and interactions involved in the Web services model. These are the three common roles involved in the Web services architectural model:

  • Service provider
  • Service registry
  • Service requester

These are three common interactions involved in the Web services architectural model:

  • Publish
  • Find
  • Bind


Figure 2. Web services roles and interactions
WS Roles and Interactions

A service provider is the owner or platform of the service. A service requester or service consumer is the application that is looking for and invoking or initiating an interaction with a service. The service consumer role can be played by a browser application driven by a person or a program without a user interface (such as PHP, Java, Ruby, a servlet, an EJB). A service registry is a searchable directory of service descriptions where service providers publish their service descriptions. Service consumers use registries to find services and obtain information about services.

The Publish interaction allows a service provider to publish a service description so that the service consumer can find the service. The Find interaction allows the service consumer to retrieve a service description directly. A Bind interaction allows the service consumer to invoke or initiate an interaction with the service at runtime using the binding details specified in the service description.

A service description is an important aspect in the Web services model. WSDL is a standard that allows service providers to describe their services in a machine- and human-readable format. Figure 3 shows the structure of a WSDL document. The WSDL standard has two parts: service interface and service implementation. A service interface is an abstract or reusable service definition that can be referenced by multiple service implementations. A service implementation describes how a particular service is implemented by a given service provider.

In the WSDL service interface, types define custom XML data types that will be used in the interaction between the service consumer and the service provider. Messages specify which XML data types constitute various parts of the payload transferred between the service consumer and the service provider. Operations define what message can appear in the input and output of a Web service interaction. PortTypes define the allowed Web service operations. Finally, binding describes the protocol and data format.

In the WSDL service implementation, port element associates an endpoint (a network address location or URL) with a binding element in the service interface. A service element contains a collection of port elements.


Figure 3. Structure of WSDL
WSDL Structure

Implementing the Web service client

One of the bank actions that the teller performs is getting the current stock portfolio value of a given account holder. When the bank teller selects that option from the Web page, an HTTP request is sent to the mid-tier PHP service. Before the Bank Logic PHP module is called to compute the total net worth of the stock portfolio, it is required to send the current stock price as a parameter to it. To get the current stock price of the single stock that is held in the account holder portfolio, you need to access a remote Web service hosted on the Internet.

To access a remote Web service, it is necessary to generate a Web service client proxy using the WSDL. In PHP, it is easy to accomplish this with a few lines of code. The PHP SOAP Client library allows a Web service client to be dynamically generated from the WSDL supplied by the service provider. In this case, you will be using a .NET-based stock quote service offered for free on the Internet by WebserviceX.net. You may want to take some time to understand the WSDL file for the remote stock quote service by visiting the WebServiceX.net link provided in the Resources section.

Use the following steps to create a Web service client in PHP using the stock quote service WSDL published at WebserviceX.net:

  1. If not done already, switch to the Eclipse PHP perspective: Click Window->Open Perspective->Other->PHP and click OK.
  2. In the PHP Explorer view (top left frame), right-click the BankTeller project and select New->PHP File.
    1. Enter the file name as GetStockPrice.php.
    2. Click Finish.
  3. Replace the contents of this file by entering or pasting the source code in Listing 2 and save the file. You can review the comments in this file to understand the code, or refer to the next section, which provides a high-level description of the code logic.


Listing 2. Contents of GetStockPrice.php file
                
<?php
/*
============================================================
Project: End-to-End-Ajax application development

Purpose: This is an example scenario to be used in
 an IBM developerWorks article.

Last modified: May/17/2007.

This PHP module provides the logic to access a remote
Web service hosted on the Internet. This Web service
will return the complete and current stock details about a 
given stock ticker symbol.

This module shows how easy it is to consume a remote
Web Service (running on a .NET platform) using PHP
dynamically by pointing to a remote WSDL file for that
Web service.

It uses PHP SOAP Client library to accomplish that. 
============================================================
*/
   // Get the required Bank Logic PHP module.
   require_once("BankLogic.php");
   
   /*
   ============================================================
   Function: getStockPortfolioValue

   Last modified: May/17/2007.
   
   This function accepts an account holder name as input.
   In this scenario, all the account holders have a single stock
   in their respective portfolio. The code here obtains the 
   ticker symbol of the stock held by the given account holder.
   Then it uses PHP SOAP client to remotely invoke the 
   .NET based stock quote Web service and obtains the 
   XML-based response returned by that service. It parses the
   XML-based stock details response and gets the current 
   stock price of that stock. It then calls another function
   inside of the Bank Logic PHP module to update the database
   with the new portfolio value.
   ============================================================ 
   */   
   function getStockPortfolioValue($accountHolderName) {
      // From the Bank Logic module, obtain all the account
      // information stored in the database.
      // [If someone wants to optimize it, a new function
      // could be written directly to return the account 
      // information for the given account holder, instead of
      // retrieving all the account information.]
      $finalResult = getAllAccountInformation();
      
      // Ensure that all the account information was read from the DB.
      if ($finalResult["ResultCode"] != 0) {
         // Some error occurred. Return now.
         return($finalResult);
      } // End of if ($finalResult["ResultCode"] != 0)
      
      // Get the account info of the customer whom we are dealing with now.
      $accountInfoArray = $finalResult["AccountInfo"];
      $tickerSymbol = null;
      
      // Loop through all the available account information and filter
      // the account information for the account holder's name that was
      // passed as a parameter to the function we are in now.
      foreach ($accountInfoArray as $accountInfo) {
         // Is it the account for the person of interest? 
         if (strcasecmp($accountInfo["AccountHolderName"], $accountHolderName) == 0) {
            // Get the stock ticker symbol of the only stock held in this portfolio.
            $tickerSymbol = $accountInfo["StockName"];
            // We got it. Skip out of the loop now.
            break;
         } // End of if (strcasecmp($accountInfo["AccountHolderName"] ...
      } // End of foreach ($accountInfoArray as $accountInfo)
      
      // If there was an error in getting the ticker symbol, return now.
      if ($tickerSymbol == null) {
         $finalResult["ResultCode"] = 1;
         $finalResult["ResultMsg"] = 
            "Unable to get ticker symbol from the account of $accountHolderName.";
         return($finalResult);                  
      } // End of if ($tickerSymbol == null)
      
      // This is the WSDL published by the service provider.
      $wsdl = "http://www.webservicex.net/stockquote.asmx?WSDL"; 
      // The magic happens here in the next four lines to execute
      // a fairly complex function remotely over the Internet.
      // It is really cool. Why can't the other middleware runtimes follow
      // the simplicity of PHP to do such things.
      // Instantiate the PHP SOAP client library by pointing directly to
      // remote WSDL URL.
      $proxy = new SoapClient($wsdl, array("trace"=>1,"exceptions"=>0));
      // Set the required input parameter for the remote service.
      // In our case, it is just the ticker symbol. 
      $param['symbol'] = $tickerSymbol;  
      // Execute the remote logic. It is that simple.
      $result = $proxy->GetQuote($param); 
      // You have the XML-based response string now.
      $quoteResult = $result->GetQuoteResult;
      
      // Convert the XML formatted string into a DOM object.
      // PHP also does XML processing so elegantly and simply.
      $xml = simplexml_load_string($quoteResult);
      $stockPrice = 0.0;
      
      // The XML-based stock quote response is somewhat elaborative.
      // It contains information about different aspects of the given stock.
      // We are interested only in the current market price of that stock.
      // That information is available as: 
      // <Stock>...<Last>56.34</Last>...</Stock>
      // All we have to do is just parse the value of the <Last> XML element.
      // See for yourself how easy it is to do this in PHP.
      if (property_exists($xml, "Stock") == true) {
         // We have the <Stock> element in the Web service response.
         $stockInfo = $xml->Stock;
         
         // Now, check if the <Stock> element contains a child named <Last>.
         if (property_exists($stockInfo, "Last")) {
            // Just retrieve the last market price of this stock.
            $stockPrice = $stockInfo->Last;
         } else {
            $finalResult["ResultCode"] = 1;
            $finalResult["ResultMsg"] = 
               "Unable to get current stock price of " .
               "$accountHolderName's stock $tickerSymbol.";
            return($finalResult);            
         } // End of if (property_exists($stockInfo, "Last"))
      } else {
         $finalResult["ResultCode"] = 1;
         $finalResult["ResultMsg"] = 
            "Unable to get current stock price of " .
            "$accountHolderName's stock $tickerSymbol.";
         return($finalResult);         
      } // End of if (property_exists($xml, "Stock") == true)
   
      // Now that we have the current stock price, compute the
      // portfolio value and update it in the DB.
      // Return the result to the caller of this function.
      $finalResult = portfolioValue($accountHolderName, $stockPrice);
      return($finalResult);
   } // End of function getStockPortfolioValue   
?>
		

Logic in the PHP Web service client

The procedure you followed in the previous section to create a Web service client is just one of the simple approaches made possible by the PHP SOAP client library. The logic in GetStockPrice.php makes the Web service client to the stock quote service hosted at the URL http://www.webserviceX.net. The PHP SOAP client library directly points to the WSDL URL published by the service provider and dynamically creates an internal in-memory proxy for the service using the service description information encoded in the WSDL. Then, the proxy code makes the remote call as if the stock quote function is available locally on your machine. In this case, the client proxy uses the SOAP access protocol to interact with the remote stock quote service by sending it a stock ticker symbol as input and thereby receiving an XML document as output from the stock quote service.

The logic in GetStockPrice.php is simple because it contains only one function: getStockPortfolioValue. This PHP module requires the BankLogic.php module to make use of the database access functions available there. The getStockPortfolioValue function takes a parameter of an account holder name for which the stock portfolio value needs to be calculated and updated. It gets all the account information stored in the database and filters the one that belongs to the given account holder. Instead of fetching information for all the accounts, you could modify this code to get the account information for just that particular account holder. (This is an exercise that you might want to do later.) From the filtered account information, the ticker symbol for only one holding in the stock portfolio is obtained. Then, the PHP SoapClient object is instantiated by passing the stock quote WSDL URL as a parameter to the class constructor. The stock ticker symbol is added as an input parameter for the Web service. Then the GetQuote method on the proxy is called, which does a SOAP invocation of the remote Web service. On success, a Web service response is received as an XML-formatted string. PHP provides one of the simplest ways of turning that XML-formatted string into an XML DOM object structure. In that XML tree, an element named <Last> holds the current market price of that stock. The value of <Last> is parsed. Finally, a function named portfolioValue in the Bank Logic PHP module is called with the account holder name and the current stock price as parameters. That function calculates the new portfolio value using the current stock price, updates the database, and returns an associative array containing previous and current portfolio values. That associative array is returned to the caller of the getStockPortfolio function as well.

The result from the remote stock quote service is an XML document with a lot of information about the stock ticker symbol in question. Refer to Listing 3, which shows an example of the stock quote service output for the stock ticker symbol IBM. The output XML contains so much information, including the date and time of the reported stock price, open and close prices, percentage change in price, price high and low points on the trading day, trade volume, annual price range, earnings per share, and the P/E ratio of the stock. Among all of this information, what we are interested in is the last closing price for the given stock ticker symbol, which is available within the <Last> XML element. All the rest of the code in the getStockPrice method is about parsing the entire XML result from the Web service using a DOM parser.


Listing 3. Example of an XML response from the remote stock quote Web service
                
<?xml version="1.0" encoding="utf-8" ?> 
<string xmlns="http://www.webserviceX.NET/">
   <StockQuotes>
      <Stock>
         <Symbol>IBM</Symbol>
         <Last>101.17</Last>
         <Date>4/27/2007</Date>
         <Time>4:01pm</Time>
         <Change>+0.27</Change>
         <Open>100.30</Open>
         <High>101.17</High>
         <Low>100.06</Low>
         <Volume>6141359<Volume>
         <MktCap>152.3B</MktCap>
         <PreviousClose>100.90</PreviousClose>
         <PercentageChange>+0.27%</PercentageChange>
         <AnnRange>72.73 - 101.17</AnnRange>
         <Earns>6.262</Earns>
         <P-E>16.11</P-E>
         <Name>INTL BUSINESS MAC</Name>
      </Stock>
   </StockQuotes>
</string> 
		

Bank actions REST request dispatcher in PHP

We are almost finished with implementing and understanding the required artifacts. This last part, covering REST request dispatcher, glues together the code for all three tiers. PHP again makes it simple to receive REST service calls (HTTP POST in this case), to dispatch them to appropriate service functions, and then to return the results back to the browser clients. The data interchange between the browser clients and this PHP module is done using the JSON format and requires a JSON parser in PHP. We will use an open source JSON parser available at PEAR.net. This section provides both the implementation details and the description of the REST request dispatcher PHP module.

Use these steps to implement the REST request dispatcher PHP module:

  1. If not done already, switch to the Eclipse PHP perspective: Select Window->Open Perspective->Other->PHP and click OK.
  2. In the PHP Explorer view (top left frame), right-click the BankTeller project and select New->PHP File:
    1. Enter the file name as JSON.php.
    2. Click Finish.
  3. Open a browser and go to the URL: http://mike.teczno.com/JSON/JSON.phps:
    1. Copy all the contents displayed in your browser to a clipboard and close the browser.
    2. Replace all the contents of the JSON.php file in your Eclipse editor by pasting the contents from your clipboard.
    3. Click File->Save.
    4. Close the file.
  4. In the PHP Explorer view (top left frame), right-click the BankTeller project and select New->PHP File:
    1. Enter the file name as BankActions_REST_Service.php.
    2. Click Finish.
  5. Replace the contents of this file by entering or pasting the source code in Listing 4 and save the file by clicking File->Save.

The compact code logic in BankActions_REST_Service.php does the dispatching of browser client requests to get the bank teller functions performed. This PHP module depends on three other PHP modules. Two of them are part of this project (BankLogic.php and GetStockPrice.php) and the other one is an external open source JSON parser in PHP. We have already covered all the functions available in the two PHP modules. The JSON.php library is very easy to use. It brings the use of JSON as close as possible to the PHP programming environment. JSON.php provides a JSON parser with the following functions:

  • decode() converts JSON data to PHP object.
  • encode() converts a PHP associative array into JSON data structure.

Whenever a browser client makes RESTful (HTTP POST) service calls to the bank scenario mid-tier service, the code in BankActions_REST_Service.php first gathers the service input posted by the browser client. Input data posted by the browser client is available as key-value pairs in the PHP server's superglobal $_REQUEST associative array. Input data sent by the browser client contains two key-value pairs. One of them contains the bank teller command that needs to be performed. It usually is either a deposit, debit, or get/update portfolio value. The other data item contains the application-specific data required to perform the bank teller command. This data is encoded using JSON. After parsing the bank teller command and the service input data, the logic here uses the JSON parser to convert the JSON formatted text into a PHP object. Once that is done, a switch statement branches control to appropriate bank teller functions in the other included PHP modules. While calling those functions, required parameters parsed from the JSON text are passed to them. When the bank teller functions return, they provide the final results (success or failure) in a PHP associative array. Such final results data is converted into JSON formatted text and returned to the browser clients.

You have now completed the implementation and description of all the required code artifacts on all three tiers for the end-to-end bank scenario. Congratulations!


Listing 4. Contents of BankActions_REST_Service.php file
                
<?php
/*
============================================================
Project: End-to-End-Ajax application development

Purpose: This is an example scenario to be used in
 an IBM developerWorks article.

Last modified: May/17/2007.

This module is the REST service request handler for the
server-side of the bank scenario. It receives the 
REST (HTTP POST) requests from the single-page based 
bank teller browser clients. It dispatches different 
request requests to different bank logic functions.
It also sends and receives application-specific data
in a previously agreed JSON format. 
============================================================
*/
   // Get the other required PHP modules.
   // JSON.php is an open source JSON parser in PHP.
   // It can be obtained from: : http://mike.teczno.com/JSON/JSON.phps 
   require_once("JSON.php");
   // Bank Logic module has all the core bank teller server-side functions.
   require_once("BankLogic.php");
   // GetStockPrice is a Web service proxy client to a remote Web service.
   require_once("GetStockPrice.php");
   
   // Define all the constants that will be used here.
   define ("BANK_TELLER_COMMAND_KEY", "Bank_Teller_Command");
   define ("BANK_TELLER_POST_DATA_KEY", "Post_Data");
   define ("GET_ALL_ACCOUNTS_INFO", "Get_All_Accounts_Info");
   define ("DEPOSIT_TO_ACCOUNT", "Deposit_To_Account");
   define ("DEBIT_FROM_ACCOUNT", "Debit_From_Account");
   define ("GET_STOCK_PORTFOLIO_VALUE", "Get_Stock_Portfolio_Value");
   
   // XHTML form data from the bank teller Ajax browser client will be
   // available in one of the PHP superglobals 
   // i.e. $_REQUEST associative array.
   // Each REST request from the client will have two key-value pairs.
   // First one is the bank teller command that tells if the client
   // wants to perform deposit, debit, portfolio value etc.
   // The second key-value pair is the application specific data sent as
   // JSON-formatted string. 
   $bankTellerCommand = $_REQUEST[BANK_TELLER_COMMAND_KEY];
   $bankTellerPostData = $_REQUEST[BANK_TELLER_POST_DATA_KEY];
   
   // Using JSON in PHP is as easy as it can get.
   // Instantiate the JSON parser (available through JSON.php)
   $json = new Services_JSON();
   // In one statement, convert the JSON formatted text to
   // an equivalent PHP class structure. That is it.
   $bankTellerInput = $json->decode($bankTellerPostData);
   $responseToBeSent = "";
   $bankActionResult = null;
   
   // Switch through the bank teller command issued by the browser client.
   switch($bankTellerCommand) {
      case GET_ALL_ACCOUNTS_INFO:
         // Go and get all the account information stored in the database.
         $bankActionResult = getAllAccountInformation();
         break;
         
      case DEPOSIT_TO_ACCOUNT:
         // Perform the deposit account transaction.
         // Pass the account holder name and the deposit amount as 
         // sent by the browser client. Third parameter of 1 indicates DEPOSIT.
         // This function is available in BankLogic.php
         $bankActionResult = 
            accountTransaction(
               $bankTellerInput->accountHolderName,
               $bankTellerInput->amount, 1);
         break;
         
      case DEBIT_FROM_ACCOUNT:
         // Perform the debit account transaction.
         // Pass the account holder name and the debit amount as 
         // sent by the browser client. Third parameter of 2 indicates DEBIT.
         // This function is available in BankLogic.php         
         $bankActionResult = 
            accountTransaction(
               $bankTellerInput->accountHolderName,
               $bankTellerInput->amount, 2);         
         break;
         
      case GET_STOCK_PORTFOLIO_VALUE:
         // Perform the "Update Stock Portfolio Value" transaction.
         // This will require going out on the Internet to a remote Web service.
         // This function is available in GetStockPrice.php
         $bankActionResult = 
            getStockPortfolioValue($bankTellerInput->accountHolderName);
         break;
         
      default:
         // Invalid Bank teller command.
         $bankActionResult["ResultCode"] = 1;
         $bankActionResult["ResultMsg"] = "Invalid Teller Command";
   } // End of switch($bankTellerCommand)
   
   // We completed everything that the client asked us to do.
   // Let us return the final results in JSON formatted text.
   // It takes one line of code in PHP to convert a 
   // PHP associative array data structure into JSON formatted text.
   $responseToBeSent = $json->encode($bankActionResult);
   // Set the HTTP header to indicate that we are sending JSON plain text data.
   // There are also efforts underway at this time (MAY/2007) to define a 
   // new content type called text/json.
   header("Content-Type: text/plain");      
   // That is it. Return the JSON formatted response in 
   // RESTful way back to the browser now.
   echo($responseToBeSent);
?>
		

Integrating the bank scenario components

At this point, you have finished developing the Ajax client, PHP mid-tier service, and MySQL database components required in the bank scenario. They are as shown here:

  • BankDB (Bank database based on MySQL)
  • BankLogic (Core logic for bank teller functions inside a PHP module)
  • BankPortal (A bank teller Web client using Ajax technologies)
  • BankActions (Bank teller actions REST service in PHP)
  • Stock Quote Web service client (SOAP proxy using PHP SOAP Client)

These components handle a specific task in our bank scenario. As you have observed in this three-part series, each component depends on some other component to connect the end-to-end picture of the bank scenario. In typical three-tier Web applications, the underlying frameworks such as .NET demand a detailed configuration of the dependencies to integrate the various application components. The technologies chosen to implement this scenario differentiate themselves by not requiring such complex integration requirements. All the needed dependencies were already taken care of while implementing these components. There is no application-specific runtime integration required with Ajax (XHTML, CSS, JavaScript, REST, JSON), PHP, or MySQL, thus eliminating significant overhead and complexity in an end-to-end application.

Deploying the bank scenario components

Now that you have completed developing and integrating part of the bank scenario components, it is time to deploy them on the Zend Core PHP server. As a reminder, Zend Core runs on top of the Apache Web server.

  1. Ensure that the Eclipse PHP perspective is active: Click Window->Open Perspective->Other->PHP and click OK.
  2. Ensure that the Window->Web Browser->Internal Web Browser option is selected.
  3. In the PHP Explorer view (top left frame), right-click the BankTeller project and select Run As->Run:
    1. In the left frame of the Run dialog box, expand the option titled "PHP Web Page" by clicking on the + sign next to it.
    2. Ensure that you see an entry called New_configuration present under the "PHP Web Page" option.
    3. If you don't see New_Configuration entry, right-click PHP Web Page and select New to create an entry.
    4. Click on the entry New_Configuration.
    5. On the right panel of the same dialog, you will see various configuration parameters for New_Configuation. Ensure that the Server field says Default PHP Web Server.
    6. In the File/Project field, click the Browse button, select BankTeller, and click OK.
    7. Ensure that the Publish files to Server check box is selected.
    8. In the Publish To: field, append the text BankTeller so that this configuration parameter reads C:\Program Files\Zend\Apache2\htdocs\BankTeller.
    9. Ensure that the Auto Generate check box is selected.
    10. Click Run.

These steps deploy the files to Zend Core and start the Eclipse internal Browser. You can close the Eclipse internal browser now.

If you noticed that the Bank Teller application main menu appears in the Eclipse Browser screen, everything has gone smoothly so far. If not, you need to ensure that you didn't inadvertently skip some steps. That is all it takes to deploy and run the end-to-end Ajax application.

Testing the bank scenario components

Finally, the moment has arrived to enjoy the fruits of all the work you have done so far in this series! The bank scenario is in a state to be tested. Follow these steps to test the bank teller actions by exercising the code in all three tiers you developed and deployed:

  1. From the Eclipse tool bar, click on the Eclipse internal Web browser (a globe-like) icon.
  2. Ensure that the Eclipse internal browser is started.
  3. In the URL address field of the browser, enter http://localhost/BankTeller as the URL for the bank scenario application running on the local machine and press Enter.
  4. This brings up the main screen for the bank teller application as shown in Figure 4. As explained in this article, this is the one and only Web page for the bank teller application. You can keep monitoring the URL while testing this application. It is the only URL you will see for the entire application. There are several sections or screens for this application that are all part of this single page. At this time, all the other user interface controls for those screens are hidden and only the main screen is made visible. The gist of this Ajax feature is that there will be no more page refreshes coming from the server for this application during this browser session.
  5. You can move your mouse over and out of the menu options. You will see the menu items highlight and unhighlight. This is a simple Ajax feature that is done by just changing the CSS class style of the <SPAN> element that holds the menu text, during mouseover and mouseout events.
  6. Click the first menu option "Deposit to an account," which brings the deposit screen up in an Ajax way (no server help involved in getting the page). You should see a list box with all the current account holders. This account data was obtained from the mid-tier server in the background using the Ajax asynchronous XHR capability. You can also notice the page footer that displays a fake bank teller name and it provides an option to go back to the main menu.
    1. Select an account owner Merry and enter a deposit amount of 125.00 as shown in Figure 5.
    2. Click Deposit. You should see a result page with the transaction details as shown in Figure 6.
    3. Click the Back to main menu link in the page footer.
  7. Click on the second menu option Debit from an account.That opens the debit screen instantaneously without any wait.
    • Select an account holder, enter the debit amount, and click Debit.
    • Check the result data returned from the server.
    • Click the Back to main menu link in the page footer.
  8. Click on the third menu option Current portfolio value. This opens the portfolio value screen instantly:
    • In the list box, select the account holder Gandalf as shown in Figure 7.
    • Click Get Stock Portfolio Value. You should get a result screen with the updated stock portfolio value as shown in Figure 8. This bank teller action sent a REST request to the mid-tier PHP service, which in turn created a dynamic Web services client for a remote stock quote service and called that service. The new stock price was updated in the database and the new portfolio value was calculated and returned to the browser client.
    • Click the Back to main menu link in the page footer.
  9. Close the Eclipse internal Web browser.

This testing procedure let you see how seamlessly this single-page Web application performed the non-trivial tasks by interacting with the application artifacts on the mid-tier and data-tier.


Figure 4. Bank Teller main screen
Main screen

Figure 5. Bank Teller deposit screen
Deposit screen

Figure 6. Bank Teller deposit result screen
Deposit result screen

Figure 7. Bank Teller portfolio value screen
Portfolio value screen

Figure 8. Bank Teller portfolio value result screen
Portfolio value result screen

Debugging options

In any software development environment, debugging tools are crucial contributors to the speed and quality of project execution. In multitier Web applications such as our bank scenario, debuggers are an indispensable part of a developer's tool set. A desirable capability is to debug all the tiers of a Web application inside of a single IDE -- that is yet to become available. However, the recent Eclipse-based open source efforts such as PHP Development Tool (PDT) and Aptana Web IDE are very promising. Because they both plug into the same Eclipse platform, we can anticipate that they will soon deliver the end-to-end debugging capability by letting developers set a breakpoint in a JavaScript function and another breakpoint in a PHP function. That will let you debug a single end-to-end transaction on both the browser and then on the Zend Core server.

On an individual basis, both PDT and Aptana have excellent debugging features for PHP and JavaScript, respectively. Aptana has nice integration with popular browsers such as Firefox, Internet Explorer, Safari, and Opera to do JavaScript debugging. Similarly, Firebug (a Firefox add-on) provides superior debugging features for JavaScript. However, at the time of this writing, these debugging tools are not yet ready to work together to provide end-to-end debugging capability from the browser code to the server code.

In the absence of an end-to-end debugger at this time, the following debugging procedures are explained in this article. Both of the following methods described work well and they provide a way to debug our end-to-end bank scenario on the server and the browser separately.

  1. Debug the PHP code on Zend Core server using the PDT debugger.
  2. Debug the client-side JavaScript code on Firefox browser using the Firebug debugger.

Debugging the server-side PHP code

Perform the following steps to debug the mid-tier PHP server logic using the PDT debugger. The instructions below assume that you have deployed the bank scenario application and have done the normal testing as directed in the previous two sections.

  1. Ensure that the Eclipse PHP perspective is active: Click Window->Open Perspective->Other->PHP and click OK.
  2. Ensure that the Window->Web Browser->Internal Web Browser option is selected.
  3. As of this writing, PDT and the Zend debugger will work only if the initial page of the application is a PHP file. Because of this, you must rename the index.html file to index.php. This just changes the file extension and does not affect the application behavior at all:
    1. In the PHP project explorer view, under the BankTeller project, right-click index.html and select Refactor->Rename.
    2. Change the filename to index.php.
  4. In the Eclipse PHP project explorer view, right-click the BankTeller project and select Debug As->Debug:
    1. In the left pane of the resulting Debug dialog box, select PHP Web Page->New_configuration. This brings up a right pane that has a Debug button at the bottom of the dialog box.
    2. Click Debug.
    3. If you are prompted to open the PHP Debug perspective, click Yes. You are taken to the PHP Debug perspective with both the index.php file and the Eclipse internal browser opened. The debugger will stop at the top of the index.php file (which contains only HTML script and not any PHP code).
    4. Quickly, click the Resume icon (a green arrow with a yellow bar) available in the Debug view (top left of the screen). If you take more than 60 seconds to do this, the internal browser times out and you have to start the debugging steps from the beginning.
    5. At the initial page load time, we use the Ajax XHR to get all the account information from the server. When that XHR request goes to the Zend Core server, the debugger stops at the first line in the REST Request dispatcher PHP file. (The debugger is configured to stop at the first line of the PHP file being executed. This is in addition to any breakpoints we may set).
    6. You can now use the debug features such as step into a function or step over a function using the icons located at the top of the Debug view.
    7. While using the step into and step over features, you can also verify the values of your application variables in the PHP code. This can be done inside the Variables view available in the Debug perspective.
    8. To avoid the browser timeout, do your tracing quickly within 60 seconds and click the Resume icon (a green arrow with a yellow bar icon) available in the Debug view.
  5. Let's check the feature of stopping at a particular breakpoint in the PHP code instead of stopping at the first line every time the PHP code is executed:
    1. Switch to the PHP perspective.
    2. In the BankTeller project, open the file BankActions_REST_Service.php.
    3. In that file, locate the PHP statement: $json = new Services_JSON().
    4. Double-click on the line number of this statement (in the far left of the editor). This inserts a breakpoint icon for this statement.
    5. Go to the end of the same file and set another breakpoint at the last line: echo($responseToBeSent).
    6. Switch to the Eclipse PHP Debug perspective.
    7. If the Eclipse internal browser is open at this time, close it now.
    8. In the Eclipse menu bar, select the Run->Debug menu item. This opens the Debug dialog box.
    9. In the right pane of this dialog, click on the Advanced tab.
    10. In the Breakpoint section, select the check box that says Override project/workspace Break at First Line. Leave the Break at First Line check box unchecked.
    11. Click Debug. This starts the internal browser and directly jumps to the first breakpoint set at the JSON statement. It stops here because the XHR request to get all the account information got fired during the initial page load.
    12. You can click on the Resume icon in the Debug view. The program proceeds and stops at the second breakpoint at the end of the file. You can click the Resume icon again to continue the execution.
    13. Now you can switch to the internal browser and perform any of the bank teller operations.
  6. In the PHP Debug perspective, click on the Breakpoints view.
    1. Right-click on any one of the breakpoints and select Remove All.
    2. If you are prompted with a confirmation dialog, click Yes to remove them.

Note: As of this writing, you need to be aware of the following minor PDT debugging issue. During the PHP debug session, you may see one or more messages like this in the Debug view once you complete the debugging of a PHP script: <terminated> New_configuration [PHP Web Page]. Don't remove them until you finish debugging the entire application. As of this writing, if you remove those messages, subsequent application function can not be debugged in the same Debug session and you may have to start a new Debug session. This problem and the internal browser timeout problem are likely to be fixed by the time PDT moves from a release candidate (RC3) to an officially released version.

Debugging the client-side JavaScript code

Use the following steps to debug the client-side JavaScript logic using the Firebug debugger you installed in Part 1 of this article series. These instructions assume that you have deployed the bank scenario application and did the normal testing as explained in the previous sections.

  1. Start the Firefox browser on your machine.
  2. Enter the URL: http://localhost/BankTeller.
  3. Ensure that the Bank teller application main menu is displayed on the browser screen.
  4. In Firefox menu bar, click Tools->Firebug->Open Firebug. That opens the Firebug window at the bottom portion of the Firefox browser window.
  5. Click the HTML tab in Firebug window:
    1. Expand the <body> section of the HTML.
    2. In the HTML text displayed there, hover your mouse on <div id="mainPage". This highlights the portions of the application screen that are contained within the mainPage <DIV> element.
    3. Hover your mouse on the text <h1 title=... and see that the application title is highlighted now.
    4. Verify the same for the tellerOptions and pageFooter <DIV> elements.
    5. You can see that pageFooter is hidden on this screen; but, Firebug is able to show you that.
  6. Click the CSS tab of the Firebug window. You can view the entire CSS file of this application.
  7. Click the Script tab of the Firebug window. You can view all the JavaScript files used in this application.
    1. At the top of the Firebug window, click on the small down arrow displayed next to the name of the JavaScript file. You can select any of the JavaScript files listed there and view the code.
  8. Click the DOM tab of the Firebug window. It displays the entire DOM structure of the bank teller browser application.
  9. Click the Net tab of the Firebug window:
    1. This lets you view all the network activity that occurred so far in the bank teller application.
    2. You can individually select HTML, CSS, JavaScript, and Images and see the HTTP Headers, Post data, and Response data.
    3. This also displays a nice graphical view of the time it took to fetch a selected content type.
    4. On the top of the Firebug window, click XHR. This displays the XHR activity that happened in the bank teller application. So far, there was only one data interchange that was done using XHR. The REST resource that was accessed on the server is shown.
    5. Expand the entry BankActions_REST_Service.php.
    6. Click the Post and Response tabs to view the JSON request and response data exchanged with the mid-tier service.
  10. With that much Firebug background, let's try to debug some JavaScript code:
    1. Click the Script tab of the Firebug window.
    2. Using the small down arrow at the top of the Firebug window, select the BankTeller.js.
    3. Scroll to the end of the BankTeller.js file contents where the last function in this file is defined.
    4. In that last function, click on the line number for the source line that reads: var textResult = teller_request.responseText;.
    5. This sets a breakpoint for this line. (Another mouse-click toggles the breakpoint. In this case, leave the breakpoint active for this line.)
    6. Scroll up to the previous function: portfolioAction_Async().
    7. Set a breakpoint at the first source statement for this function.
    8. In the Bank Teller application menu (at the top half of the browser window), select option 3 for the current portfolio value. Instantly, you will be at the portfolio value screen.
    9. Select an account holder and click Get Stock Portfolio Value. The JavaScript execution stops at the breakpoint inside of the portfolioAction_Async() function.
    10. In the bottom right side of the Firebug window, you can see icons for continue, step over, step into, and step out debugger options.
    11. Click on the step over icon (or press the F10 key). This advances the execution to the next line.
    12. You can step over few lines and also view the variable values in the right side of the Firebug window.
    13. You can step over a few more lines and see how a JSON object is created and then converted to a JSON formatted string.
    14. Click the Continue icon (or press the F8 key).
    15. That finishes execution of the current function -- sending a request to the mid-tier server to get the portfolio value.
    16. If your machine is connected to the Internet, the mid-tier PHP service will invoke the remote stock quote Web service and return a result back to the server. This causes the JavaScript code to stop at our other breakpoint set at the XHR callback function.
    17. Press F10 (step over) continuously through all the statements of this function. Notice the JSON response received from the server and how that is parsed safely using parseJSON (courtesy of Douglas Crockford). Toward the end of this function, you will also see for yourself how we hide one application screen (get portfolio) and show another application screen (teller operation results) without ever doing any page refresh from the mid-tier server.
    18. Click the Back to main menu link in the page footer of the bank teller application.
    19. Close the Firebug window.
  11. Close the Firefox browser.

That's all it takes to debug any Ajax application using Firebug. It has many easy-to-use features nicely integrated with the Firefox browser. In my opinion, Firebug has clearly set a benchmark and an expectation for future debugger products through its elegance and simplicity.

A quick summary

In all three parts of this article, you have gone through different stages of developing an end-to-end Ajax application. One of the goals of this exercise is for you to see many of the improvements that Ajax-style Web applications offer. Some of those improvements are listed in the next section. If you were able to see them through our end-to-end Ajax application, then this article has met its objective.

Points to remember

  • In this Ajax application, did you ever click and wait for page refreshes? Probably not, and never.
  • Fast response time is achieved here by localizing all the client logic in a single browser application.
  • When you clicked on the Deposit, Debit, and Get Stock Portfolio Value buttons, this application communicated with the PHP mid-tier server to exchange tiny amounts of data. (This greatly improves the network bandwidth usage.)
  • It appears as if you are navigating to different pages while using this application. In reality, this effect was created by simply hiding and showing different DIV elements that are all placed on a single HTML page.
  • Designing Web applications in this Ajax style will make it possible to bring rich user interaction patterns to a browser. This results in improved customer and user satisfaction.
  • This way of architecting Web applications will also reduce the burden on the mid-tier server because the server is no longer contacted for unnecessary HTML page refreshes. Instead, the server is contacted only when data interchange is required.
  • While working with this application, did you notice the browser URL change? It never changes, and it was set to just one URL for the entire application. This is what makes it a single-page browser application. The presentation content (HTML, CSS, and JavaScript) was downloaded from the Web server only once at the very beginning.

Conclusion

The developerWorks Ajax resource center
Check out the Ajax Resource Center, your one-stop shop for information on the Ajax programming model, including articles and tutorials, discussion forums, blogs, wikis, events, and news. If it's happening, it's covered here.

You have just completed the journey of developing an Ajax application that spans across all three tiers (client, middle, and data tiers). You used several open source technologies as the middleware stack to build and deploy this application. In that process, you used some of the core features of Zend Core and some Eclipse IDE tools. You gained insights into the concepts of MySQL, PHP, Ajax (XHTML, CSS, JavaScript, XHR), REST, JSON, XML, and a Web services client. In the Eclipse IDE, you gained exposure to working in the PHP and Aptana perspectives. You also learned about deploying and testing with the Zend Core/Apache server. Code artifacts for the JavaScript client-side logic, a PHP Web services Client, a PHP REST request dispatcher, and the stock quote WSDL used in this part of the article series are included in a downloadable file included with this article (see Download).

Our non-trivial bank scenario nicely covers the basics of Ajax, PHP, and MySQL. With the basic knowledge you obtained in this three-part article series, you can now go on to extend your knowledge into other advanced areas of Web application development using these powerful open source offerings.

In summary, this article series demonstrated the power of Zend Core and Eclipse (PDT and Aptana) development environment. Both of them are credible choices for small and medium businesses that are either looking into selecting an open-source middleware stack or evaluating a migration path from .NET environment into an open source stack. These runtimes and Eclipse-based tools also provide a natural extension into mission-critical commercial offerings of products and services from several vendors. Finally, full credit goes to the people at Zend and Aptana for bringing simplicity into this new era of Web development through their open source work on Zend Core, the Eclipse PDT, and Aptana Web IDE.



Download

NameSizeDownload method
wa-aj-end2end3.zip28KB HTTP

Information about download methods


Resources

Learn

Get products and technologies

About the author

Photo of Senthil Nathan

Senthil Nathan is a senior software engineer at the IBM T.J. Watson Research Center in Hawthorne, New York. He has 22 years of experience in building software for different kinds of enterprise applications. His current professional interests are fully buzzword-compliant -- including SOA, Web services, Java 2 Platform, Enterprise Edition (J2EE), PHP, Ruby On Rails, Web 2.0, and Ajax development.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development, Open source, XML
ArticleID=240616
ArticleTitle=End-to-end Ajax application development, Part 3: Integrate, test, and debug the application
publish-date=07192007
author1-email=sen@us.ibm.com
author1-email-cc=ruterbo@us.ibm.com

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers