Step 9 - Accessing the data over the Web from a Fat client

wwBusiness also includes support for serving data over the Web to a business object, essentially providing you the utilility of using data located on a Web Server in a Fat client application. If you've already built your business object and the backend uses SQL Server you need to make no changes to the business object for this to work.

In the following example I'll build a Visual FoxPro form that uses the cDeveloper class to retrieve the data from SQL Server over the Web. This involves a quick three step process:

  1. Setting up the server side application to handle our SQL requests
  2. Making a couple of property changes on the cDeveloper class
  3. Building the client side application using the cDeveloper class (form example here)

Setting up the server side

The first step is to set up the server side so that the wwBusiness object subclsass can access the remote database. The server side piece basically needs to be configured to determine which database to access and set up the connection to the database in order to provide the security rather than providing a generic access mechanism. To do this we'll need to set up a new Web Connection Process method - you can set this up in the DevProcess class we've worked on so far or put it into a completely different location. For demo purposes I'll put this data into my remote access demo process class which is located in wwdemo\http.prg. I create a method in there as follows:

************************************************************************
* HTTP :: HTTPSQL
****************************************
FUNCTION HTTPSQL_wwDevRegistry()

*** Create Data Object and call Server Side Execute method (wrapper for Process Method)
SET PROCEDURE TO wwHTTPSQLServer ADDITIVE
loData = CREATE("wwHTTPSQLServer")
loData.cConnectString = "server=(local);driver={SQL Server};database=wwDeveloper;pwd=sa;uid=;"
loData.cAllowedCommands = "select,execute,insert,update,delete,method,"

*** Retrieve XML input and then try to execute the SQL
loData.S_Execute(Request.FormXML())   

*** Send the output back to the client
loHeader = CREATEOBJECT("wwHTTPHeader")
loHeader.SetProtocol()
loHeader.SetContentType("text/xml")
loHeader.AddForceReload()
loHeader.AddHeader("Content-length",TRANSFORM(LEN(loData.cResponseXML)))
Response.Write( loHeader.GetOutput() )

Response.Write( loData.cResponseXML )
ENDFUNC
*  HTTP :: HTTPSQL_wwDevRegistry

That's all it really takes.

For more detailed info on how to configure the server side for reusable SQL connections and security check out How wwHTTPSQLServer works.

Testing the business object from the client side

Ok, now that the server is in place we can use the business object to actually hit the server. What happens here is that we'll have two instances of VFP running - one to run the Web Connection server, the other to host the client application using the wwBusiness subclass (cDeveloper). We can now try this with code like this:

DO WCONNECT
SET CLASSLIB TO wwDeveloper Additive
SET PROCEDURE TO wwHTTPSQL Additive

oDev = CREATEOBJECT("cDeveloper")
oDev.nDataMode = 4
oDev.cServerUrl = "http://localhost/wconnect/wc.dll?http~HTTPSQL_wwDevRegistry"

*** Sets up the HTTP object so we can configure it (optional)
oDev.Open()
oDev.oHTTPSQL.nConnectTimeout = 40
*oDev.oHTTPSQL.cUsername = "rick"
*oDev.oHTTPSQL.cPassword = "keepguessingbuddy"

? oDev.Query()   && Retrieve all records
? oDev.cErrorMsg

BROWSE

This should show you all the records from the server. Note that this data was retrieved from the Web Server, not from the local SQL Server. All operations that you could perform on the business object before still work as you would expect with data coming over the Web:

*** Load one object
oDev.Load(8)
? oDev.oData.Company
? oDev.oData.Name
oDev.oData.Company = "West Wind Technologies"
? oDev.Save()
? oDev.cErrorMsg

*** Create a new entry
 ? oDev.New(), "TEst"

 loData = oDev.oData

 loData.Company = "TEST COMPANY"
 loData.Name = "Rick Sttrahl"
 ? oDev.Save()

? oDev.Execute("select * from " + oDev.cFileName )
BROWSE

Ok, so this works just fine here, let's use the business object in a Fox form.

Using the wwBusiness Object in a Fox form with Web Data

(code for this example can be found in wwBusSample.scx)

We'll build the following form based applet using the wwBusiness object with data that is retrieved over the Web. Note, that you can also switch operation of the application easily to pull data either from VFP or SQL Server tables locally simply by changing the properties on the instance of the class on the form.


This form uses a wwBusiness object instance to retrieve all data from a remote data source over the Web. This form contains very little code and requires no changes to pull data from local VFP or SQL data, or by pulling data over down over the Web; voila, the power of business objects!

  1. Start out by creating a new Form and add the listbox named oList to the form.
  2. Add a page frame with the two tabs show above to the form.
  3. Add an instance of the the cDeveloper class (wwDeveloper in the sample SCX) and name it oDeveloper.
    Set the following properties:
    nDataMode=4
    cServerUrl=http://localhost/wconnect/wc.dll?http~HTTPSQL_wwDevRegistry
    lValidateOnSave=.T.
    Finally add THIS.Load(0) to the Init event - this makes sure the oData member is loaded with empty data that the form fields can bind to before any explict developer has been loaded.
  4. Add a Statusbar ActiveX control and name it oStatus. Add two panels to it.
  5. Now add all the fields to display onto the first page frame page. As you add each field set the ControlSource to the appropriate field of the business object's oData member like this:
    THISFORM.oDeveloper.oData.Company
    THISFORM.oDeveloper.oData.Name
  6. Add the following method to load the list box:
    LPARAMETERS lnListValue
    
    IF EMPTY(lnListValue)
       lnListValue = 1
    ENDIF
    
    THISFORM.Showstatus("Loading Developer List...")
    
    loDev = THIS.oDeveloper
    loDev.Query("select company,pk from wwDevRegistry ORDER BY Company","TDevelopers") 
    IF loDev.lError 
       MESSAGEBOX("Can't load customer data" + CHR(13) + ;
                  loDev.cErrorMsg,48,"wwBusiness Web Data Sample")
       RETURN .F.
    ENDIF
    
    THISFORM.oList.RowSourceType= 2
    THISFORM.oList.RowSource = "TDevelopers.Company"
    THISFORM.oList.Value = lnListValue
    
    THISFORM.ShowStatus("",RECCOUNT("TDevelopers"))

    THis is the most code we're going to write for this application in a single method!

    ShowStatus simply updates the two panels of the statusbar:

    LPARAMETERS lcPanelText, lnRecordCount
    IF EMPTY(lcPanelText)
      lcPanelText = "Ready"
    ENDIF
      
    THISFORM.oStatus.Panels(1).Text = lcPanelText
    
    IF !EMPTY(lnRecordCount)
       THISFORM.oStatus.Panels(2).Text = TRANSFORM(lnRecordCount) + " records "
    ENDIF

  7. Handle the movement through the list with the WHEN event like this:
    IF TDevelopers.Pk < 1
       RETURN
    ENDIF
    
    THISFORM.LoadDeveloper(TDevelopers.pk)
    

    and then implement LoadDeveloper like this:

    *** LoadDeveloper()
    LPARAMETERS lnPK
    
    THIS.ShowStatus("Loading Developer...")
    loDev = THIS.oDeveloper
    
    IF !loDev.Load(lnPK)
       MESSAGEBOX("Couldn't load this developer",48)
       RETURN .F. 
    ENDIF
    
    THISFORM.Refresh()
    
    THIS.ShowStatus()
    RETURN .T.

  8. Now implement each one of the methods that are called from the buttons:

    *** New
    THISFORM.oDeveloper.New()
    THISFORM.Refresh()

    *** Save
    THIS.ShowStatus("Saving...")
    IF !THISFORM.oDeveloper.Save()
       MESSAGEBOX("Unable to save the developer entry." + CHR(13) + CHR(13) + ;
                  THISFORM.oDeveloper.cErrormsg,48)
       THIS.ShowStatus()              
       RETURN              
    ENDIF   
    
    THIS.ShowStatus("Developer Entry Saved...") && Update the list by requerying
    

    *** Delete
    IF THISFORM.oDeveloper.Delete()
       WAIT WINDOW NOWAIT "Developer entry deleted..."
       THISFORM.LoadDeveloperList(THISFORM.oList.Value)
       WAIT CLEAR
    ELSE   
       MESSAGEBOX("Couldn't delete developer entry" + CHR(13) +;
                  THISFORM.oDeveloper.cErrorMsg,48)
    ENDIF
    

    Now point each of the buttons at these methods of the form: THISFORM.New(), THISFORM.Save(), THISFORM.Delete().

  9. Implement the Search Page

    Simply add the fields shown and name them as appropriate based on the following handler snippet you can attach to the Search button:
    loPage = THIS.Parent
    lcCompany = TRIM(loPage.txtCompany.Value)
    lcState = UPPER(TRIM(loPage.txtstate.Value))
    lcZIp = TRIM(loPage.txtZip.value)
    lcCountry = TRIM(loPage.txtCountry.value)
    
    THISFORM.oDeveloper.cSQLCursor = "TDevelopers"
    lnResult = THISFORM.oDeveloper.Developerlistquery(lcCompany,,,lcState,lcZip,"",lcCountry,0,0,0,"Company","Company,pk")
    IF THISFORM.oDeveloper.lError
       MESSAGEBOX("Query failed" +CHR(13) + CHR(13) + ;
                  THISFORM.oDeveloper.cErrorMsg)
       RETURN
    ENDIF
    
    IF lnResult < 1
       MESSAGEBOX("No matches found...",64)
       THISFORM.ShowStatus()
       RETURN
    ENDIF   
    
    THISFORM.oList.Requery()
    

    Again we're reusing the business object method DeveloperlistQuery() reusing existing functionality to reduce the amount of code we have to write. This code simply queries the data again and returns a new resultset which gets re-bound to the listbox. The Search All button on the other hand simply calls the LoadDeveloperList() method to refresh the listbox with all entries from the server.

That's pretty much it! Note that when you save validation occurs if you don't fill in the form completely or make the service description too short. All the rules of the business object work just as they did before except we are now pulling the data from the Web!

If you wanted to pull the data from your local SQL Server instead, change nDataMode to 2 and add a cSQLConnectString for the connection and off you'd go against SQL Server. Change the nDataMode to 0 and set the cDataPath appropriately and off you go against Fox data!

I hope this example has demonstrated the power and flexibilty you have with this simple business object.



  Last Updated: 1/13/2002 | © West Wind Technologies, 2008