Professional Documents
Culture Documents
FTPOnline has put together a comprehensive white paper with more than 30 informative pages on Asynchronous JavaScript and XML, or Ajax. Learn how to use this new technology to build more responsive Web applications. Page # The Ajax Approach to Richer Interfaces .. 2 The Year of Ajax 19 Gates Gives Glimpse of the "Next Web"..21 Build Smart Web Apps With Atlas 23 Innovation Information Delivery 30
can generate XML (or any markup), the core AJAX technology is widely available. The architecture for a pure AJAX application is very straightforward (see Figure 1). Core AJAX Figure 1 The core AJAX architecture for a pure AJAX application is straightforward.
As you can see in Figure 1, an AJAX application in its simplest form is essentially a standard HTML user interface with JavaScript functions to interact with any type of HTTP server technology that can handle the request and respond dynamically in XML. The key elements of a core AJAX application are an HTML page and a server-side Web technology that can process HTTP requests (such as Java servlets, PHP, and so on) and respond in XML markup. The HTML page contains user interface (UI) elements that interact with AJAX JavaScript functions and JavaScript functions that interact with an AJAX server. Reviewing the key elements in a more realistic scenario involves an HTML UI with elements such as an input field, a button, or anything that can be linked to JavaScript. For example, a button could fire a JavaScript function when pressed, or for even more subtle usage an input field could fire a JavaScript function as the user types into the field. This functionality is accomplished by setting the onkeyup attribute of the input field to a JavaScript function that processes the data in the input field. For example, the input field searchField will call the JavaScript function lookup() when an onkeyup event occurs (that is, during typing): <input type="text" id= "searchField" size= "20" onkeyup="lookup( 'searchField');"> In addition to responding to UI interactions (like typing), AJAX JavaScript functions can operate independently on their own timers. (An AJAX autosave feature can be implemented using this approach.) Now that weve reviewed how the AJAX JavaScript code can be invoked, lets review the JavaScript code that can issue an XML HTTP request. Recalling that both major browser families
support remote JavaScript in a similar fashion, some code can be added to a page to first check for its support and then instantiate the appropriate object: if (window.XMLHttpRequest) { req = new XMLHttpRequest(); } else if (window.ActiveXObject) { req = new ActiveXObject( "Microsoft.XMLHTTP"); } Once instantiated, it can be operated on in exactly the same manner. To initialize a connection to a server, use the open method: req.open("GET", url, true); The first argument is the HTTP method (GET or POST). The second argument is the servers URL (or form action if using a POST). When true, the third argument denotes whether the call should be made asynchronously (hence, the A in AJAX). An asynchronous call means that the browser can continue doing other things while the request is being fulfilled. A false value in the open method denotes a non-asynchronous or serial processing, which is not recommended because your browser will cease operations until the response has been returned. Making the Connection After using open to initialize a connection, an onreadystatechange call is made (only for asynchronous calls). This call registers a callback function, which will be invoked once the request is complete: req.onreadystatechange = processXMLResponse; This callback function example, processXMLResponse(), processes the returned XML response and is invoked when the request is fulfilled. A callback function can also be declared inline in the onreadystatechange statement: req.onreadystatechange = processXMLResponse() { // process request }; Oftentimes its necessary to set a header value in the XML HTTP request object by using setRequestHeader(). For example: req.setRequestHeader( Cookie, someKey=true); Once the XML HTTP request object (req) has been fully initialized, you can use send() to initiate a call to the server: req.send(null);
For GET requests, a null value or empty string "" is used. POST requests contain a string argument with form data. They also require the Content-Type to be set in the header of the request. These two lines show how to perform an AJAX POST request: req.setRequestHeader( "Content-Type", "application/ x-www-form-urlencoded"; req.send( name=scott&email= stiger@foocorp.com); The callback function, which is called once the request has been fulfilled, usually has some code to make sure the request has not resulted in an error. You can accomplish this reassurance by checking the readyState as well as the overall status of the HTTP request. (A readystate of 4 means the XML HTTP request is complete, and 200 means it was a successas opposed to 404.) function processXMLResponse() { if (xmlreq.readyState == 4) { if (xmlreq.status == 200) { // Process the XML response... } } } You can use standard JavaScript DOM methods to process the XML response. For example, to extract the employee name from the incoming XML stream shown here: <employee> Chris </employee> use this method: var name = req.responseXML. getElementsByTagName( "employee")[0]; Parsing more complex XML usually involves iterating through the elements using code such as this: for (i=0;i<elements.length;i++) { for (j=0;j<elements[i]. childNodes.length;j++) { var ElementData = elements[i].childNodes[j]. firstChild.nodeValue; }
}
Be aware that the XML response obtained through the XML HTTP request object doesnt always need to be well formed and valid. The AJAX server-side component can send HTML content directly to the client. JavaScript can then retrieve the HTML content by using the req.responseText() method/property, which simply retrieves the content as a string. The HTML string text can then be used in whatever fashion to alter the page. For example, this HTML stream: <h3>Hello there!</h3> <p> This is <b>HTML</b></p> could be retrieved into a string using: var HTMLcontent = req.responseText; and then added to a specific HTML DIV tag: document.getElementById("div1"). innerHTML += HTMLcontent; Having stepped through the basics of an AJAX transaction, lets consider a pure AJAX application example that we can later compare to a revised version of the same example implemented with JSF technology. Automatic Completion To get a better feel for how AJAX can be used in a more realistic scenario, consider a DirectorySearch application. This AJAX example presents the user with a single input text field. When the user begins typing characters into the field, a list of corresponding matches from a fictitious corporations employee directory appears (see Figure 2). As the user continues typing, the list decreases in size until a best match is found. The user doesnt even have to click on a Submit button as the page updates itself with the most correct matches based on the input so far. This behavior all occurs without requiring a traditional page submission and a complete refresh cycle (see Figure 3). Note that with just a little more JavaScript, the DirectorySearch example could be transformed into the popular autocompletion feature.
Smart Field Figure 2 The AJAX DirectorySearch application presents a single input text field that displays a list of corresponding matches from a directory when a user begins typing characters into the field.
No Refresh Required Figure 3 When a desired match from the input displays, the page updates itself with the most correct matches without requiring a traditional page submission and a complete refresh cycle.
The DirectorySearch AJAX example consists of an HTML page, directory.html, and a Java servlet that responds to the AJAX request in XML. The HTML page contains an input text field and a set of JavaScript functions residing in the same HTML page that react to the characters entered, invoke an AJAX request, and update the UI with the response. The first thing to consider
in this example is how the AJAX request gets started. It occurs because the input text field in the HTML page has an onkeyup attribute set with a reference to a JavaScript lookup() function: <input type="text" id= "searchField" size="20" onkeyup="lookup( 'searchField');"> Notice the searchField ID is passed to the lookup() function. It uses this function to determine the current value of the field. The JavaScript lookup() function along with the other functions is embedded inside of a pair of <script> tags in the header of the HTML page. By examining the lookup() function more closely, we see that its purpose is to initiate the AJAX call with a URL of ajaxdirectoryservice, which is the (mapped) name of the AJAX servlet. It uses the input parameter to pass the value of the searchField input field to the AJAX servlet (see Listing 1). Initiate the AJAX Call Listing 1 The purpose of the JavaScript lookup() function is to initiate the AJAX call with a URL that is the (mapped) name of the AJAX servlet. <script type="text/javascript"> var xmlreq; var writeloc; function lookup(field) { writeloc = field + ":table"; var searchField = document.getElementById(field); var url = "ajaxdirectoryservice?input=" + searchField.value; if (window.XMLHttpRequest) { xmlreq = new XMLHttpRequest();} else if (window.ActiveXObject) { xmlreq = new ActiveXObject("Microsoft.XMLHTTP");} xmlreq.open("GET", url, true); xmlreq.onreadystatechange = processXMLResponse; xmlreq.send(null);} Once the request is initiated in asynchronous mode, a processXMLResponse() function will be invoked once the request is complete. The processXMLResponse() function merely checks to see that the request completed successfully from the servlet and sends it to a rendering function: function processXMLResponse() { if (xmlreq.readyState == 4) { if (xmlreq.status == 200) { renderTable(); } } }
The data retrieved from the servlet in this example is pure XML and is based on the input text that was supplied to it. For example, if a string"sc"is sent to the servlet, it will respond in XML with: <?xml version = '1.0'?> <directory> <employee> <NAME>Chris Schultz</NAME> <TITLE>JSF Developer</TITLE> <EMAIL>cschultz@foocorp.com </EMAIL> <PHONE>1-408-555-1234</PHONE> </employee> <employee> <NAME>Scott Borland</NAME> <TITLE>Electrical Engineer </TITLE> <EMAIL>sborland@foocorp.com </EMAIL> <PHONE>1-408-555-2468</PHONE> </employee> ... </directory> The JavaScript renderTable() function, which is called by the callback function, has the task of iterating through the XML data, constructing an HTML table from it, and appending it to the original HTML page (see Listing 2). Render a Table Listing 2 The JavaScript renderTable() function iterates through the XML data, constructs an HTML table from it, and appends it to the original HTML page. function renderTable() {xmlDoc = xmlreq.responseXML; var elements = xmlDoc.getElementsByTagName( 'employee'); var table = document.createElement('table'); table.setAttribute('cellPadding',3); table.setAttribute('border',0); var tbody = document.createElement('tbody'); table.appendChild(tbody); var h_row = document.createElement('tr'); for (i=0;i<elements[0].childNodes.length;i++) { if (elements[0].childNodes[i].nodeType != 1) continue; var t_header = document.createElement('th'); var headerData = document.createTextNode( elements[0].childNodes[i].nodeName); t_header.appendChild(headerData); h_row.appendChild(t_header);}
tbody.appendChild(h_row); for (i=0;i<elements.length;i++) { var t_row = document.createElement('tr'); for (j=0;j<elements[i].childNodes.length;j++) { if (elements[i].childNodes[j].nodeType != 1) continue; var td = document.createElement('td'); var tdData = document.createTextNode( elements[i].childNodes[j]. firstChild.nodeValue); td.appendChild(tdData); t_row.appendChild(td); } tbody.appendChild(t_row);} // Clear previous table var element = document.getElementById(writeloc); while(element.hasChildNodes()) element.removeChild(element.firstChild); // Append new table document.getElementById(writeloc).appendChild( table);} The code may be a bit cryptic, but essentially it just loops through the XML data, constructs a new HTML table with the data in it, and appends it to a specified write location (writeloc), which is an HTML DIV defined just below the input text field. Notice that it clears out the previous HTML results table just before it appends the new one. The DIV that serves as the write location writeloc is defined as: <div id= "searchField:table"></div> The servlet responds to the request and processes the incoming input parameter (see Listing 3). Input Process Listing 3 The AjaxDirectoryService servlet responds to the XML HTTP request and processes the incoming input parameter. package com.ajaxjsf.servlet; public class AjaxDirectoryService extends HttpServlet { public void init(ServletConfig config) throws ServletException { super.init(config);} public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException { response.setContentType("text/xml"); response.setHeader("Cache-Control", "no-cache"); XmlGenerator myXmlGenerator = new XmlGenerator( request.getParameter("input")); String xmlout = myXmlGenerator.getXmlResponse(); PrintWriter out = response.getWriter(); out.write(xmlout); out.close();}} Aside from being a typical HTTPServlet, the key things to notice are that the ContentType is set to text/xml and a header parameter, Cache-Control, is set to no-cache. This setting prevents any of the XML data from being cached by the browser. Notice also the XMLGenerator class, which is a custom class that does the XML generation. It can use any technology, but for the example here its coded to use an Oracle database along with the Oracle XML SQL feature to return a pure XML string from a SQL query constructed from the input value (see Listing 4). SQL Query Return Listing 4 The XMLGenerator class called by the AjaxDirectoryService servlet can use any technology, but in this case is coded to use an Oracle database and the Oracle XML SQL feature to return a pure XML string from a SQL query. package com.ajaxjsf.xml; // JDBC Based XmlGenerator import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import oracle.xml.sql.query.OracleXMLQuery; public class XmlGenerator { String xmlResponse; public XmlGenerator(String input) { // Construct query based on input string and return // and XML response. // The code for this example is Oracle specific // and is omitted for brevity.} An AJAX-Enabled JSF Component Whats wrong with the pure AJAX version of DirectorySearch? Having reviewed the architecture of the pure AJAX DirectorySearch application example, we see that the page author bares a very large responsibility for making the AJAX transaction work. As you recall, the AJAX plumbing is constructed manually using JavaScript in the HTML client and can easily become very complex.
An ideal solution would be to offer a DirectorySearch JSF component where the page author can simply drop it onto a page and it just works. A JSF version of the DirectorySearch AJAX example is very easy to use. The page author simply adds the directorysearch component to a JSF JSP page: <%@ page contentType="text/html"%> <%@ taglib uri= "http://java.sun.com/jsf/html" prefix="h"%> <%@ taglib uri= "http://java.sun.com/jsf/core" prefix="f"%> <%@ taglib uri= "http://javapro.com/ajaxjsf/ demo/components" prefix= "ajaxjsf" %> <f:view> <html> <body> <h:form> <h2> AJAX JSF Directory Search </h2> <ajaxjsf:directorysearch border="0" /> </h:form> </body> </html> </f:view> At runtime, directorysearch performs its job of providing a directory lookup just like the pure AJAX version (see Figure 4). Since the directorysearch JSF component provides attributes, the user can easily alter certain aspects of the directorysearch without having to edit any JavaScript code. Heres an example of the same component but with a different background. This tag generates the result shown in Figure 5: <ajaxjsf:directorysearch border= "0" tablebgcolor="#99EEFF" />
Dj Vu
Figure 4 An AJAX JSF DirectorySearch component provides a directory lookup just like in the pure AJAX version.
Feeling Blue Figure 5 The DirectorySearch component with a blue background tag provides this result.
The architecture of the JSF-enabled DirectorySearch is similar to the pure AJAX example shown previously, except that the JSF component performs the task of rendering the JavaScript directly into the HTML (JSP) page. The JavaScript code then initiates an XML HTTP request to either the same AJAX servlet that was used before, or the AJAX XML response code that was
used in the servlet could instead be inserted into the JSF application in two different waysas a JSF PhaseListener or into the decode() method of the JSF component itself (see Figure 6). Pure and Easy Figure 6 The existing AJAX servlet is the easiest architecture if it doesnt need to interact often with the JSF application data. Place the code inside either a JSF PhaseListener or the JSF component itself to have a pure JSF AJAX solution without using a separate servlet.
Lets take a look at the key elements of the JSF AJAX version of DirectorySearch. As you can see in Figure 6, the JSF architecture has a JSF component that renders the necessary JavaScript on the client to communicate to the AJAX-enabled service: <ajaxjsf:directorysearch> The AJAX service, with which the JavaScript communicates, can be implemented in two ways: either it can remain as an independent AJAX servlet, as was used in the pure AJAX example discussed previously, or it can be embedded into the JSF application such as with a PhaseListener or in the components decode method. The DirectorySearch Component If the JSF application retains the same AJAX servlet from before, it then has the JSP page, Directory.jsp, and the AJAX servlet, which responds to the XML HTTP requests. The JSP page contains a DirectorySearch tag referencing the underlying UI component and the DirectorySearch UI component that renders both an input field along with the necessary JavaScript to communicate with the AJAX server. This approach is adapted most easily from the pure AJAX architecture. The only real change is that a custom DirectorySearch JSF component is built, and it takes on the responsibility of rendering the necessary JavaScript at runtime. Building the DirectorySearch JSF component. For those familiar with JSF custom component development, building a DirectorySearch custom JSF component that renders an input field along with the necessary JavaScript is fairly easy. However, those unfamiliar with custom JSF
component development will need some background information on this topic (See Resources online at www.javapro.com for a link to the article Building Custom JavaServer Faces UI Components). In general, custom JSF component development involves creating a UIComponent class that provides the behavior of the component and an optional Renderer class that has code to present or render itself to the client. The Renderer class is optional because the UI component can also contain logic to render itself. For usage in JSP, a custom JSP tag handler (UIComponentTag ) must also be created. In the interest of brevity, lets consider only the UIComponent source code, which includes the code that renders the AJAX JavaScript functions. The DirectorySearch UIComponent class has the core structure shown in Listing 5. The JSF UIComponent Listing 5 The DirectorySearch UIComponent extends the standard UIInput component. package com.ajaxjsf.component; // Import statements omitted for brevity public class DirectorySearch extends UIInput { private static final String SCRIPT_RENDERED_FLAG = "directory-js-script-rendered"; public DirectorySearch() { setRendererType(null);//This component // renders itself } public void encodeBegin(FacesContext context) throws IOException { String clientId = getClientId(context); encodeAjaxJavascript(context); encodeInputField(context, clientId); } ... Notice that the class extends the standard UIInput component, which is just an input text field, and it also has a setRendererType() method that has a null argument. The null argument just means that a separate renderer is not used with this UI component because it renders itself. Also notice the encodeBegin() method, which is overridden where custom rendering code is placed. The two main tasks of rendering JavaScript and the input field are placed in separate methods. The body of the encodeAjaxJavascript() method is shown in Listing 6. Encoding JavaScript Listing 6 The encodeAjaxJavascript() method includes three JavaScript rendering methods. public void encodeAjaxJavascript( FacesContext context) throws IOException { String border = (String)getAttributes().get(
"border"); String tablebgcolor = (String)getAttributes().get( "tablebgcolor"); // Render Ajax enabled Javascript only once if (!jsRenderedFlag(context)) { // if not rendered yet, go ahead and do it ResponseWriter writer = context.getResponseWriter(); writer.startElement("script", this); writer.writeAttribute("type", "text/javascript", null); render_lookup_function(writer); render_processXML_function(writer); render_renderTable_function(writer, border, tablebgcolor); writer.endElement("script"); } } Notice in the code the three JavaScript rendering methods: render_lookup_function(), render_processXML_function(), and render_renderTable_function(). Each of these methods renders their respective JavaScript functions shown previously using a ResponseWriter object (writer) that is retrieved from the FacesContext. Conditionally rendering JavaScript. Before executing the three JavaScript rendering methods, notice that a check is performed first using jsRenderedFlag() to determine whether the AJAX JavaScript functions have already been rendered onto the page. This check is necessary because if this component were to be used more than once on the same page, it would have to make sure it rendered the JavaScript functions only once since other DirectorySearch components on the page could just use the same AJAX JavaScript code. The jsRenderedFlag() method works by placing a marker flag variable onto the Request, and if other instances of this component find this flag they will refrain from rendering an unneeded copy of the JavaScript code. The jsRenderFlag() method returns a boolean value: public boolean jsRenderedFlag( FacesContext context){ Map requestMap = context. getExternalContext(). getRequestMap(); if (requestMap.get( SCRIPT_RENDERED_FLAG) == null){ // this is the first time // so render it requestMap.put( SCRIPT_RENDERED_FLAG, true); return false; }
else return true; } Note that SCRIPT_RENDERED_FLAG is defined earlier in the class as: private static final String SCRIPT_RENDERED_FLAG = "directory-js-script-rendered"; Assuming the component hasnt already rendered the AJAX JavaScript functions, the three JavaScript rendering methods will render the functions onto the page using the provided ResponseWriter (writer) objectfor example: public void render_lookup_function( ResponseWriter writer) throws IOException { String ajaxurl = ""; writer.write( "function lookup(field) {\n" + "writeloc = field + \":table\";\n" + "var searchField = document.getElementById( field);\n" + "\n" + "if (document.all)\n" + "xmlreq = new ActiveXObject( \"Microsoft.XMLHTTP\");\n" + "else\n" + "xmlreq = new XMLHttpRequest();\n" + "\n" + ... The source for the remaining JavaScript rendering methods, render_processXML_function() and render_renderTable_function(), are omitted for brevity but are coded in a similar fashion to render_lookup_function(). For a minimal approach to rendering JavaScript, note that an alternative to rendering the JavaScript code onto the page can be achieved by just rendering the JavaScript inclusion string onto the page: <script language="JavaScript" src= "js/AjaxScript.js"></script> When using this approach, make sure that the referred JavaScript file is available to the Web page (such as in a js/ subdirectory). In addition to just placing the JS file in a directory below the HTML root, there are other more sophisticated techniques beyond the scope of this article for
dynamically delivering the JavaScript file without the need for copying a JS file under your HTML root directory. From a conceptual standpoint, thats all you need to understand this version of the JSFenabled DirectorySearch component because it simply renders the input field and the associated AJAX JavaScript, and the page author doesnt have to bother with it. Although this approach is the easiest way to implement AJAX with JSF, it still uses a separate AJAX server object. This approach may be adequate for many applications; however, it may be advantageous to integrate the AJAX server object into the JSF application. Embedding AJAX Server Logic There are some possible benefits to integrating the AJAX server logic into the JSF application. These include having direct access to the JSF application data and UI component model, as well as not requiring an additional servlet configuration. When integrated into the JSF application, the AJAX server logic must listen to the incoming requests, determine if its an AJAX request, and then respond appropriately. Two possible locations to embed this logic are a PhaseListener, which is independent of the component itself and is usable by the entire application, and the components decode() method, which is the standard location where postback requests are processed. It resides either in the UIComponent or the associated Renderer class. A JSF application with integrated AJAX server logic has a JSP page, Directory.jsp, or a JSF PhaseListener that is used to process AJAX requests. The JSP page contains a DirectorySearch tag referencing the underlying UI component and the DirectorySearch UI component, which in addition to rendering the component, its decode() method processes the AJAX request. The specific details on how to implement the AJAX server logic inside of the JSF application are beyond the scope of this discussion, but the general approach remains the same where an incoming request is processed and a response is generated. Details on how to do this implementation can be found in the AJAX-focused chapter (11) in JavaServer Faces: The Complete Reference (see Resources online at www.javapro.com); my coauthor of this book, Ed Burns, is also the co-specification lead of the JavaServer Faces specification (JSR 127). AJAXs hype is valid as it provides the end user with a truly superior client experience; however, implementing AJAX, especially when doing so in a purely manual way, can involve some fairly advanced JavaScript development. Fortunately, the JSF-component model allows for the encapsulation of complex AJAX code into the components themselves, thus greatly simplifying the page authors task of building an AJAX-enabled application. JSF page authors dont need to know how the AJAX JSF components work; they just work! About the Author Chris Schalk is a principal product manager and Java evangelist for application server and development tools at Oracle Corporation. Chris maintains a blog on Java EE Web development, and he has also coauthored JavaServer Faces: The Complete Reference (McGraw-Hill Osborne Media, 2006) with Ed Burns. Contact Chris through his blog at: http://jroller.com/page/cschalk.
need a Java Web application framework (theoretically, the browser can talk directly to Web services!). Others have tried building a lot of custom AJAX code only to quickly realize why they haven't written any JavaScript in a long time. The Web application framework market has been quick to respond; you can now use AJAX with JavaServer Faces (JSF), Struts, WebWork, Tapestry, and so on. These frameworks handle things in different ways, but the key point is that it's possible to leverage what you already know. There's no need to reinvent the wheel. And unless you're anxious to manage all of the complexities of the browser DOM, JavaScript, and CSS, you probably shouldn't. The bottom line is that AJAX is here to stay, and it's great to see that the Web has quickly realized a powerful, (sometimes) backwards-compatible evolutionary path. Fortunately, the Java ecosystem is dynamic enough to rapidly support new technologies, and you can use your existing Web framework with different AJAX frameworks. Component-based Web frameworks such as JSF give you the best of both worlds: components that have both server-side and client-side representations, which means that developers are shielded from the complexities of AJAX and use the same event-oriented programming model whether the components are displaying AJAX code; WML; XUL; or plain vanilla HTML. AJAX is a powerful way to use a set of ubiquitous technologies, and it's here to stay (although, I'm not convinced the buzzword will be immortal). For Java developers, architects, and managers, the key is to make the best decision for the project at hand. More often than not doing so means using frameworks, either on the client, the server, or, better yet, both. The most important rule, however, is to remember that any technology should be used only where appropriate: every single Web application doesn't need to be "ajaxified," and those that can benefit from AJAX may be fine with just a few "ajaxian" features. About the Author Kito D. Mann is editor-in-chief of JSF Central (http://www.jsfcentral.com/), principal consultant at Virtua Inc., and author of JavaServer Faces in Action (Manning, 2005).
Gates also highlighted technologies complementary to the browser. He focused on hardware beyond the PCfrom mobile devices to large-screen TVs that run Windows XP Media Center Edition. He discussed how a new generation of applications "assumes the internet": the "live" generation of applications. "It's about a platform," he explained. "[Microsoft's efforts are] about making things simpler, and simpler, for you. You can focus on making a great UI instead of lots of complex code."" Microsoft, he said, is focusing on building tools that make it easy for you to build rich, nextgeneration applications that consume services provided by the Internet, RSS-based or otherwise. The new Expression tools, he said, show, "a commitment to the audience who wants to [create this] kind of rich project." He then featured two partners that built businesses on Microsoft technologies, MySpace.com and the British Broadcasing Corporation (BBC). Both demonstrated their current abilities using today's technologiesand then demonstrated new capabilities based on tomorrow's technologies, including IE7 and Windows Vista. A question-and-answer session with Publisher Tim O'Reilly ended the keynote. One exciting part of this conclusion was Gates' response to O'Reilly's question about former Microsoft competitors. He quipped, "'There [are] a lot of these so-called fights where the other guy knocked himself out." Gates' keynote, while more subdued than many, still offered some tantalizing glimpses into Microsoft's new Web technologies. Gates called on you to begin developing the next-generation Web applications, using the new technologies released at MIX06 Web Conference. About the Author Jeff Hadfield is vice president of FTP's Windows Publishing Group.
Figure 1 Create an Atlas Project Once you run the Atlas VSI, you get a template for creating Atlas projects the next time that you open Visual Studio 2005. You can find this template by selecting New Website from the File menu.
Manage Atlas Activities The key item on the sample page is the ScriptManager control whose methods and properties allow you to manage the Atlas activities on the page. The ScriptManager control writes out all the HTML tags that add the JavaScript client-side code libraries to the page. The ScriptManager object doesnt yet appear in the Visual Studio toolbox. This means creating a new Atlas-enabled page requires adding the control in Source view using this tag, rather than dragging a control from the toolbox: <atlas:ScriptManager ID="ScriptManager1" runat="server" /> The sample code in this article accesses a Web Service defined in an ASMX file called GetCustomerData in the Atlas project. The Web Service includes a WebMethod called GetCustomerName. This method requires only that you pass it a single string (the Customer Id); it returns a single string in response (the customer name). Accessing a Web Service from Atlas requires several steps. Begin by adding a reference to the Web Service to the Atlas ScriptManagers Services collection. The required tags for the GetCustomerData.ASMX file look like this in Source view: <atlas:ScriptManager ID="ScriptManager1" runat="server" > <Services> <atlas:ServiceReference Path="GetCustomerData.asmx" /> </Services>
</atlas:ScriptManager> You dont have to type this text yourself; rather, use Visual Studios ServiceReference Collection Editor to generate the tags (see Figure 2). Figure 2 Create a Service Reference You bring up the ServiceReference Collection Editor by clicking on the builder button for the Services collection in the Properties Window of the ScriptManager control. Next, click on the Add button to add a ServiceReference to the Members list where you can enter the name of any ASMX file in your project to the Path property.
Next, call the service from your client-side code and pass the data required by the WebMethod. You must call the service from some client-side event that occurs after the documents onload event. Also, the call to the Web Service is made asynchronously, so should avoid calling the Web Service from any routine that submits the page. By the time that the Web Service returns the result, the page will probably be on its way back to the server. You pass any parameters required by the WebMethod, as well as one required and one optional parameter when you call the WebMethod from client-side code. The required parameter is the name of a client-side function in the page that ASP.NET calls when the Web Service returns the result. The optional parameter is the name of another client-side function that ASP.NET calls if the Web Service times out. In this sample code, you set the onblur event of a text box to call a JavaScript function named GetName. GetName, in turn, calls the GetCustomerName WebMethod of the GetCustomerData Web Service, passing it two parameters. The first parameter is the one required by the WebMethod (in this example, the parameter is set to ALFKI) and the second parameter is the name of the routine to call when the WebMethod returns (WhenDone): <asp:TextBox ID="TextBox1" runat="server"
onblur="GetName()"> </asp:TextBox> <script type="text/javascript" language="javascript"> function GetName() { GetCustomerData.GetCustomerName("ALF KI", WhenDone); } </script> Catch the Result The routine you call when the Web Service returns must accept a single parameter, which consists of the data returned by the Web Service. This sample routine displays only the returned data: <script type="text/javascript" language="javascript"> function WhenDone(result) { alert(result); } </script> A more robust example might pick up a value from a control on the page. Similarly, most applications will update another control on the page when the Web Service returns its result. You can simply hard code the control names into your client-side scripts. However, ASP.NET sometimes alters the server-side name of the control to create the client-side name when it generates the HTML for a control. Also, hard-coding the names of your controls makes your application more brittle: If you change the name of a control and dont update your client-side code, the problem wont show up until the client-side script executes in the browser. A better solution is to generate the client-side code in your server-side code and insert the generated code into your page using the RegisterClientScriptBlock methods of the ClientScriptManager object (available through the Pages ClientScript property). This allows you to insert a controls ClientID property into your JavaScript to ensure that the code uses the clientside name generated by ASP.NET. Be aware that you get a compile-time error if you change the name of a control, but and dont update your dynamically generated JavaScript. This example adds client-side code that passes the value of the txtCustomerID control to the GetCustomerName WebMethod: Me.ClientScript.RegisterClientScriptBlock(Me .GetType, _ "CallWebService", "function GetName()" & _ "{GetCustomerData.GetCustomerName(" & _ "form1." & Me.txtCustomerID.ClientID & _
".value, WhenDone);}", _ True) The browser result looks like this, once formatted for readability: <script type="text/javascript"> <!-function GetName() { GetCustomerData.GetCustomerName( form1.txtCustomerID.value, WhenDone); }--> </script> All this technology is useful, but you face two issues you cant ignore: compatibility and performance. For compatibility, I tested the sample code in Opera, FireFox, and Navigator, but it worked only with Internet Explorer. However, I should note that I didnt spend any time tracking down the problems. The equivalent code on the Atlas site worked in both Internet Explorer and FireFox, suggesting that compatibility among browsers is possible. I didnt spend much time on testing compatibility, but I was keenly interested in performance. My test was simple: How long does it take to request the customer name 100 times using Atlas versus using some other technique? The most surprising result from my tests is the performance difference between using a Smart Client and Atlas code to access the same Web Service. The Atlas code is 25 percent faster in accessing the Web Service 100 times, compared to an equivalent Smart Client. And running Atlas code requires only that the user has Internet Explorer or (potentially) any modern browser. Compare Performance The Atlas code isnt faster in all cases. For example, the Atlas code takes about five times longer to post a WebForm page back to the server and retrieve the data 100 times before returning the page to the client. But dont run for the hills, yet. If you change up the test so that you include the data retrieval code in the WebForm page, the WebForm page calls the same Web Service as the Atlas code, and performance starts to equalize. In this example, the Atlas version is only 10 percent slower. This analysis ignores an important point: You can interleave the Atlas codes 100 trips with user activity. A fairer test might be to compare posting the WebForm back to the server 100 times, then retrieving the data each time. The time to post a WebForm page back to the server 100 times would be measured in minutes, not seconds, so I didnt bother with this test. There is another choice in ASP.NET 2.0: client-side callbacks. A client-side callback that makes a single trip to the server and calls the Web Service 100 times has performance equivalent to posting back the complete WebForm. In addition, the client-side callback model is less flexible than the Atlas model. While Atlas code can call a Web Service directly and integrate those calls with any other client-side code, client-side callbacks must call a single method in the host WebForm, using code generated by ASP.NET. Interleaving callbacks with user actions would not only be difficult with a client-side callback, but would be as time-consuming as a full postback. Accessing a Web Service in Atlas is fast and flexible, but you lose that benefit if the number of trips to the server increases dramatically. You should pass and receive multiple values in each trip to the server. In other words, you need to pass objects, rather than simple data types, on each Web Service call. Fortunately, passing objects in Atlas is easy. For example, this class module supports holding all the information for a Customer contact:
Public Class ContactData Private _Customerid As String Private _ContactName As String Private _ContactTitle As String Public Property Customerid() As String Get Return _Customerid End Get Set(ByVal value As String) _Customerid = value End Set End Property ' repeat for ContactName and ContactTitle properties End Class You pass WebMethod a ContactData object and use the CustomerID property to create and return a ContactData object: <WebMethod()> _ Public Function GetContactInfo(ByVal ContactData As String) As ContactData Dim strCustomerid As String strCustomerid = ContactData.Customerid ' code to retrieve contact information with customerid ' goes here Dim cd As New ContactData cd.ContactName = dr("ContactName") cd.ContactTitle = dr("ContactTitle") cn.Close() Return cd End Function Call WebMethod Calling this WebMethod is simple enough. Your client-side code must create a CustomerData object and set the Customerid property. This GetContact function defines a variable using the ContactData class from the Web Service, then sets the variables CustomerID property before passing the object to the Web Service: function GetContact() { var cd = new ContactData();
cd.CustomerID = form1.txtCustomerID.value; GetCustomerData.GetContactInfo(cd, WhenDone); } The client-side function that Atlas calls when the Web Service returns still accepts a single result. However, the result will have all of the properties of the object returned from the Web Service. The GetContactInfo method returns a ContactData object, so this sample code can access the ContactTitle and ContactName properties of that object: function WhenDone(result) { form1.txtContactName.value = result.ContactName; form1.txtContactTitle.value = result.ContactTitle; } Theres still much to be done before Atlas is ready for prime time. Debugging is awkward, and the lack of IntelliSense support in Source view means that coding is error prone. Yes, Atlas code outperformed a Smart Client in my test, but Smart Client applications remain more flexible compared than AJAX applications. For example, Smart Clients, with the appropriate trust settings, can access resources on the client, while AJAX applications are more strictly limited to the sandbox. In addition, Smart Clients have a wider range of connection options than AJAX. Its much easier to use Remoting from a Smart Client than from JavaScript or to make a network request programmatically using HTTPRequest. Unlike AJAX clients, a Smart Client doesnt need to be connected to the Internet or an intranet to execute. Finally, developers are limited to what can be done with HTML and Java applets in AJAX applications. Windows Forms developers have both a richer event model and a larger set of user interface controls available to them. All that said, its quite cleareven in this beta versionthat Atlas has the potential to change the way that you build Web applications. Its important that there be compatibility between browsers, but I suspect that will develop over time. The beta version also has considerably more functionality than taken advantage of by the sample application. For example, you can add an attribute to your WebMethod to cache the WebMethods result. This means repeated calls wont cause server-side code to re-execute unnecessarily. Using Atlas, you can also create Web Services in your JavaScript code and call them from your Atlas application. But Web Services are only part of the Atlas functionality: Atlas also allows you to databind the ASP.NET DataView controls without having to post back to the server to refresh the DataView. Its worth your time to visit the Atlas site, where a wide range of quickstart tutorials will show you how Atlas can change the way you build Web applications. About the Author Peter Vogel (MBA, MCSD) is a principal at PH&V Information Services. Peter teaches for Learning Tree International and wrote its ASP.NET and Technical Writing courses. Peter is the editor of the Smart Access newsletter from Pinnacle Publishing and wrote Visual Basic Object & Component Handbook (Prentice Hall). He is currently working on Professional Custom Controls, Web Parts, and User Controls with ASP.NET 2.0 for Wrox.
In the Zimbra Collaboration Suite all of the indexing and searching is done on the server side, which according to Dargahi has a number of benefits. One benefit is that as mail is delivered, it's indexed and through a combination of Lucene and MySQL, and Zimbra has built a number of optimization and search algorithms around it. With the index on the server side and "preindexed" through an inverted-list technology using Lucene, users are able to do rapid searches and information retrieval no matter on which client is being used, whether at an airport kiosk, a laptop computer, or a mobile device. "We have a rich query language, and we make mail very searchable," Dargahi said. "Much like Gmail, you can have large volumes of mail, and you can very rapidly search for content, and we even have a graphical tool that allows you to do that." The idea, Dargahi said, is to give users innovative, compelling features that make collaboration or messaging easier to use and provide seamless integration between calendaring contacts and mail with existing information systems. "I like to call it information when you need it, just-in-time delivery of information," Dargahi said. For the administrator and on the server side, the technology provides all of the tools and capabilities expected in a modern system, making it easier to administer, making it scalable, and enhancing it with hierarchic storage management, archiving, and back up and restore. "We're basically bringing messaging into the new millennium; we shouldn't be stuck back in the eighties. There's been so much advancement in technologies, so let's leverage that." On the server side Zimbra has taken best-of-breed open source technologies like Postfix and MySQL and used Java to build a collaboration suite on top of them that provides the unique messaging capabilities that the suite offers. "It's totally open, we provide SOAP APIs, we get all the benefits of Java, we don't have cluster overflow issues from a security perspective, and Java allows us to rapidly develop the systems," Dargahi said. Despite the many benefits for administrators and end users, the Zimbra Collaboration Suite and other related Zimbra technologies will offer developers a lot of opportunities for customizing Zimbra's system for their organizations. Dargahi said that early on the company realized they would need to develop a toolkit that he said "you can think of as sort of a mini JDK." "When we started developing this system there really was no Ajax yet, and we sort of toyed with, 'should we do a thick client? Should we do a thin client?' We looked at the browser and really discovered that you can push it a long way," Dargahi said. "As much as Swing provides buttons and tables and layout managers, you need the same kind of thing for building an application on the Web. So as part of developing our systems we built up this toolkit. There's a .Net component, there's an I/O component, there's a UI component, and we felt that this could be useful for other folks who want to develop Ajax applications. At the end of the day, from Zimbra's perspective, we want to see Ajax succeed in a nonproprietary way, so we felt that putting it under the Apache license and making it freely available would benefit everyone." Browser Magic Zimbra has made recent announcements that highlight two noteworthy technologies, which will flourish in upcoming product releases. The most recent announcement generating some excitement is Ajax Linking and Embedding (ALE), which essentially does for browser-based applications what Microsoft's Object Linking and Embedding (OLE) does for desktop applications, allowing the user to incorporate, or embed, a component from another application source (for example, an Excel spreadsheet) into a document in the target application (for example, Microsoft Word). Dargahi said ALE lets you do rich text editing by putting a browser to work in design mode, where "it behaves very much like a word processor." "The nice thing about [ALE] is that it's operating systemagnostic and platform-agnostic, so it's not proprietary," Dargahi said. "We contribute it to open source. [The ALE technology] really shows you how far you can take these applications. We've heard so much about zero footprint clients, and so far, to date, they really haven't delivered. Now with a new generation of clients
like Zimbra, we're seeing that you can get compelling applications, thin applications, to the end user that they enjoy using, which we think is exciting." Another example Dargahi offered is the ability to create an ALE component that implements a drawing tool. "We allow different APIs so folks can extend Zimbra. On the other side of it we're integrating with Zimbra all of our APIs; our Web client essentially uses a bunch of SOAP APIs. It's all open and all documented, so if folks want to integrate external systems with Zimbra that way, they can certainly do that as well. There's a number of different dimensions with which to integrate and extend Zimbra, and I think it's pretty exciting from a developer perspective," he said. The other recent technology is a framework or mechanismcalled Zimletsthat allows developers to integrate the Zimbra Collaboration Suite with different information systems. Dargahi explained a Zimlet as an entity that a developer can create that integrates, for example, with a third-party information system, such as Salesforce.com, in such a way that a user can drag a contact or e-mail message onto the Zimlet that will in turn populate the Salesforce.com account with that information. "Zimlets essentially provide both a client- and server-side framework for allowing you to do that [integration], which makes the Zimbra application extensible, both on the client and server side," Dargahi said. "I can integrate it with Internet or intranet applications or systems," he said, citing examples like integrating it with maps, with the Zimbra-based calendar, and so on. Zimbra works closely with the open Ajax initiative. Their toolkit is part of the open Ajax effort and is one of the toolkits that's supported directly in Eclipse. Though the company is quite young, customers are warming up to Zimbra's offerings. In addition to organizations in the financial industry, Zimbra has also acquired customers that are doing some other innovative projects with the technology. Dargahi said that in the medical imaging space Zimbra has been integrated with radiology data so that physicians can see radiology information. Other customers have integrated Zimbra with flight information to facilitate booking travel. Visit Zimbra's Web site for more information (http://www.zimbra.com). About the Author Terrence O'Donnell is editor of Java Pro and senior editor of FTPOnline.