The NWS Tutorial

AJAX invocation (Javascript) of Web services



WARNING: This tutorial is being written! Do not hesitate to report any errors or suggestions.

Quick access:
   Limitations induced by a browser
   Generating a Javascript proxy through the WSDL
   Implementing a client for a chat program
   Synchronous calls against the asynchronous calls
   Exchanging objects through a Web service

We are going in this chapter to focuse us on the use of the web services within a web page, through the Javascript language. However, if you have any questions on the nature of a web service or on how to encode and deploy a web service through the NWS framework, i previously referral you to the dedicated chapter on this topic: implementation of Web services.

Limitations induced by a browser

CAUTION: in order to avoid some risks of viruses or hack of Web sites, a Web browser, and more specifically the AJAX components (which are used in the solution which will be shown), can't open HTTP connections only, to the Web server where the HTML page, that performs the request, is derived from. In other words, you can't use, through a NWS Web page incorporating the Javascript language, only some web services hosted by the HTTP server from which the page is from.

Any other attempt to connect to a Web service hosted by another HTTP server will result by an error in the context of a Web browser.

Generating a Javascript proxy through the WSDL

To invoke a web service, an HTTP request containing a SOAP flow must be generated. Similarly, the return value of the web service will be returned to you through a HTTP answer containing a SOAP flow. It would be inefficient that you may be required to take charge this SOAP formalism management: you are normally here to be careful on what must be your web application, but not always on how it should does it. The SOAP communication is not your responsibility.

The solution offered to you to invoke a Web service through Javascript takes care of the generation of a Javascript object (called a proxy) that will represent the remote Web service. The generation of this proxy is automatic: it is done by downloading the WSDL (Web Service Description Language) of the considered service. This proxy will expose as many methods that those exposed by the Web service. They will, moreover, the same names and may have (in the case of a synchronous call) the same signatures (prototypes) than their counterparts on the remote web service.

To generate the proxy, things are very simple: it is sufficient to invoke the construction function WebService passing it as a parameter the Service locating (URL). Some services are secured: you can also put on second and third parameter, the connection login and the associated password. Here is a small example of code that accesses to the Web service ChatRoom developed in the chapter Implementation of Web services.

 
importPackage( "corelib/services/web/javascript/webservices/WebService.js" );

var proxy = new WebService( "http://localhost:8080/VirtualCaddy/ChatRoom.ws" );
proxy.subscribe( "Essai" );
proxy.unsubscribe( "Essai" );
Example of generation of a web service proxy

Implementing a client for a chat program

By way of demonstration, we will code a client (as a NWS web page) to access from our chatroom service developed previously. You can note that this web page is a part of the demonstration application of the NWS framework. This client will be composed of two files: a NWS web page (that doesn't need any page class) and a Javascript code file attached to this page. I would remind you that the NWS framework automatically loads a file of JavaScript code, for a given web page, and this at the moment where the filenames (excluding extensions, of course) are the same. You will not find, of course, any tag <script> in the example below. The whole point of this example is to allow the page to refresh itself, but never completely without unloading this page. Therefore there will not any effect of flicker.

 
01 <?xml version="1.0" encoding="ISO-8859-1" ?>
02 <web:Html xmlns:web="corelib.services.web.components"
03           codeBehind="corelib.services.web.server.WebPage">
04     <head>
05         <title>ChatUser.wp - Web Service Javascript Invocation</title>
06     </head>
07     <body style="padding: 0px; margin: 0px; overflow: hidden">
08         <table style="width: 100%; height: 100%" border="0" cellspacing="0" cellpadding="0">
09         
10             <tr>
11                 <td rowspan="3" style="width: 20%">
12                     <textarea id="txtUsers" readonly="readonly"
13                               style="width: 100%; height: 100%">
14                     </textarea>                    
15                 </td>
16 
17                 <td style="width: 70%; height:24px;">
18                     <input id="txtUsername" type="text"
19                            style="width: 100%; height: 100%" />
20                 </td>
21 
22                 <td style="width: 10%; height:24px;">
23                     <input id="btnSubscribe" type="button"
24                            style="width: 100%; height: 100%" value="Subscribe" />
25                 </td>                
26             </tr>
27             
28             <tr>
29                 <td colspan="2" style="height: 100%">
30                     <textarea id="txtOutput" style="width: 100%; height: 100%"
31                               readonly="readonly"></textarea>
32                 </td>
33             </tr>
34             
35             <tr>
36                 <td style="width: 70%; height:24px;">
37                     <input id="txtMessage" type="text"
38                            style="width: 100%; height: 100%" />
39                 </td>
40 
41                 <td style="width: 10%; height:24px;">
42                     <input id="btnSend" type="button"
43                            style="width: 100%; height: 100%" value="Send" />
44                 </td>                
45             </tr>
46         
47         </table>
48     </body>
49 </web:Html>
File of web page "ChatUser.wp"

It remains only to add the event managers, through Javascript, to react to the activation of the various graphical elements of the interface. Note that the proxy is built at the beginning of the program (line 04), and then is reused in the event managers. Indeed, at the moment of the proxy construction, the WSDL is downloaded and analyzed through a DOM parser: it consumes a certain number of resources. It is therefore preferable to build it one time for all.

 
 01 importPackage( "corelib/services/web/javascript/webservices/WebService.js" );
 02 
 03 
 04 var chatRoom = new WebService( "http://localhost/VirtualCaddy/samples/ChatRoom.ws" );
 05 
 06 var txtUsername = null;
 07 var txtOutput = null;
 08 var txtUsers = null;
 09 var txtMessage = null;
 10 
 11 var btnSubscribe = null;
 12 var btnSend = null;
 13 
 14 var idClock = null;
 15 
 16 var strToSend = null;
 17 
 18 function enabledOrDisabledTextBoxes() {
 19     if ( idClock == null ) {
 20         txtUsername.disabled = false;
 21         txtUsername.focus();
 22         txtMessage.disabled = true;
 23     } else {
 24         txtUsername.disabled = true;
 25         txtMessage.disabled = false;
 26         txtMessage.focus();
 27     }
 28 }
 29 
 30 
 31 function refreshTextBoxes() {
 32 
 33     if ( strToSend != null ) {
 34         chatRoom.sendMessage( txtUsername.value, strToSend );
 35         strToSend = null;
 36     }
 37 
 38     var users = chatRoom.getUsers();
 39     txtUsers.value = users.join( "\n" );
 40 
 41     var messages = chatRoom.getMessages( txtUsername.value );
 42     var newContent = messages.join( "\n" );
 43     if ( newContent != "" ) newContent += "\n";
 44     txtOutput.value += newContent;
 45     
 46 }
 47 
 48 
 49 addEventHandler( window, "onload", function() {
 50 
 51     txtUsername  = document.getElementById( "txtUsername" );
 52     txtOutput    = document.getElementById( "txtOutput" );
 53     txtUsers     = document.getElementById( "txtUsers" );
 54     txtMessage   = document.getElementById( "txtMessage" );
 55     
 56     btnSubscribe = document.getElementById( "btnSubscribe" );
 57     btnSend      = document.getElementById( "btnSend" );
 58 
 59     addEventHandler( btnSubscribe, "onclick", function( event ) {
 60         if ( idClock == null ) {
 61             if ( txtUsername.value == null ) return;
 62             chatRoom.subscribe( txtUsername.value );
 63             btnSubscribe.value = "Unsubscribe";
 64             
 65             idClock = window.setInterval( "refreshTextBoxes();", 1000 );
 66         } else {
 67             chatRoom.unsubscribe( txtUsername.value );            
 68             btnSubscribe.value = "Subscribe";
 69             
 70             window.clearInterval( idClock );
 71             idClock = null;
 72 
 73             txtUsers.value = "";
 74             txtOutput.value = "";
 75         }
 76         enabledOrDisabledTextBoxes();
 77     });
 78     
 79     addEventHandler( btnSend, "onclick", function( event ) {
 80         strToSend = txtMessage.value;
 81         txtMessage.value = "";
 82         txtMessage.focus();
 83     });
 84     
 85     addEventHandler( txtUsername, "onkeyup", function ( event ) {
 86         if ( event.keyCode == 13 ) btnSubscribe.click();
 87     });
 88 
 89     addEventHandler( txtMessage, "onkeyup", function ( event ) {
 90         if ( event.keyCode == 13 ) btnSend.click();
 91     });
 92 
 93     enabledOrDisabledTextBoxes();
 94     
 95 });
 96 
 97 
 98 addEventHandler( window, "onunload", function() {
 99 	if ( idClock != null ) { 
100 		window.clearInterval( idClock );
101 		chatRoom.unsubscribe( txtUsername.value );			
102 	}
103 });
File of client code "ChatUser.js"

Synchronous calls against the asynchronous calls

NWS allows you two types of calls. A synchronous call: as the web method is not yet completed, the proxy does not give the hand. Or a asynchronous call. The client code invokes the method on the proxy and returns the hand immediately. Once the web service has finished its work, a reminder method (a callback) will be invoked to notify you the end of the treatment and return you the eventual return value.

The choice between a synchronous call or an asynchronous call is done simply by adding an extra parameter (the callback) to the invocation. Indeed, through the WSDL, we know the number of parameters expected by the web method: it is so easy to distinguish the two types of invocation. The file "samples/UseWebService.wp" of the demonstration application of the NWS framework shows you an example of the use of synchronous and asynchronous calls: you can compare the two types of call. Here is the contents of this file (note that in this simplistic example, the javascript is embedded in the page).

 
01 <?xml version="1.0" encoding="ISO-8859-1" ?>
02 <web:Html xmlns:web="corelib.services.web.components"
03           xmlns:demo="corelib.services.web.samples.virtualcaddy.webcomponents"
04           codeBehind="corelib.services.web.server.WebPage">
05     <head>
06         <title>Web service invocation sample</title>
07         <link rel="stylesheet" type="text/css" href="CssStyles.css" />
08     </head>
09     <body>
10         <h1>Web service invocation sample</h1> <br />
11     
12         <table border="0" width="300">
13         
14             <tr>
15                 <td width="45%"><input id="btnSync" type="button"
16                          value="Synchronized call" style="width: 100%" /></td>
17                 <td><input id="txtSync" style="width: 100%" /></td>
18             </tr>
19             <tr>
20                 <td><input id="btnAsync" type="button"
21                          value="Asynchronized call" style="width: 100%" /></td>
22                 <td><input id="txtAsync" style="width: 100%" /></td>
23             </tr>
24         </table>
25 
26         <script language="javascript">
27             importPackage( "corelib/services/web/javascript/webservices/WebService.js" );
28         
29             var addService = new WebService( "http://localhost/VirtualCaddy/Add.ws" );
30         
31             addEventHandler( document.getElementById( "btnSync" ), "onclick", function( event ) {
32                 var a = new Date().getMinutes();
33                 var b = new Date().getSeconds();
34                 var strResult = a + " + " + b + " == " + addService.add( a, b );
35                 document.getElementById( "txtSync" ).value = strResult;
36             });
37 
38             addEventHandler( document.getElementById( "btnAsync" ), "onclick", function( event ) {
39                 var a = new Date().getMinutes();
40                 var b = new Date().getSeconds();
41                 addService.add( a, b, function( result ) {
42                     var strResult = a + " + " + b + " == " + result;
43                     document.getElementById( "txtAsync" ).value = strResult;
44                 });
45             });
46         
47         </script>
48 
49     </body>
50 </web:Html>
File "samples/UseWebService.wp"

Exchanging objects through a Web service

The xwb services can help, through the parameters of methods and return values, to exchange some datas like objects. The solutions offered by the NWS framework are, of course, compatible with these possibilities. The use of the web services through the Javascript API (provided by the framework) allows to rebuild Javascript objects that represent the datas (of object type) acquired during a call. Warning: the methods of the server will not be rebuilt on the Javascript side. Only the public properties of the servers objects transported will be rebuilt.

For example, here is still a webpage issued from the demonstration application of the NWS framework. It invokes the recovery web service of the catalog items (see the chapter on the Web services on the server side) and displays the returned items by the service on the page.

 
01 <?xml version="1.0" encoding="ISO-8859-1" ?>
02 <web:Html xmlns:web="corelib.services.web.components"
03           xmlns:demo="corelib.services.web.samples.virtualcaddy.webcomponents"
04           codeBehind="corelib.services.web.server.WebPage">
05     <head>
06         <title>Web service invocation advanced sample</title>
07         <link rel="stylesheet" type="text/css" href="CssStyles.css" />
08     </head>
09     <body>
10         <h1>Web service invocation advanced sample</h1> <br />
11     
12         <p>
13             Please, enter an identifier:
14             <input type="text" id="txtIdentifier" value="1" style="width: 100px" />
15             <input type="button" id="btnDisplay" value="Display" />
16         </p>
17     
18         <h2>Liste des articles</h2>    
19 
20         <p>
21             <script language="javascript">
22 
23                 importPackage( "corelib/services/web/javascript/webservices/WebService.js" );    
24                 var catalogBrowser = new WebService( "http://localhost/VirtualCaddy/CatalogBrowser.ws" );
25 
26                 addEventHandler( document.getElementById( "btnDisplay" ), "onclick", function() {
27                     var identifier = document.getElementById( "txtIdentifier" ).value;
28                     var article = catalogBrowser.getArticle( identifier );
29                     alert( article.idArticle + ": " + article.brand + " " + 
30                            article.designation + " - " + article.unitaryPrice );
31                 });
32 
33                 var articles = catalogBrowser.getAllArticles();
34                 for( var i=0; i != articles.length; i++ ) {
35                     var theArticle = articles[i];
36                     document.write( theArticle.idArticle + ": " + theArticle.brand + " " + 
37                         theArticle.designation + " - " + theArticle.unitaryPrice + "&lt;br/&gt;" );
38                 }
39             </script>
40         </p>
41 
42     </body>
43 </web:Html>
File "samples/UseComplexWebService.wp"

CAUTION : NWS is proposed to you in BETA version to allow evaluation of this framework. Infini Software is released from any responsibility for the use of framework NWS. In addition, Infini Software can in no way be liable for the use of information contained in these tutorials.

Dominique LIARD - © 2007 SARL Infini Software - All rights reserved
Other brands and product names in these documents are the property of their respective owners.