• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Oracle XE   AJAX: Asynchronous XML in Action
 

Oracle XE AJAX: Asynchronous XML in Action

on

  • 1,279 views

 

Statistics

Views

Total Views
1,279
Views on SlideShare
1,279
Embed Views
0

Actions

Likes
0
Downloads
3
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Microsoft Word

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Oracle XE   AJAX: Asynchronous XML in Action Oracle XE AJAX: Asynchronous XML in Action Document Transcript

    • About the Authors Przemek Piotrowski has been following Oracle 10g Express Edition (Oracle XE) development since its early beta stages back in 2005 and can easily distinguish the new features in this first database from Oracle that’s free to develop, deploy, and distribute. Oracle XE’s small footprint doesn’t stop him from taking advantage of many of its advanced features and plugging them into existing Web architectures. Mark Townsend is vice-president of database product management in Oracle’s Server Technology Division. His responsibilities include requirement analysis, release planning, coordinating database product management activities, communicating with analysts and the press on database topics, and the development and delivery of field technical training. He was product boss for Oracle XE. Mark has been with Oracle since 1991 and has specialized in the Oracle database for over 15 years. Cutout: Build dynamic AJAX Web pages with transparent, asynchronous requests sent directly to Oracle Database 10g Express Edition. Serve dynamic content that doesn’t require a full-page refresh on the client side by leveraging the full potential of JavaScript’s Document Object Model, Oracle XML DB, and Embedded PL/SQL Gateway. Oracle XE + AJAX: Asynchronous XML in Action Building a real application by Przemek Piotrowski and Mark Townsend Build dynamic AJAX Web pages with transparent, asynchronous requests sent directly to Oracle Database 10g Express Edition. Serve dynamic content which doesn't require full page refresh on the client-side by leveraging the full potential of JavaScript's Document Object Model, Oracle XML DB and Embedded PL/SQL Gateway. This article covers the process of building a real working AJAX application on top of Oracle’s free database software. The demonstration
    • takes advantage of features integrated directly into Oracle Database 10g Express Edition (Oracle XE),: the Embedded PL/SQL Gateway and Oracle XML DB, which let you serve XML content derived from SQL queries directly over HTTP protocol. You’'ve probably run up against one of the numerous frameworks for AJAX that were probably Java- or .NET- based. Although they perfectly fit large-scale environments perfectly, smaller projects may not require such overwhelming resources as a J2EE stack. Besides, understanding the technology behind XMLHttpRequest may help you use asynchronous requests better under just about any Web development environment. Introduction The concept is surprisingly simple - to make Web applications more responsive so that they behave like regular desktop software. Waiting for a page to reload, just to see a change in a single place seems like a complete waste of both time and bandwidth. Having said that, using AJAX to create Web applications is good not only for the end user, but for the Web server, as well. Applying this relatively simple technique boosts your Web applications’' usability many times. The potential of AJAX is so big that Web applications now have a real chance of replacing desktop programs without degrading the user experience. Prerequisites All you need is access to an Oracle XE database instance and its WebDAV folders. Oracle XE is much more than just a database server: Embedded PL/SQL Gateway makes it a regular Web server, while the built-in WebDAV and FTP listeners let you connect and store files served over HTTP directly through one of these protocols. This tutorial assumes that you have Oracle XE installed on your local computer. Sso that URLs refer to localhost, you may need haveto adjust it to fit your network's configuration. I’ will be using the sample HR schema that ships with Oracle XE. By default it remains unlocked after installation so you need to unlock it first using the SYSTEM account through SQL*Plus (this command will also set a new password for HR):
    • SQL> alter user hr identified by hr account unlock; User altered. On Windows accessing Oracle XE’'s storage area is trivial with the Web Folder Access feature of Microsoft Internet Explorer;, just select File > Open and type http://localhost:8080 in the address field selecting and the Open as Web Folder check box. Then you’'ll be prompted for a login and password., Supply hr twice here. If working under Linux or Windows you may choose to leverage the FTP server built -into the XDB component of Oracle Database 10g. You activate it by connecting via SQL*Plus as SYSTEM and issuing the following commands: Connected to: Oracle Database 10g Express Edition Release 10.2.0.1.0 - Production SQL> exec dbms_xdb.setftpport('21'); PL/SQL procedure successfully completed. SQL> alter system register; System altered. The former first command enables the FTP listener in the database, while the latter registers the just- activated listener on PMON (process monitor) without actually waiting for its 60-second refresh cycle. Now you can connect to the server with any FTP client or directly from the command line (ftp localhost). Now we can upload static files into the database storage area. What about dynamic content? The Embedded PL/SQL Gateway -– introduced for the first time within Oracle XE -– enables you to access stored procedures directly from a Web location. Using the http://SERVER_ADDRESS:PORT/apex/SCHEMA_NAME.PROCED URE_NAME syntax one can use PL/SQL stored procedures to trigger
    • database events and generate content. For security reasons this feature has been disabled by default in Oracle XE so you have to unlock it first. Since this is a component of Application Express you need have to modify the WWV_FLOW_EPG_INCLUDE_MOD_LOCAL function inside the FLOWS_020100 schema. As shown in Listing 1 below, yYou need have to make two changes: comment out the return false statement just after begin and add the name of a stored procedure you’d would like to grant external access to (in this tutorial we’'ll be running HR.AJAXE stored procedure listed later). create or replace function wwv_flow_epg_include_mod_local(procedure_name in varchar2) return boolean is begin -- return false; -- remove this statement when you modify this function -- -- Administrator note: the procedure_name input parameter may be in the format: -- -- procedure -- schema.procedure -- package.procedure -- schema.package.procedure -- -- If the expected input parameter is a procedure name only, the IN list code shown below -- can be modified to itemize the expected procedure names. Otherwise you must parse the -- procedure_name parameter and replace the simple code below with code that will evaluate -- all of the cases listed above. -- if upper(procedure_name) in ( 'HR.AJAXE') then return TRUE; else return FALSE;
    • end if; end wwv_flow_epg_include_mod_local; This strict policy on running stored procedures prevents against one from executing arbitrary PL/SQL code on the server side, which would be a major security risk. Using a list of allowed procedures' names makes it very convenient to limit execution. To finalize this step, grant execute privileges on the AJAXE procedure to PUBLIC (in SQL*Plus while logged in as SYSTEM or HR): SQL> grant execute on hr.ajaxe to public; Grant succeeded. Outputting XML from SQL Queries You can generate XML directly from SQL queries using the PL/SQL package DBMS_XMLGEN, which does n’ot depend on Java and thus sois available on XE. The query process takes four basic steps: 1. Get the context by supplying a valid SQL query 2. Set options on the newly created context using the DBMS_XMLGEN procedures (optionally) 3. Get the XML result by using getXML() to get CLOB or getXMLType() to obtain XMLType. At this point you can also limit the numbers of rows returned by the XML engine using the setMaxRows() procedure on the context 4. Reset the query to perform step 3 again or run closeContext() to free up allocated resources. Unfortunately DBMS_XMLGEN has one flaw that you’ll would run up against almost immediately when working with remote XML -– HTP.PRN, that whichwill be used to print the output accepts strings of a VARCHAR2 type that can be 4,000 bytes long. This is n’ot much for XML so to deal with it., The AJAX_XMLHTP procedure was created, which takes a context as a parameter and using the DBMS_LOB PL/SQL package outputs XML in 4,000-char chunks, leveraging the possibilities
    • standing behind LOBs. See Listing 2. create or replace procedure ajax_xmlhtp(ctxt in number) as xml clob; chnk number := 4000; begin owa_util.mime_header('text/xml'); dbms_xmlgen.setnullhandling(ctxt, dbms_xmlgen.empty_tag); dbms_xmlgen.setprettyprinting(ctxt, false); xml := dbms_xmlgen.getxml(ctxt); for i in 1..ceil(length(xml)/chnk) loop htp.prn(dbms_lob.substr(xml, chnk+1, i+(i-1)*chnk)); end loop; dbms_xmlgen.closecontext(ctxt); exception when others then htp.print('<?xml version="1.0"?>'); htp.print('<ROWSET><ROW><INFO>No data found.</INFO></ROW></ROWSET>'); end; / This procedure will handle all the processing work for us by setting the content type to text/xml (required by the XmlHttpRequest object for DOM processing), setting null handling to output empty tags for null values, and disabling pretty printing for smaller response size. Also, when something goes wrong at the query level, e.g., empty result set or invalid query, the procedure manually outputs the hard-coded "No data found." message to indicate its state. The AJAXE procedure will be a core dispatcher of responses. It will accept two parameters: q -– the type of the action requested, w -– the extra parameter required for certain types of action (optional). See Listing 3. create or replace procedure ajaxe(q varchar2, w varchar2 default '') as ctxt number;
    • i number; begin if q='count' then ajax_xmlhtp(dbms_xmlgen.newcontext('select count(*) "EMPLOYEES" from employees')); elsif q='search' then i := length(w); ctxt := dbms_xmlgen.newcontext('select * from employees where upper(substr(last_name, 0, :LEN))=upper(:PREFIX) order by last_name'); dbms_xmlgen.setbindvalue(ctxt, 'LEN', i); dbms_xmlgen.setbindvalue(ctxt, 'PREFIX', w); ajax_xmlhtp(ctxt); elsif q='fetch' then ajax_xmlhtp(dbms_xmlgen.newcontext('select * from employees')); elsif q='select' then ajax_xmlhtp(dbms_xmlgen.newcontext('select department_id, department_name from departments order by 2')); elsif q='dept' then ctxt := dbms_xmlgen.newcontext('select * from employees where department_id=:DEPT'); dbms_xmlgen.setbindvalue(ctxt, 'DEPT', w); ajax_xmlhtp(ctxt); end if; end; / The fFunctionality of the sample application will be divided into three basic use cases: • Filling the SELECT list with elements obtained through the AJAX request and hooking the event handler to it • Obtaining the results set and rendering them it into a HTML table dynamically • Using simple auto-complete on the HTML input field Note that both these procedures need have to be created in the HR schema to work. At this point you can test whether the procedure is working for you by accessing http://localhost:8080/apex/hr.ajaxe? q=count (remember to adjust the URL to reflect your system’'s
    • configuration). A vValid XML document should be outputted at this address. You’ are half-way through. Hands- on Document Object Model (DOM) AJAX extensively relies on a set of JavaScript programming techniques once referred to as DHTML. But now they’ are much more powerful thanks to the unification of the DOM implementation among Web browser vendors. Back in the times days of Netscape Navigator 4 and Microsoft Internet Explorer 4 one had to use a different syntax for each browser for to manipulateing page content dynamically for each browser. No need to do that anymore. The Document Object Model is a tree-like hierarchical representation of nodes/HTML elements. The root node of an HTML document is the <html> tag which that most of the time usually has two children: <head> and <body> – - the manipulations happen within the latter onesecond. As Since DOM is quite a vast subject, I wiwe’ll focus only on the most useful properties and methods of JavaScript XML objects. For a the complete specification of DOM specification, please refer see to the W3C Web site at http://w3.org. Querying There are two core methods of querying an existing DOM tree DOM that can be used both on document object and nodes themselves: • document_or_node.getElementById(id) Query context for a node with an id attribute equal to the given ID. Returns a single node or null if not found. • document_or_node.getElementsByTagName(tag_name) Query context for all nodes with the given tag name. Always returns a list of nodes (HTMLCollection object) even if it means that it’ is empty (no nodes with the given tag name were found). Creating Element is the basic node type that represents a tag element on nodes a Web page. To create a new node of the Element type call the createElement method on the document object: • document.createElement(name) Note, however, that this element is initially empty. You
    • can append other elements or append a text to it with the document.createTextNode(text) method. Removin To remove a node from the DOM tree you need have to find g nodes it's parent and use the following removal function: • some_node.removeChild(old_child) This function removes the old_child node from the document in place and returns it. Note that this method can be slow when used to recursively remove trees recursively from the main DOM tree. It can drastically decrease page performance so you can use a the well-known trick of setting node’'s innerHTML attribute to an empty string. Manipula There are several methods for manipulating the DOM tree, ting some of them are: nodes • existing_node.appendChild(new_node) This method appends the new_node at the end of the existing_node’'s children list. To be able to insert a node before some node one needs to make use of the insertBefore method. • setAttribute(name, value) HTML requires many elements to have certain attributes set. For example, this method could be used to set the href attribute of <a> element. Again, remember that there are a number of other JavaScript methods for working with the DOM object. For the purposes of this demo article, an ajaxTable function was created: it builds up the HTML table from the XML response obtained through an the AJAX request. By default DBMS_XMLGEN returns a XML tree of the following structure: <?xml version="1.0"?> <ROWSET> <ROW> <COLUMN1></COLUMN1> <COLUMN2></COLUMN2>
    • ... </ROW> ... </ROWSET> The code below in Listing 4represents the body of the ajaxTable function.:
    • function ajaxTable(xml) { document.getElementById('q').innerHTML = ''; var rows = xml.documentElement.getElementsByTagName('ROW'); var table = document.createElement('table'); var tbody = document.createElement('tbody'); table.setAttribute('border', '1'); var tr = document.createElement('tr'); for (var h=0; h<rows[0].childNodes.length; h++) { if (rows[0].childNodes[h].nodeType!=1) { continue; } var th = document.createElement('th'); th.appendChild(document.createTextNode(rows[0].childNodes[h].tagNa me)); tr.appendChild(th); } tbody.appendChild(tr); for (var i=0; i<rows.length; i++) { var tr = document.createElement('tr'); for (var j=0; j<rows[i].childNodes.length; j++) { if (rows[i].childNodes[j].nodeType!=1) { continue; } var td = document.createElement('td'); if (rows[i].childNodes[j].childNodes.length!=0) { td.appendChild(document.createTextNode(rows[i].childNodes[j].firstChil d.nodeValue)); } else { td.appendChild(document.createTextNode('-')); } tr.appendChild(td); } tbody.appendChild(tr); }
    • table.appendChild(tbody); document.getElementById('q').appendChild(table); } This function appends the following DOM tree in Listing 5 to the DIV element of ID="q" (the TBODY element is required here to render properly under Internet Explorer).: <table border="1"> <tbody> <tr> <th>HEADING1</th> <th>HEADING2</th> <th>...</th> </tr> <tr> <td>ROW1, COLUMN1</td> <td>ROW1, COLUMN2</td> <td>ROW1, ...</td> </tr> ... </tbody> </table> AJAX = Asynchronous JavaScript and XML Technologically, AJAX is nothing new. It owens its recent popularity to the unification of the JavaScript standards that allow access to the XmlHttpRequest object. Neither is XML isn’t new either. , which It now plays a key role in data and information interchange and is universally adopted by both the enterprise and the open source community. The biggest problem with the Web was that applications needed a full- page refresh after each user action on the page. It didn’'t matter whether this it was just sorting table elements or downloading a whole new page. XmlHttpRequest object methods and properties.
    • abort() Aborts the current request getAllResponseHeaders() Returns all headers from the HTTP response as a string getResponseHeader(label) Returns the header value specified by the label open(method, URL) Overloaded open() methods assign open(method, URL, async) parameters to the request object open(method, URL, async, open(method, URL, async, The method is one of "GET" or "POST" username, password) (use the latter one when the data is sent with a request that exceeds 512 bytes) URL might be relative or absolute The async flag is the heart of AJAX, setting it to true or false you control whether the script is waiting for a response in the background or halts execution until the response is received You can also supply username and password for direct HTTP authorization - note that this is not safe, as the script will always remain visible to the user send(content) Sends the request together with the specified content (can be null) setRequestHeader(label, Sets a label/value pair to be included in the value) requests header onreadystatechange The eEvent handler watching the requests state readyState Status of the request given by the integer:
    • 0 uninitial Basic state ized 1 open open() method has been successfully called 2 sent The request has been sent 3 receivin After receiving any of the g response headers and just before receiving the body 4 loaded All data transferred responseText Plain text content of the response responseXML XML content of the response status HTTP status of the URL called as a number (eg. 200, 403, 404) statusText HTTP status of the URL called as a string (e.g., OK, Authorization Required, Not Found) There are a number of AJAX frameworks on the market, including the Dojo Toolkit, Prototype, or Microsoft Atlas to name just a few. However, writing basic handlers alone helps enable a deeper understanding of what’ is happening behind under the AJAX’s hood. First, we need an XmlHttpRequest object that unfortunately still depends on the browser -– Microsoft Internet Explorer continues handling it using an ActiveX control so the appropriate requests object creation code is presented belowin Listing 6. This is the only part that requires browser- compatibility code. , Everything else here is cross-browser. var request = false; try { request = new XMLHttpRequest(); } catch(e) { try { request = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e2) { request = new ActiveXObject("MSXML.XMLHTTP");
    • } } As for the handler, a dedicated function to handle all the asynchronous operations was created. It takes a request method, a URL, and callback functions as parameters. After the response is obtained the callback is immediately called with the fetched XML passed as an argument. function async(method, url, callback) { request.open(method, url, true); request.onreadystatechange = function() { if (request.readyState == 4 && request.status == 200) { callback(request.responseXML); } } request.send(null); } The callback function will do all the DOM processing based on the XML content from the response. The callback works directly on the XMLDocument JavaScript object. Putting It All Together Now that we have all the building blocks of the solution, let’s take a look at the logic behind it. The diagram below separates client -side and server -side into 3 three separate blocks: Oracle XE, Web page, and browser client. Please Notice that the Web page has been separated here because of its state-dependent nature -– thanks to AJAX it now incorporates the logic to handle user actions and transform them into HTTP requests, the standard Web model assumes that it isit’s the browser that dispatches HTTP requests to the Web server. That is the key concept of in the solution. Take a look at the diagram below to see the whole interaction process.
    • 1. The browser client sends a direct HTTP request to the Web server (here: the Embedded PL/SQL Gateway of Oracle XE), the URL of the request has been supplied in the location bar or through a link. 2. Oracle XE serves a static HTML page which that includes AJAX scripts. This static page is then rendered by the browser and now all the requests and interaction happen indirectly, as AJAX requests. 3. When the user triggers an event on the Web page by interacting with page elements such as forms and links, an event handler catches it and sends the appropriate HTTP request through the AJAX engine. 4. The XMLHttpRequest object requests a remote resource from the Embedded PL/SQL gateway. At this point, the user does n’ot have to wait for the script to finish fetching responses from the server, it’s handled in an asynchronous manner. The script takes as much time as needed to fetch the response while the user can further interact with the Web page. 5. The script performs DOM modifications in the background while the user can trigger the next events on the Web page. You can now upload the attached HTML file (hr-ajax-demo.html)
    • into your XE instance through FTP or WebDAV, which contains the complete code for the sample application. After putting it into the /public/ folder of XE, open http://localhost:8080/public/hr-ajax-demo.html with your Web browser. After carefully following all the steps of this tutorial the demo page should pre-load the list of departments and respond to user interaction almost immediately. Advantages and Potential Problems Although programming AJAX is n’ot necessarily difficult, the question is whether asynchronous querying techniques are safe and production- ready. AJAX is n’ot always a sure choice over standard development methodologies. There are at least several viewpoints to consider:. Advantages: • Better user experience • Greater responsiveness • Smaller server load, increased performance • Network applications can be updated on-the-fly without redistributing updates • Separation of content from presentation (easier maintenance) Potential problems: • Cross-browser incompatibilities • Client-side security concerns The Real World Today, AJAX applications are gaining a lot of steam. With Google leading this trend together with AND Yahoo! and Microsoft Followingtrailing, the desktop experience is delivered to users through increasingly dynamic Web pages. Since the early adoption of AJAX it has matured to be a full-blown solution often chosen by enterprises for commercial solutions. After the unification of DOM implementations and the adoption of World Wide Web Consortium (W3C) standards among developers of Web browsers, it’ is now possible to create cross-platform Web applications more easily than ever before. With a wide range of available AJAX
    • toolkits, developers can now upgrade Web applications to an asynchronous architecture at a low cost and with limited investment. AJAX is flexible enough to be plugged into existing solutions and greatly enhances usability. The movement of desktop applications toward the Web seems likeis a matter of time and represents the next step in software evolution. Przemek Piotrowski has been following Oracle 10g Express Edition (Oracle XE) development since its early beta stages back in 2005 and can easily distinguish new features of this first database from Oracle, free to develop, deploy and distribute. Oracle XE's small footprint doesn't stop him from taking advantage of many of its advanced features and plugging them into existing Web architectures. Mark Townsend is the Vice President of Database Product Management in Oracle's Server Technology Division. His responsibilities include requirement analysis, release planning, co- ordination of database product management activities, communication with analysts and press on database topics, and development and delivery of field technical training. He was also the product boss for Oracle XE. Mark has been with Oracle since 1991 and has specialized in the Oracle database for over 15 years.Listing 1 Listing 1 create or replace function wwv_flow_epg_include_mod_local(procedure_name in varchar2) return boolean is begin -- return false; -- remove this statement when you modify this function -- -- Administrator note: the procedure_name input parameter may be in the format: -- -- procedure -- schema.procedure -- package.procedure -- schema.package.procedure --
    • -- If the expected input parameter is a procedure name only, the IN list code shown below -- can be modified to itemize the expected procedure names. Otherwise you must parse the -- procedure_name parameter and replace the simple code below with code that will evaluate -- all of the cases listed above. -- if upper(procedure_name) in ( 'HR.AJAXE') then return TRUE; else return FALSE; end if; end wwv_flow_epg_include_mod_local; Listing 2 create or replace procedure ajax_xmlhtp(ctxt in number) as xml clob; chnk number := 4000; begin owa_util.mime_header('text/xml'); dbms_xmlgen.setnullhandling(ctxt, dbms_xmlgen.empty_tag); dbms_xmlgen.setprettyprinting(ctxt, false); xml := dbms_xmlgen.getxml(ctxt); for i in 1..ceil(length(xml)/chnk) loop htp.prn(dbms_lob.substr(xml, chnk+1, i+(i-1)*chnk)); end loop; dbms_xmlgen.closecontext(ctxt); exception when others then htp.print('<?xml version="1.0"?>'); htp.print('<ROWSET><ROW><INFO>No data found.</INFO></ROW></ROWSET>'); end; / Listing 3
    • create or replace procedure ajaxe(q varchar2, w varchar2 default '') as ctxt number; i number; begin if q='count' then ajax_xmlhtp(dbms_xmlgen.newcontext('select count(*) "EMPLOYEES" from employees')); elsif q='search' then i := length(w); ctxt := dbms_xmlgen.newcontext('select * from employees where upper(substr(last_name, 0, :LEN))=upper(:PREFIX) order by last_name'); dbms_xmlgen.setbindvalue(ctxt, 'LEN', i); dbms_xmlgen.setbindvalue(ctxt, 'PREFIX', w); ajax_xmlhtp(ctxt); elsif q='fetch' then ajax_xmlhtp(dbms_xmlgen.newcontext('select * from employees')); elsif q='select' then ajax_xmlhtp(dbms_xmlgen.newcontext('select department_id, department_name from departments order by 2')); elsif q='dept' then ctxt := dbms_xmlgen.newcontext('select * from employees where department_id=:DEPT'); dbms_xmlgen.setbindvalue(ctxt, 'DEPT', w); ajax_xmlhtp(ctxt); end if; end; / Listing 4 function ajaxTable(xml) { document.getElementById('q').innerHTML = ''; var rows = xml.documentElement.getElementsByTagName('ROW'); var table = document.createElement('table'); var tbody = document.createElement('tbody'); table.setAttribute('border', '1'); var tr = document.createElement('tr');
    • for (var h=0; h<rows[0].childNodes.length; h++) { if (rows[0].childNodes[h].nodeType!=1) { continue; } var th = document.createElement('th'); th.appendChild(document.createTextNode(rows[0].childNodes[h].tagNa me)); tr.appendChild(th); } tbody.appendChild(tr); for (var i=0; i<rows.length; i++) { var tr = document.createElement('tr'); for (var j=0; j<rows[i].childNodes.length; j++) { if (rows[i].childNodes[j].nodeType!=1) { continue; } var td = document.createElement('td'); if (rows[i].childNodes[j].childNodes.length!=0) { td.appendChild(document.createTextNode(rows[i].childNodes[j].firstChil d.nodeValue)); } else { td.appendChild(document.createTextNode('-')); } tr.appendChild(td); } tbody.appendChild(tr); } table.appendChild(tbody); document.getElementById('q').appendChild(table); } Listing 5 <table border="1">
    • <tbody> <tr> <th>HEADING1</th> <th>HEADING2</th> <th>...</th> </tr> <tr> <td>ROW1, COLUMN1</td> <td>ROW1, COLUMN2</td> <td>ROW1, ...</td> </tr> ... </tbody> </table> Listing 6 var request = false; try { request = new XMLHttpRequest(); } catch(e) { try { request = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e2) { request = new ActiveXObject("MSXML.XMLHTTP"); } }