12.9. Mozilla's XML Extras and SOAP

Mozilla has built functions called XML Extras that allow the use of XML as data in both JavaScript and C++. Such functions are an XML Serializer, XMLHttpRequest, XML Parser, SOAP-based RPC, and XML Persistence. You can find more information about these functions, along with examples, at http://www.mozilla.org/xmlextras/.

The following sections assume that you are familiar with SOAP and .NET. If not, some good O'Reilly books available on these subjects can help get you started.

12.9.1. Mozilla, SOAP, and .NET

In this section, SOAP is used to access data in a .NET web service, therefore allowing the Snake game to have features such as a saved game score, a retrieved game score, and a list of high scores.

12.9.2. Setting Up a .NET Web Service

The easiest way to create a .NET web service is through Visual Studio.NET, which provides a template for creating these services. Example 12-14 shows a bare minimum of C# code used to compile the functions that return a value to the Snake game.

Obviously, a full implementation would need a database to store these scores. For this section, seeing how the interfaces work for these SOAP functions is more important.

The most important part of Example 12-14 is the WebServiceAttribute because it sets up a URI reference to the SnakeService object. When a request is sent from the Snake game to the .NET SnakeService, uri:SnakeScore becomes the name for the object providing functions for getting and setting the game's score.

In Example 12-14, all the parameter and return values are of the string type. Considering the brevity of this example, expanding it would not be hard. Functions using other objects and database connections would really make it a true web application.

12.9.3. .NET WSDL

.NET automatically generates WSDL interfaces inside a web service. Mozilla SOAP doesn't need to reference a WDSL file to make SOAP transactions.

Example 12-15 is a portion of the WDSL that .NET generates and is the specific portion that relates directly to sending raw SOAP calls to the SnakeService. Also, only the definitions for the GetScore function are in this abbreviated definition.

Example 12-15. Abbreviated WSDL as produced by .NET web service.

<?xml version="1.0" encoding="utf-8"?>
<definitions 
   xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" 
   xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
   xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:s0="uri:SnakeScore" 
   xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
   xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" 
   xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" 
   targetNamespace="uri:SnakeScore"
   xmlns="http://schemas.xmlsoap.org/wsdl/">
  <types>
    <s:schema elementFormDefault="qualified"  
              targetNamespace="uri:SnakeScore">
      <s:element name="GetScore">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="PlayerName"
                       type="s:string" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:element name="GetScoreResponse">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="GetScoreResult" 
                       type="s:string" />
          </s:sequence>
        </s:complexType>
      </s:element>
    </s:schema>
  </types>
  <message name="GetScoreSoapIn">
    <part name="parameters" element="s0:GetScore" />
  </message>
  <message name="GetScoreSoapOut">
    <part name="parameters" element="s0:GetScoreResponse" />
  </message>
  <portType name="SnakeServiceSoap">
    <operation name="GetScore">
      <input message="s0:GetScoreSoapIn" />
      <output message="s0:GetScoreSoapOut" />
    </operation>
  </portType>
  <binding name="SnakeServiceSoap" type="s0:SnakeServiceSoap">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" 
                  style="document" />
    <operation name="GetScore">
      <soap:operation soapAction="uri:SnakeScore/GetScore" 
                      style="document" />
      <input>
        <soap:body use="literal" />
      </input>
      <output>
        <soap:body use="literal" />
      </output>
    </operation>
  </binding>
  <service name="SnakeService">
    <port name="SnakeServiceSoap" binding="s0:SnakeServiceSoap">
      <soap:address 
         location="http://localhost/SnakeService/SnakeService.asmx" />
    </port>
  </service>
</definitions>

The most important thing to notice in this WSDL is the soapAction. In Example 12-15, uri:SnakeScore/GetScore is defined as the identifier for the SnakeScore object's GetScore function. This identifier makes the call to this function in Example 12-19.

12.9.4. SOAP Call XML Formats

When .NET and Mozilla serialize SOAP calls, they produce different XML formats. The namespace prefixes differ, and Mozilla produces more of these namespaces in its version of the SOAP message. However, the code comparison in Examples 12-16 and 12-17 mean fundamentally the same thing. Thus, .NET and Mozilla are able to communicate.

Realizing these formatting differences in Examples 12-16 and 12-17 is important because if you develop with SOAP by using .NET and Mozilla, you are bound to run across variations in your future software projects. Luckily the W3C has set a standard that Mozilla and Microsoft adheres to.

12.9.5. Adding SnakeService SOAP to Snake

Developers use built-in methods to just write JavaScript and use SOAP easily. There is no need for enhanced privileges because nothing could affect the client adversely. However, there are some limitations on how SOAP JavaScript is used.

Mozilla sets one level of security with the SOAP function by requiring that the web service and the JavaScript file that makes the SOAP functions access that service be on the same domain. For example, you will not encounter problems when running everything as localhost (as shown in the examples). If you try to move the JavaScript files to mozdev.org, though, they will no longer work.

Another limitation of the SOAP functions is that they can't be used directly in XUL documents. However, a hack, discussed in the next section, can get around this limitation.

12.9.6. Make SOAP Functions Work in XUL Documents

The best way to circumvent the SOAP-in-XUL-documents problem in Mozilla 1.0 (and probably 1.1) is to initially load the JavaScript file containing the SOAP functions from an HTML file, as shown in Example 12-18.

As stated earlier, scores.js (containing the SOAP functions) must exist outside the JAR file. It is loaded up into cache with this HTML document, and then the page redirects to the XUL file that has the SOAP function user interface. That JavaScript file is already loaded up in cache and will work fine.

Remember that doing this is a hack, but later versions of Mozilla that fix the SOAP-in-XUL-document problem would still not break this code.

12.9.7. Examining SOAP Functions for Snake

Example 12-19 shows how to create two functions (SaveScore and SaveScoreResponse) to handle SOAP transactions with the previously examined .NET web service.

The object defined here is the same as the namespace defined in Example 12-14. Again, this snake score object (uri:SnakeScore) is simply an identifier to that exact web service. The transportURI is the location of the web service. As you can see here, it runs localhost along with the files for the Snake remote Mozilla application. Moving into the actual SaveScore function, a PlayerName is pulled from a <textbox> in the XUL.

The method is the name of the function in the .NET web service with which this code will communicate. headers is an empty array because no SOAP headers are needed for this simple example. Two SOAPParameters are also defined here in an array, and they are just simple strings. Moving on down the code in Example 12-19, a new SOAPCall( ) is defined into the call variable. Two URIs are set up for this SOAPCall object: call.transportURI and call.actionURI, which combines an object and a method into one string. The next two lines, encode and asyncInvoke the SOAPCall and the encoded XML message, as shown in Examples Example 12-16 and Example 12-17, are sent to the .NET web service. When a response is received, the SaveScoreResponse function is called.

Currently, a hack is used in SaveScoreResponse so a DOM property accesses the XML of the returned SOAP message. This implementation is not the best way, but the optimal way doesn't currently work. Here is an easy version of the code:

ret = resp.getParameters(false, new Array( ));
alert(ret[0]);

If you put this code in SaveScoreResponse and it works, it could replace the code in Examples 12-16 and 12-17. You need to play around with these different SOAP functions and see what works for you. Again, the code just shown does not work in Mozilla 1.0, but will hopefully work in all future versions of Mozilla.

Example 12-20 shows the code for GetResponse and GetHighScores. Compare the JavaScript code to the code and WSDL in Examples 12-14 and 12-15 to see how they all work together.

Figure 12-10 shows how the XUL interface to these functions in Example 12-20 is designed. Here the score is replaced with "990," as this number is pulled from the code shown in Example 12-14.