AJAXian GOOGLE Search:
home / examples / Google Service Invocation example overview  
INTRODUCTION  
This article describes the implementation of the 'Ajaxian Google' example. The example is available at: http://www.themidnightcoders.net/examples/googleonsteroids/googleonsteroids.htm

The 'Ajaxian Google' example demonstrates integration with the Google XML web service. The user interface of the example is as basic as the main Google search page. The search query text field is sensitive to the user input. As soon as the script detects user idle time or when the user enters space or presses Enter, the script sends a search request to WebORB. The WebORB server runs a .NET application which uses a web reference to the Google web service. When WebORB receives client request, it sends a SOAP request to Google, receives a response and passes it to the client. The client application gets the response and displays it in the same web page. Below is a diagram demonstrating the relationship between the architectural components of the example.

  IMPLEMENTATION
The client side script uses the Rich Client System to perform object binding and to handle remote method invocations. The script binds to a .NET object running in a WebORB server. There are 2 reasons why requests must go through an intermediary (the WebORB server) rather than hitting Google web service directly:
  1. Some browsers do now allow direct connections to 3rd party hosts. Requests can be sent only to the server where the page is loaded from. The example page is loaded from the same machine where the WebORB server is running.
  2. The example uses a license key we received from Google. Since we cannot expose our license key, we have to hide it in our server side class. The license key supports only 1000 queries per day. When the limit is reached, a user can enter his own Google key to perform additional searches.

The full source code for this example is available at: http://www.themidnightcoders.com/examples/googlesearch.zip

To install and run the example locally, follow the instructions below:

  1. Download and install WebORB for .NET
  2. Unzip the contents of googlesearch.zip into a temporary directory. The example includes both client and server side code.
  3. Copy bin/Debug/googlesearch.dll into the /bin folder of the weborb's ASP.NET application directory
  4. Copy googleonsteroids.html and /googleonsteroids_files to the root folder of the weborb's application directory
  5. Obtain a license key from Google to use Google's Web API
  6. Run the example by opening the following URL in a browser: http://localhost/weborb/googleonsteroids.htm
  SERVER CODE
The server-side code is a C# class. The .NET project imports a web reference pointing to the Google service WSDL: http://api.google.com/GoogleSearch.wsdl. Proxy class generated for the web reference is placed into the googlesearch.com.google.api namespace:

Below is the server-side implementation class:

using
System;
using googlesearch.com.google.api;

namespace googlesearch
{
  public class GoogleSearch
  {
    public GoogleSearchResult runGoogleSearch( string query, string googleKey )
    {
      // check if the user provided his own google key, if not default to ours
      if( googleKey == null )
        googleKey = "************ our google code goes here ***********";

      // create google proxy object
      GoogleSearchService service = new GoogleSearchService();
      // send out search query to google
      GoogleSearchResult searchResult = service.doGoogleSearch(
                                       googleKey, query,
                                       0, 10, false, "", false, "", "", "" );
      // return search result 'as-is' - WebORB and the Rich Client System will make
      // sure the data structure on the client side is identical to the return value here
      return searchResult;
    }
  }
}

  CLIENT CODE
The client side is implemented in JavaScript. It uses the Rich Client System to bind to the server object and perform remote method invocations. The client code is responsible for the following tasks:
  1. Perform remote object binding to make sure a proxy is always ready to perform search queries
  2. Detect user idle time, check user input for the space character or Enter key
  3. Notify use of any pending search requests
  4. Display search results

To import the Rich Client System, the script uses the external script loading mechanism:

      <script type="text/javascript" src="../WebORB.js"></script>

Object binding

The script performs object binding upon loading of the page. The <body> element of the page registers a 'onload'  event handler:

      <body onload="bind()">

In addition to the remote object binding, the 'bind' function performs some additional startup initialization tasks. The function is implemented as follows:

function bind()
{
   //hide the animation notifying the user when the search is in progress
   document.getElementById( "animation" ).style.visibility = "hidden";
  
   // if the proxy object does not exist - create one.
   // The arguments for the webORB.bind call are:
   //   1. "googlesearch.GoogleSearch" - classname with namespace of the server side class
   //   2. "weborb.aspx" - URL of the WebORB handler. Designates that requests sent by
   //      the Rich Client System must be processed by WebORB

   if( googleProxy == null )
     googleProxy = webORB.bind( "googlesearch.GoogleSearch", "weborb.aspx" );

  // create new request stack - will be used as a mechanism to ignore multiple search calls
  requestStack = new Array();
}

Handling user input

The search query text field registers a onkeypress handler:

             <input type=text id="searchtext"
                    name="searchtext"
                    size="54"
                    onkeypress="handleKeyPress( event )">

The handleKeyPress() function checks the conditions for sending out a search query. The function's implementation can be found below:

function handleKeyPress( evt )
{
   // get the time when the user pressed a key and save it in a global variable.
   // the script will use the value to check the for user idle time

   lastKS = new Date().getTime();

   // make sure we have a cross-browser compatible event object
   evt = (evt) ? evt : ((event) ? event : null);

   // get the key code from the event
   var code = evt.charCode ? evt.charCode : evt.keyCode;

   // if the user entered a space character or pressed Enter, do the search,
   // otherwise schedule a timer to call the runSearch function in 300 ms.

   if( code == 32 || code == 13)
     search();
   else
     setTimeout( runSearch, 300 );
}

The runSearch function checks if there was any typing activity since the last keystroke. If no key has been pressed, the function requests a search to take place:

function runSearch()
{
  if( new Date().getTime() - lastKS > 250 )
    search();
}

Remote method invocation

The search() function is where the script sends out a remote method invocation on an object hosted in WebORB:

function search()
{
  // get the search query text
  var search = document.getElementById( "searchtext" ).value;

  // get the user' google key
  var googleKey = document.getElementById( "gkey" ).value;  

  // push the request onto the stack.
  // the stack is used to ignore intermediary calls if there are any
  requestStack.push( search );

  // turn on the animation to indicate that a search query is in progress
  document.getElementById( "animation" ).style.visibility = "visible";

  // create an async object with pointers to the functions where
  // the response should be delivered to. one function is for successful invocations
  // and the other is for errors and exceptions

  var async = new Async( processSearchResults, processError );

  // issue a remote method invocation
  googleProxy.runGoogleSearch( search, googleKey.length != 0 ? googleKey : null, async );
}

Processing results

The page HTML declares a block level element to contain the markup for the search results:

     <div id="searchResults"></div>

Upon a successful method invocation, the Rich Client System executes the first function from the Async object - processSearchResults. The function's argument is an object returned from the C# code shown above. The Rich Client System ensures that the data structure of the object is the same as on the server side. Since the C# code does a pass through on the response value, the result has the same fields as declared in the Google web service API documentation.

function processSearchResults ( result )
{
  // pop a request from the stack
  requestStack.pop();

  // if the stack is not empty, ignore this response - there is more on the way
  if( requestStack.length > 0 )
    return;

  // get a reference to the search results block level element
  var searchResults = document.getElementById( "searchResults" );

  // clear out any existing search results
  searchResults.innerHTML = "";

  // start building new search results markup
  var searchContent = "";
 
  // iterate over the search results in the result object and create a markup
  // that would look similar to the original google search results

  for( var i = 0; i < result.resultElements.length; i++ )
  {
    searchContent += "<a style=\"font-size: 12pt; color: #0000FF; text-decoration: " +  
                     "underline\" href=\"" + result.resultElements[ i ].URL + "\">" +
                     result.resultElements[ i ].title + "</a><br>";

    if( result.resultElements[ i ].snippet )
      searchContent += result.resultElements[ i ].snippet + "<br>";

    searchContent += "<font color=\"#339933\">" + result.resultElements[ i ].URL +
                     "</font><br><br>";
  }
 
  // hide the animation
  document.getElementById( "animation" ).style.visibility = "hidden";
  // render the new search results
  searchResults.innerHTML = searchContent;
}

.Error handling

The error handling in the example is limited to displaying the error to the user:

function processError( error )
{
   // pop a request from the stack
   requestStack.pop();
   // use the same search results element to render the error
   var searchResults = document.getElementById( "searchResults" );

   // render text of the error/exception
   searchResults.innerHTML = "Server reported an error: <b>" + error.description
                              "</b><br><br>";
   // render the server-side stacktrace
   searchResults.innerHTML += error.details;
}
..

Copyright © 2003-2005 Midnight Coders, LLC.  Privacy Policy | Contact Webmaster
Home | Products | Customers | Download | Licensing | Forum | Developers | About