<?xml version="1.0" encoding="ISO-8859-1" ?>

<?xml-stylesheet type="text/xsl" 
href="/lib/xsl/devedge-1.00/article_en.xsl"?>
<nde:article 
  xmlns:nde="http://devedge.netscape.com/2002/de"
  url="/viewsource/2003/wsdl/01/"
  xmlns="http://www.w3.org/1999/xhtml"  
  xml:lang="en">

  <nde:header>
    <nde:title>Accessing Web Services In Netscape 7.1/Mozilla 1.4 Using WSDL Proxying</nde:title>
    <nde:category>Article</nde:category>
    <nde:authlist>
      <nde:author>
        <nde:authname>Doron Rosenberg</nde:authname>
        <nde:authaffil>Netscape Communications</nde:authaffil>
        <nde:email href="/community/feedback/">Feedback</nde:email>
      </nde:author>
    </nde:authlist>
    <nde:pubdate year="2003" month="06" day="06"/>
    <nde:moddate year="2003" month="06" day="30"/>

    <nde:channels>
      <nde:channel id="viewsource" />
      <nde:channel id="gecko" />
      <nde:channel id="xml" />
      <nde:channel id="ns-7" />
      <nde:channel id="ns-7.1" />
    </nde:channels>
    
    <nde:summary>
      Learn how to access web services in Netscape 7.1/Mozilla 1.4 using WSDL proxying from JavaScript.
    </nde:summary>  

  </nde:header>

 <nde:head>
   <style type="text/css">
     .sourceCode {padding-left: 5px; padding-top:5px;}        
     
     .sourceView {margin: 5px; padding: 5px; border: 1px dashed grey; font-family: Verdana,sans-serif;}
     .sourceInfo {padding-bottom: 5px; font-size: 0.9em; padding-right: 5px; border-bottom: 1px dashed gray; 
                  border-right: 1px dashed gray;}

     .color1 {color:green;}
     .color2 {color:blue;}
      /*#content h2 {margin-top: 1em}*/
    </style>
</nde:head>

<nde:content>

  <h2>Introduction</h2>
     <p>
       The <a href="/viewsource/2003/soap/01/">SOAP</a> (Simple Object Access Protocol) article covered how to access 
       web services using the low-level SOAP API in Mozilla-based browsers.  SOAP is an XML language that forms the basis upon which web services are built. 
       Using SOAP has been somewhat tedious, requiring manual construction and delivery of the SOAP envelope the web service expects.  
			 SOAP-based response also has need to be parsed manually for the information required.
     </p>
     
     <p>
       Relief is in sight: As of Netscape 7.1/Mozilla 1.4, <a href="/central/gecko/">Gecko</a> supports WSDL 1.1 (<a href="http://www.w3.org/TR/wsdl">Web Services Description Language</a>) proxying.
       A WSDL file describes the interfaces that a web service provides, similar to a header file in C or IDL.  Using the WSDL file, 
       Gecko can offer developers a way to "script" web services as if it were a native object, hiding the SOAP and XML aspect.  For example,
       after creating a proxy instance of a web service using WSDL, one can call methods on the proxy object like one would on any
       JavaScript object (<code>proxy.getTranslation("en_fr", "Hello World")</code> for example).
     </p>

     <p>
       This article covers Netscape 7.1/Mozilla 1.4's WSDL support.  It also addresses the cross-domain issue and new security model
       that Netscape is proposing that would allow web services to determine if the client can access the service from any domain or only specific domains.
     </p>
     
     <p>
       This article draws upon the <a href="http://xmethods.net/ve2/ViewListing.po?key=uuid:E00104D5-2AC8-9DEA-EF4C-8BD920E1B4DD">Babelfish 
       web service</a> provided by <a href="http://www.xmethods.net">XMethods</a>, which was the first site to implement the
       new Gecko web services security model, allowing Gecko browsers to directly access web services from other domains.
     </p>
       
  <h2>Creating a WSDL Proxy</h2>
     
    <p>
      A web service proxy can be created from JavaScript by creating an instance of <code>WebServiceProxyFactory</code>.  The actual
      WSDL file is loaded by calling the <code>createProxyAsync</code> method on the <code>WebServiceProxyFactory</code> object.      
    </p>
    
    <p>
      <code>createProxyAsync</code> takes in five parameters.</p>
			<ol>
			<li>The first one is the location of the WSDL file.  For the BabelFish web service, it is 
      <a href="http://www.xmethods.net/sd/2001/BabelFishService.wsdl">http://www.xmethods.net/sd/2001/BabelFishService.wsdl</a>.</li> 
			<li>The second parameter is the port name.  The port name can be found in the WSDL file itself, under the <code>service</code> element as shown in 
      figure 1.</li>
			<li>The third argument is an optional qualifier, which one doesn't have to worry about.</li>
			<li>The fourth parameter is a boolean
                          indicating if the proxy should be loaded asynchronous or not.  Netscape 7.1/Mozilla 1.4 doesn't support synchronous proxy creation. Since
      the method name itself contains "Async", this parameter is somewhat redundant and should always be set to <code>true</code>.</li>
			<li>The final 
      parameter is the callback function (the creation listener) that gets called once the proxy is generated, which is discussed in detail in 
      the next section.</li>    
			</ol>
         
     <p>     
       <div class="sourceView">
         <span class="sourceInfo">Figure 1: Instantiating and initializing a web service proxy</span>
    
         <p class="sourceCode">
           <strong>JavaScript:</strong><br />
           var factory = new WebServiceProxyFactory();<br />
           factory.createProxyAsync("http://www.xmethods.net/sd/2001/BabelFishService.wsdl", <span class="color1">"BabelFishPort"</span>, "", true, aCreationListener); <br /><br />
           
           <strong>WSDL:</strong> <br />
           &lt;?xml version="1.0"?><br />
           &lt;definitions name="BabelFishService" ...><br />
             &#160;&#160;...<br />
             &#160;&#160;&lt;service name="BabelFishService"><br />
               &#160;&#160;&#160;&#160;&lt;documentation>Translates text of up to 5k in length, between a variety of languages.&lt;/documentation><br />
               &#160;&#160;&#160;&#160;&lt;port <span class="color1">name="BabelFishPort"</span> binding="tns:BabelFishBinding"><br />
                 &#160;&#160;&#160;&#160;&#160;&#160;&lt;soap:address location="http://services.xmethods.net:80/perl/soaplite.cgi"/><br />
               &#160;&#160;&#160;&#160;&lt;port><br />
             &#160;&#160;&lt;/service><br />
           &lt;/definitions><br />
         </p>
       </div>        
     </p>
     
     <p>
      To recap, an interface look at the creatProxyAsync method:
      <dt class="interfaceItem">void createProxyAsync (String wsdlURL, String portname, String qualifier, boolean isAsync, WebServiceProxyCreationListener listener)</dt>
     </p>
     
  <h2>The Callback</h2>
  
  <p>
    As noted above, the last parameter <code>createProxyAsync</code> takes is a creation listener.  The creation listener is an object 
    which implements several methods. The creation listener is called when either the proxy generation has been successful or if an error has 
    occurred.  It is also used when a method is called on the proxy object.
  </p>
  
  <p>
    The creation listener is a variable that holds several methods.  Since the proxy is generated asynchronously, 
    it holds a <code>onLoad</code> function that gets called when the proxy has been initialized, meaning it is now possible
    to call methods on the proxy.  <code>onError</code> is called if an error occurs while generating the proxy or during a
    method call.
  </p>
  
  <p>
    Calling a method on the proxy is also executed asynchronously.  Therefore, the creation listener holds callbacks for each
    method that will be called.  The methods for these follow a specific naming scheme: <code>{methodName}Callback</code>.
    The BabelFish web service only contains one method, <code>BabelFish</code> (in the WSDL, methods are denoted by the 
    <code>operation</code> element), so the callback function is called <code>BabelFishCallback</code>.  As can be seen from the 
    WSDL file (relevant parts shown in figure 2), the <code>BabelFish</code> method takes in an <code>BabelFishRequest</code>, which
    is composed of two parameters, and returns the translated value as a string.
  </p>
  
  <p>
    <div class="sourceView">
    <span class="sourceInfo">Figure 2: </span>
    
      <p class="sourceCode">
        <strong>JavaScript:</strong><br />
          var listener = {<br /><br />

            &#160;&#160;// gets called once the proxy has been instantiated<br />
            &#160;&#160;<strong>onLoad</strong>: function (aProxy) <br />
            &#160;&#160;{<br />
              &#160;&#160;&#160;&#160;gProxy = aProxy;<br />
              &#160;&#160;&#160;&#160;gProxy.setListener(listener);<br />
              &#160;&#160;&#160;&#160;requestTranslation(aValue);<br />
            &#160;&#160;},<br /><br />
         
            &#160;&#160;// gets called if an error occurs<br />
            &#160;&#160;<strong>onError</strong>: function (aError) <br />
            &#160;&#160;{<br />
              &#160;&#160;&#160;&#160;alert("An error has occured while processing the WSDL file: " + aError);<br />
            &#160;&#160;},<br /><br />
      
            &#160;&#160;// callback function is hardcoded to {methodname}Callback in 1.4beta<br />
            &#160;&#160;<strong>BabelFishCallback</strong>  : function (aResult) <br />
            &#160;&#160;{<br />
              &#160;&#160;&#160;&#160;alert(aResult)<br />
            &#160;&#160;}<br />
          };<br /><br />
          
          function requestTranslation(aValue){<br />
            &#160;&#160;if (gProxy) {<br />
              &#160;&#160;&#160;&#160;gProxy.BabelFish("en_fr", aValue);<br />
            &#160;&#160;} else {<br />
              &#160;&#160;&#160;&#160;alert("Error: Proxy set up not complete!");<br />
            &#160;&#160;}<br />
        }<br /><br />
        
        <strong>WSDL:</strong><br />

          &lt;message name="BabelFishRequest"><br />
            &#160;&#160;&lt;part name="translationmode" type="xsd:string"/><br />
            &#160;&#160;&lt;part name="sourcedata" type="xsd:string"/><br />
          &lt;/message><br /><br />

          &lt;message name="BabelFishResponse"><br />
            &#160;&#160;&lt;part name="return" type="xsd:string"/><br />
          &lt;/message><br /><br />

          &lt;portType name="BabelFishPortType"><br />
            &#160;&#160;&lt;operation <span class="color1">name="BabelFish"</span>><br />
              &#160;&#160;&#160;&#160;&lt;input message="tns:BabelFishRequest"/><br />
              &#160;&#160;&#160;&#160;&lt;output message="tns:BabelFishResponse"/><br />
            &#160;&#160;&lt;/operation><br />
          &lt;/portType><br />
         </p>   
       </div>    
  </p>
  
  <h2>Example</h2>
  <p>
    This example takes the parts shown in previous figures and creates a fully working example of how to call the BabelFish
    web service to translate an inputted string.
  </p>
  
  <p>
    The user is given a form to fill out, with two dropdowns and an input field.  The first dropdown (id="lang_from") contains the language to translate
    from, and the second (id="lang_to") has the language to translate to.  The input is used to enter the string that will be translated.  There is
    also a button labeled "translate", which calls the function <code>initTranslation</code>.  The function gets the chosen languages
    from the form and checks if they are the same. If they are different, the function <code>Translate</code> is called.  The
    Babel Fish web service takes in two methods: a string of the format fromLanguage_toLanguage and the string to translate.
  </p>
  
  <p>
    <div class="sourceView">
      <span class="sourceInfo">Figure 3: Initializing the translation</span>
    
      <p class="sourceCode">
        <strong>JavaScript:</strong><br />           

        function initTranslation(){<br />
          &#160;&#160;var fromLang = document.getElementById('lang_from').value;<br />
          &#160;&#160;var toLang = document.getElementById('lang_to').value;<br /><br />
      
          &#160;&#160;if (fromLang != toLang)<br />
            &#160;&#160;&#160;&#160;Translate(fromLang+'_'+toLang, document.getElementById('inputValue').value);<br />
          &#160;&#160;else<br />
            &#160;&#160;&#160;&#160;alert("Translating a language to itself is kinda useless :)");<br />
        }  
      </p>
    </div>
  </p>
  
  <p>
    The <code>Translate</code> function is the one that actually takes care of the web service call.  It first checks if a
    proxy object has already been created by checking if the global variable <code>gProxy</code> is not <code>null</code>.  If it is
    null, then a creation listener is generated and stored into a variable called <code>listener</code>.  It then calls the function
    <code>createProxy</code> with the creation listener.  If however the proxy was already created, the <code>requestTranslation</code>
    function is called.
  </p>
  
  <p>
    <div class="sourceView">
      <span class="sourceInfo">Figure 4: Proxy generation</span>
    
      <p class="sourceCode">
        <strong>JavaScript:</strong><br />           

        var gProxy = null;<br /><br />

        function Translate(aLangToFrom, aString){<br />
          &#160;&#160;if (!gProxy) {<br />
            &#160;&#160;&#160;&#160;var listener = {<br /><br />

              &#160;&#160;&#160;&#160;&#160;&#160;// gets called once the proxy has been instantiated<br />
              &#160;&#160;&#160;&#160;&#160;&#160;onLoad: function (aProxy)<br />
              &#160;&#160;&#160;&#160;&#160;&#160;{<br />
                &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;gProxy = aProxy;<br />
                &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;gProxy.setListener(listener);<br />
                &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;requestTranslation(aLangToFrom, aString);<br />
              &#160;&#160;&#160;&#160;&#160;&#160;},<br /><br />
         
              &#160;&#160;&#160;&#160;&#160;&#160;// gets called if an error occurs<br />
              &#160;&#160;&#160;&#160;&#160;&#160;onError: function (aError)<br />
              &#160;&#160;&#160;&#160;&#160;&#160;{<br />
                &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;alert("An error has occured: " + aError);<br />
              &#160;&#160;&#160;&#160;&#160;&#160;},<br /><br />
      
              &#160;&#160;&#160;&#160;&#160;&#160;// callback function is hardcoded to {methodname}Callback<br />
              &#160;&#160;&#160;&#160;&#160;&#160;BabelFishCallback  : function (aResult)<br />
              &#160;&#160;&#160;&#160;&#160;&#160;{<br />
                &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;document.getElementById("results").innerHTML = aResult;<br />
              &#160;&#160;&#160;&#160;&#160;&#160;}<br />
            &#160;&#160;&#160;&#160;};<br /><br />
       
            &#160;&#160;&#160;&#160;<span class="color1">createProxy(listener)</span>;<br />
          &#160;&#160;} else {<br />
            &#160;&#160;&#160;&#160;<span class="color2">requestTranslation(aLangToFrom, aString)</span>;<br />
          &#160;&#160;}<br />
        }<br /><br />

        function <span class="color1">createProxy</span>(aCreationListener){<br />
          &#160;&#160;try {<br />
            &#160;&#160;&#160;&#160;var factory = new WebServiceProxyFactory();<br />
            &#160;&#160;&#160;&#160;factory.createProxyAsync("http://www.xmethods.net/sd/2001/BabelFishService.wsdl", "BabelFishPort", "", true, aCreationListener);<br />
          &#160;&#160;} catch (ex) {<br />
            &#160;&#160;&#160;&#160;alert("Failed creating the proxy: "+ ex);<br />
          &#160;&#160;}<br />
        }<br /><br />
        
        function <span class="color2">requestTranslation</span>(aLangToFrom, aString){<br />
          &#160;&#160;if (gProxy) {<br />
           &#160;&#160;&#160;&#160;gProxy.BabelFish(aLangToFrom, aString);<br />
          &#160;&#160;} else {<br />
            &#160;&#160;&#160;&#160;alert("Error: Proxy hasn't been set up correctly!");<br />
          &#160;&#160;}<br />
        }<br /><br />
      </p>
    </div>
  </p>
  
  <p>
    <code>createProxy</code> is run the first time a translation is requested.  It instantiates a <code>WebServiceProxyFactory</code>
    and creates a new proxy using <code>createProxyAsync</code>, which uses the creation listener.  Once the proxy has been created,
    the <code>onLoad</code> method defined in the creation listener is called.  It stores the generated proxy in the global
    <code>gProxy</code> variable, sets the listener to be the creation listener and calls <code>requestTranslation</code>, as the proxy
    is now ready to be used.
  </p>
  
  <p>
    The <code>requestTranslation</code> function calls the <code>BabelFish</code> method on the proxy to initiate the web service
    call.  If the call is sucessfull, the <code>BabelFishCallback</code> method in the creation listener is called, which
    writes out the translated value into a <code>div</code>.  If the call failed for some reason (such as a SOAP fault was returned), 
    <code>onError</code> is called.
  </p>
  
  <p>
    The full example can be seen <a href="example.html">here</a> (requires Netscape 7.1/Mozilla 1.4 or above).
  </p>

  <h2>The Security Model</h2>  
  <p>
    One problem facing web services support in the browser is the cross-domain security model.  JavaScript is limited to only
    being able to load data from the same domain the JavaScript lives on.  For example, Netscape.com can only load XML using XMLHTTPRequest from the 
    netscape.com domain, and not from foo.com.  If a site is to be able to connect to a remove web service, a new security model
    is required.
  </p>
  
  <p>
    Netscape has proposed a security model to the W3C in which the web service provider determines if the web service is accessible by anyone,
    from certain domains only, or not at all from the Internet.  An in-depth look at the security model can be found 
    <a href="http://lxr.mozilla.org/mozilla/source/extensions/webservices/docs/New_Security_Model.html">here</a>.  In brief, the web service
    provider has to put an XML file in the top level directory where the web service is located.  In the case of <a href="http://www.xmethods.net">XMethods</a>, 
    it is located at <a href="http://services.xmethods.net/web-scripts-access.xml">http://services.xmethods.net/web-scripts-access.xml</a> and
    allows any domain to contact the web service.  This is why the example in this article can contact a cross-domain server.
  </p>
      
  <h2>Resources</h2>    
     <ul>
       <li><a href="http://lxr.mozilla.org/mozilla/source/extensions/webservices/docs/New_Security_Model.html">Mozilla's Web Services Security Model</a></li>
       <li><a href="/viewsource/2003/soap/01/">SOAP in Netscape Gecko-based Browsers</a> at DevEdge</li>       
       <li><a href="http://www.w3.org/TR/wsdl">W3C WSDL 1.1 Specification</a></li>
       <li><a href="/viewsource/2002/bypassing-security-restrictions/">Bypassing Security Restrictions and Signing Code</a></li>
       <li><a href="http://www.mozilla.org/projects/webservices/">Mozilla.org's Web Services Project Page</a></li>
     </ul>    
  </nde:content>

  <nde:related area="nde">
    <nde:item url="/central/xml">XML Central</nde:item>
    <nde:item url="/library/releases/netscape-7.1/">Other Articles about Netscape 7.1</nde:item>
  </nde:related> 

  <nde:related area="ext">
    <nde:item url="http://lxr.mozilla.org/mozilla/source/extensions/webservices/docs/New_Security_Model.html">Web Services Security Model</nde:item>     
    <nde:item url="http://www.mozilla.org/projects/webservices/">Web Services Projects Page</nde:item>
    <nde:item url="/viewsource/2003/soap/01/">SOAP in Netscape Gecko-based Browsers</nde:item>     
    <nde:item url="http://www.w3.org/TR/wsdl">W3C WSDL 1.1 Specification</nde:item>    
  </nde:related>
  
</nde:article>

