[ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ]

Once this result is retrieved the code loops through the portfolio for the user again and retrieves a node reference to each of the symbols by using and XSLT query to retrieve each of the symbol nodes:

    loSymbol = oDOM.selectSingleNode("/stockquotes/quote[@symbol='" + lcSymbol+ "']")

From here the node can be imported into an object using wwXML and the low level ParseXMLToObject() method which takes an XML node as input and parses the properties below it into the object provided:

    loQuote = loXML.ParseXMLToObject(loSymbol,loQuote)

At this point the object is filled with the appropriate data and the portfolio record in the table is updated with this data using a standard VFP REPLACE command to fill in the data from the loQuote object.

SOAP Browser Clients

So far you may find that what I presented doesn't show huge improvements over what you can accomplish with straight XML messaging. The main reason is that some of the tools like wwXML's data conversions can make short work of generating and importing XML easily. However, if you're not using high level tools SOAP's advantages become much more obvious. To demonstrate I used SOAP to retrieve a single stock quote from the HTML based Web page.

In this scenario, only client side Jscript code in Internet Explorer 5.0 and later is used to make the SOAP method call and display the data in the browser without refreshing the entire Web page. If you look at figure 3 again, you can see the Get Single Quote textbox and the Go button. The Go button points to some Jscript in the Web page:

    SCRIPT SRC="wwsoap.js"></script>
    <script>
     
    SERVER_URL =  "http://www.west-wind.com/wconnect/soap/stockservice.wwsoap";
    oDOM = null;
    cPk = 0
     
    function GetQuote() {
      
       gcParameters = ""
       addParameter("lcSymbol",document.forms[0].symbol.value,"string")
     
       /// make the call - XML string result (object)
       lvResult =  CallMethod("GetStockQuote",SERVER_URL);  
     
       if (lvResult.length == 0) {
          alert("Invalid SOAP Response")
          return
       }
     
         
       oDOM = new ActiveXObject("Microsoft.XMLDOM");
       oDOM.loadXML(lvResult);
      
       if (oDOM.parseError.reason != "") {
          alert( oDOMparseError.reason);
          return;
       }
      
      
       lcHTML = "<hr><b>" + oDOM.selectSingleNode("/return/ccompany").text +
                "</b><br><b>Last:</b> " + oDOM.selectSingleNode("/return/nlast").text +
                "<br><b>Previous Close:</b> " + oDOM.selectSingleNode("/return/nopen").text +
                "<br><b>Change:</b> " + oDOM.selectSingleNode("/return/nnetchange").text ;           
      quoteresult.innerHTML = lcHTML;
    }
    </script>

When you click on the Go button, a SOAP call is initiated from the browser. The actual library functions for this are contained in wwsoap.js (you can look at that from the sample page as well – there's a link on the bottom for it). The key features are the AddParameter and CallMethod functions which work like their counterparts in wwSOAP. The difference is that Jscript doesn't support objects so these functions are not set up as methods. The Jscript SOAP implementation I created is also very basic and rather crude, but it will work well against the Web Connection Web Service and MS SOAP clients. wwSOAP.js uses the XMLHTTP component to communicate with the Web Server and retrieve the data.  

Since Jscript doesn't support objects and GetStockQuote() returns and object, the Jscript wwsoap implementation returns an XML string as a result value. This string is loaded into the XMLDOM and then the individual values are retrieved and build up into an HTML string, which is simply replaced into the HTML document. The entire section below the Quote text box is dynamically generated using a <span> tag whose .innerHTML property is set by the HTML created.

 

XMLHTTP and cross domain HTTP access

Creating SDL files and  MS SOAP compatibility

The examples I've shown where all built 
for West Wind Web Connection, but they 
can easily be applied to other development
tools.

You can take the StockService Web Service we created and use it with MS SOAP by simply compiling the Web Service into a COM object (make sure you add all dependent files to the project – they won't automatically get pulled in) and then running the MS SOAP SDL Wizard to generate the SDL and ASP files. You can look at the MS SOAP article on details for this.


Web Connection however, can also create an SDL file from your Web Service. This is a requirement in order for a Web Service to work with the MS SOAP client. To perform this task Web Connection contains a Wizard for generating the SDL file from an existing Web Service class as shown in Figure 4.

 

Figure 8 – The SDL Creation Wizard takes a Web Service class and generates a 
Service Description XML file from it.

An SDL file basically describes the functionality of a Web Service and acts as a sort of type library. The MS SOAP Toolkit actually requires use of an SDL file both on the client and the server side of a Web Service implementation, so any type of Web Service that wants to be accessed through MS SOAP must expose an SDL file.

The SDL Wizard generates an SDL file from any PRG file that contains an embedded OLEPUBLIC class with the same name as the file. So, if I have StockService.wwSOAP and a StockService class inside of it an SDL file called StockService.xml is generated in the same directory as the Web Service. Note that .wwSOAP files are really just PRG files with a different extension, so this Wizard will work on any PRG based class.

What gets generated is SDL that's compatible with the MS SOAP toolkit. Unfortunately, SDL specifications are not really specifications and Microsoft currently has 3 different implementations of it: The MS SOAP toolkit, the .Net implementation and the new forthcoming WSDL specification which supposedly will be supported both by .Net and MS SOAP. The current Wizard generates MS SOAP compatible SDL, because that is the only shipping SDL format that you're likely to find at the moment.

    The generated SDL file for our Stock Service looks as follows:

      <?xml version='1.0' ?>
      <!-- Generated by West Wind SOAP Helper at 10/23/2000 07:05:34 PM -->
      <serviceDescription name='stockservice'
          xmlns='urn:schemas-xmlsoap-org:sdl.2000-01-25'
          xmlns:dt='http://www.w3.org/1999/XMLSchema'
          xmlns:stockservice='stockservice'
      >
      <import namespace='stockservice' location='#stockservice'/>
       
          <soap xmlns='urn:schemas-xmlsoap-org:soap-sdl-2000-01-25'>
              <interface name='stockservice'>
               <requestResponse name="GetStockQuoteSimple">
                  <request ref="stockservice:GetStockQuoteSimple"/>
                  <response ref="stockservice:GetStockQuoteSimpleResponse"/>
                  <parameterorder>lcSymbol</parameterorder>
               </requestResponse>
               <requestResponse name="GetStockQuotes">
                  <request ref="stockservice:GetStockQuotes"/>
                  <response ref="stockservice:GetStockQuotesResponse"/>
                  <parameterorder>lcXMLSymbols</parameterorder>
               </requestResponse>
               <requestResponse name="GetStockQuote">
                  <request ref="stockservice:GetStockQuote"/>
                  <response ref="stockservice:GetStockQuoteResponse"/>
                  <parameterorder>lcSymbol</parameterorder>
               </requestResponse>
       
              </interface>
              <service>
                  <addresses>
                      <address uri='http://localhost/wconnect/soap/stockservice.wwsoap'/>
                  </addresses>
                  <implements name='stockservice'/>
              </service>
          </soap>
       
          <stockservice:schema id='stockservice' targetNamespace='stockservice'
                               xmlns='http://www.w3.org/1999/XMLSchema'>
            <element name="GetStockQuoteSimple">
               <type>
                  <element name="lcSymbol" type="dt:string"/>
               </type>
            </element>
            <element name="GetStockQuoteSimpleResponse">
               <type>
                  <element name="return" type="dt:integer"/>
               </type>
            </element>
            <element name="GetStockQuotes">
               <type>
                  <element name="lcXMLSymbols" type="dt:string"/>
               </type>
            </element>
            <element name="GetStockQuotesResponse">
               <type>
                  <element name="return" type="dt:string"/>
               </type>
            </element>
            <element name="GetStockQuote">
               <type>
                  <element name="lcSymbol" type="dt:string"/>
               </type>
            </element>
            <element name="GetStockQuoteResponse">
               <type>
                  <element name="return" type="dt:object"/>
               </type>
            </element>
       
          </stockservice:schema>
       
      </serviceDescription>

There are a couple of key elements in an SDL file. The top section describes the interface of the Web Service: The name of the methods and the parameters it expects as well as pointers to the implementation section of the schema. The implementation is provided below and shows in detail what types are passed as parameters and and what return types are returned.

Note that the GetStockQuote method of the Web Service returns an object parameter with MS SOAP does not understand, so it will fail on this call. You can change the type manually to dt:string to work around this, but realize that you'd have to parse the XML manually.

The other important thing in the SDL file is the <address> fragment which determines the locations of the actual Web Service request handler. It's important that you remember to either change this value or re-run the Wizard when you move your Web Service between a development machine and the live Web Server because the URLs likely will change. Here I generated it to localhost for local testing. When I put it online I'll want to call the service on www.west-wind.com.

We can test this out real quick using the MS SOAP Toolkit with the following code:

    oWire=CREATEOBJECT("Rope.WireTransfer")
    lcXML = oWire.GetPageByURI("http://localhost/wconnect/soap/stockservice.xml")
     
    *** Work around VFP COM case bug
    lcXML = STRTRAN(lcXML,"GetStockQuoteSimple","getstockquotesimple")
     
    oProxy = CREATEOBJECT("Rope.Proxy")
    ? oProxy.LoadServicesDescription(2, lcXML) && 1
     
    lvResult = oProxy.GetStockQuoteSimple("MSFT")
     
    ? "Result Type: ",VARTYPE(lvResult)
    ? lvResult
     
    oWire = .F.
    oProxy = .F.

and it will correctly return me the stock quote. Note that although the SDL file marks the return value as integer, MS SOAP will always return a string.

The big SOAP down

I've been using SOAP on a couple of projects now and we're finding that building Web Services is a huge timesaver, because it's real easy to build server functionality. We're also finding that although SOAP greatly simplifies building distributed applications in many cases, we still end up using XML extensively asparameters for method calls in order to avoid continuous server round trips. Most sample Web Services out there today are returning unrealistic types of information that does not reflect reallife applications. For SOAP to really be useful objects or other compound data needs to be passed. Passing objects is a good option, but for generic access this gets difficult to implement because the client application will have to deserialize the object into an existing structure, which requires some coupling. Object support varies greatly for the various SOAP implementations and some, notably the MS SOAP toolkit don't support objects at all, so passing data in XML format continues to be a common requirement even with SOAP.

There is also a mental hurdle to overcome with SOAP especially for those of us that have used XML in distributed applications before. Raw XML is simple and elegant and with the right tools it takes very little code to build XML solutions without requiring SOAP in the middle. In fact, when I first put out my SOAP implementation I asked around for some solutions that would really highlight a SOAP interface as opposed to a custom XML implementation. Lots of suggestions came up, but the reality is that none of them screamed out and said SOAP is the clear choice. The examples that were perfect for SOAP mostly revolved around simplistic examples that passed in single values and returned a single or multiple simple results. Most real-world applications don't work that way! This means you need to either objects with the varying support or you're back to using XML to pass as parameters with all the manual parsing this involves. I can live with that since we use XML extensively in every application anyway and we have tools like wwXML to make use of that XML nearly transparent. But those unfamiliar with XML or not using high level tools will not see that much of a benefit from SOAP over a custom XML implementation.

The big benefit of SOAP is a common interface that is universal. SOAP clients are already available for almost any platform and most let you make the SOAP call with just a couple of lines of code as I've shown. .Net takes this one step further and directly integrates remote calls into the environment which based on compiler settings can tell whether an object is local or remote and transparently call the remote object. As nice as that sounds, right now .Net is the only thing that can talk to .Net even it uses SOAP underneath the covers, as the implementation is proprietary relying on a custom SDL file format. This will likely change by the time .Net is ready, but right now it's just another piece in the puzzle to figure out.

On the downside when comparing SOAP to a plain XML solution is that SOAP has more overhead. For many applications that pass complex data around double XML parsing occurs. One to create your actual XML messages (object packaged as parameters or return values) or XML string parameters. And then there's the actual creation of the SOAP package and content. Request times over the Internet are good with most examples I ran taking under 1 second including Web round trips on a slow dial-up connection. This will be fine for most distributed applications, but it's not a replacement for a high performance application that uses DCOM now, which on the same network would run around 1,000 times faster making the same method call.

Still, getting familiar with SOAP and Web Services now is a good idea, because it's here to stay. Exposing your application logic with Web Services today will make sure that those services can be consumed by future applications that will be built around the SOAP standard.In the future SOAP tools will become even easier than what I've shown here. Languages will natively support SOAP so that objects can be accessed locally or globally using the same syntax. For today, we have to do a little more work, but we also get the benefit of full control – we can actually see what's happening under the hood.

SOAP is underlying a lot of Microsoft's new technology, especially .Net, so I would encourage you to learn more about it and start using it as it will prepare you for what's to come. The tools are here today, so you can use and integrate this technology today… Get to it!

Rick Strahl is president of West Wind Technologies on Maui, Hawaii. The company specializes in Web and distributed application development and tools with focus on Windows 2000 and Visual Studio. Rick is author of West Wind Web Connection, a powerful and widely used Web application framework for Visual FoxPro, West Wind HTML Help Builder and co-author of Visual WebBuilder. He's also a Microsoft Most Valuable Professional, and a frequent contributor to FoxPro magazines and books. He is co-publisher and co-editor of CoDe magazine, and his book, "Internet Applications with Visual FoxPro 6.0", is published by Hentzenwerke Publishing. For more information please visit: http://www.west-wind.com/.  

[ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ]