Database Publishing on the Internet with Visual FoxPro
Revised session notes from my 1996 Visual
FoxPro DevCon session.
Download this document (HTML)
Download Word '97 Document
Download Powerpoint '97 slides
This white paper will walk you through some of the tools available to build Web based applications with Visual FoxPro. Focus will be on building server side applications by introducing some of the tools that can take advantage of Visual FoxPro data and code to build high performance backends.
This paper will cover the following areas:
- Overview of Web Technology with focus on building backend applications that connect to databases and Visual FoxPro in particular.
- Review the hardware and software required to build Web based applications.
- Using Microsoft Active Server (IIS 3.0) to access FoxPro data via ODBC.
- Integrating Visual FoxPro with Active Server via Visual FoxPro Automation Servers.
- Using FoxISAPI to build Visual FoxPro Automation servers that interface directly with Web links.
- Using Web Connection to simplify Web application development using a Web Application framework for Visual FoxPro.
Building applications that are integrated with Internet technology and can connect databases to Web sites will likely become an important aspect in your software development in the future, if it hasn't become so already. We're just at the beginning of the move toward 'Active' Web content and while we'll surely see improvements in the tools available down the line, applications built with Visual FoxPro can provide extraordinary power, versatility and speed using standard PC hardware to drive high powered Web sites today.
Internet Development is exploding
People are flocking to the Internet and the World Wide Web in particular by the millions. Because of the both the hype and the actual traffic on the 'Net, the Internet is an exploding market as businesses are trying to integrate the Internet into their existing business strategies.
There is tremendous demand for developers who can build the dynamic content necessary to build truly useful, distributed business applications that can run over the open Internet or the local Intranet.
Active, Database Applications are in high demand
Database applications are the key to building active Internet applications. Until recently static content via plain HTML pages has been the standard fare on the World Wide Web. However, for conveying lots of information the static HTML concept falls apart quickly and becomes a maintenance nightmare.
The true potential of the Web lies in giving users access to data that is always up to date, allows them to see only what they chose to look at by allowing the data to be filtered and queried to display only small, appropriately sized chunks at a time.
Visual FoxPro is ready for the Internet today
Visual FoxPro is more than ready for this challenge. I've been involved in building several high volume Web sites that use Visual FoxPro as the database backend. Not only did Visual FoxPro perform extremely well under heavy load, but it also allowed creation of applications in record time and with a budget that was a fraction of the nearest competitors bid.
Visual FoxPro provides exceptional database access speed that's unrivaled by ODBC based tools and even full blown SQL servers in many circumstances. In addition you can leverage your existing FoxPro skills and code base to build quality applications with full featured logic using the data centric and object oriented, FoxPro language. If designed properly, applications can also be scaled to handle just about any request load scaling to multiple processors or even multiple servers on separate machines across a network.
Why build Web Applications?
If youve read through the computer trade papers or even the mainstream computer press youve probably noticed that the Web is affecting just about every aspect of computing these days. Software developers and tool vendors alike are focusing on the Web as the next development platform. Clients are asking about Net technology and how they can take advantage of the distributed envrionment which the Web offers.
For better or worse, the Web is here to stay. There are a number of very important steps forward, but at the same time a few blems that take us a step back in the area of application development. In this section and the following one Id like to point out some of the strengths and weaknesses of Web based application development.
Distribute widely, administer centrally
The Internet could become the ultimate client/server platform. It provides:
- Easy access from any network connection
Web applications are accessible from any network connection using TCP/IP. As long as the network is not locked off intentionally with security measures, you can access the same application from anywhere within your company or even from the outside when you're on the road.
- Applications are maintained in one place
While the application can be run from any network connection the server is centrally located and maintained. The application can be updated in one place and all clients automatically see the update next time the application is run. The application also does not need to be installed on the client in any way - no making sure all the DLLs are available etc. All the thin client needs is a compatible Web browser.
- Clear separation of client and server
The Web provides a typical client/server implementation where the client has no direct access to the server's functionality but converses with the server via transactions. Since all processing happens on the server and only the result returns to the client limited network bandwidth.
Note: Visual FoxPro can act as a server in this type of Client/Server environment. The Web acts as the middleware that connects the client to the server.
Universal Client Interface
The Web Browser is quickly becoming one of the killer applications that come around and change the computing environment. The quick acceptance of this interface is driving changes in software design that is moving more and more technology towards a Web based interface.
- Cross Platform Clients
Cross platform issues have been short-changed by software developers for the longest time. Browsers are perhaps the first tool that bring the various operating systems and environments together and allow them to run the same applications.
- Develop on your platform of choice and run on any platform
Another big advantage of a server based Web application is that you can build your application under 32 bit Windows and expect people on Unix, Mac or Windows 3.x boxes to run them as is. Browsers make it possible to use whatever tool/platform you're comfortable with to build backend applications and still provide the cross platform capability. No extra development required.
In addition, the hyperlinked nature of the Web makes it possible to link together applications that are built with different environments a Windows application might call a Unix server to complete some of the support tasks or simply spread the load onto another server in another department.
- No hassle application updates
Applications can be updated on the server in a central location without having to touch the client machines.
Application Platform of the future
Web browsers have brought about drastic changes in the software development field. One look at the latest software offerings from all the major software houses shows this in evidence: Internet connectivity and output options for creating HTML are evident wherever you look. This integration will only become more prelevant with Microsoft and Netscape's plans to integrate the desktop environment more completely with the Browser in forthcoming versions of Windows.
Development tools of all types now have at least rudimentary tools to connect to the Internet. Microsoft has been previewing the next release of Internet Explorer which promises to integrate the Web browser directly into the operating system, where the desktop and integral file operations as well as access the network occur within a familiar browser interface. It's all geared towards integrating the Internet more transparently into the operating system. The hyperlinked nature of HTML (and the supporting ActiveX and Java technologies) makes it possible to transparently tie together the local machine, the interoffice workgroup or Intranet applications and the entire Internet. Welcome to the Network machine!
Limitations of Web Applications
There are many benefits to building an application that runs over the Web. But it's also extremely important to understand the limitations that you will face when building Web applications. They are not insurmountable, but they do require rethinking application development to some extent.
Web Development is definitely more involved than building a standalone application using a visual tools such as Visual FoxPro or Visual Basic. For one thing you are dealing with a larger number of entities rather than just a single environment: The Web server, a connector application of some sort and the backend application, HTML pages and code. The complexity of how many different pieces are involved varies between the various approaches to development. Typically, youll use a Browser to test your code, rather than simply running an application.
You need to have a basic understanding of how the components fit together in order to make all the pieces work together. The promise of component based software is starting to materialize with the Web, but as of now, its not necessarily easy to make the components play nice.
Interface limitations of HTML
If you believe the trade press, the Web is the nirvana of application development that will solve all your problems yeah, right! The tools that are available today are downright primitive when compared to full visual development tools and Web applications reflect this in rather simplistic interfaces that are used to present forms and interaction with the user.
Although typical HTML output can be very visual, there are various limitations in HTML that require re-thinking your typical database application user interface.
- No data access from client
The browser has no direct access to the data which is maintained and accessed by the server. Data access is transaction based and any updates based on data retrieved from the server typically requires the HTML document containing the form data to be reloaded. This is a very important point without direct data access any lookup including field validations require a trip back to the server and reloading the HTML with the updated data or error message from the server. This means that much of what goes into an 'event driven' database application interface goes out the window. Typical HTML interfaces are form based where validation occurs after the user has entered all the data. For true public Web applications there appears to be no change in sight for this situation and it's something you have to design for.
- Limited object model/scripting
The soon to be released versions of Internet Explorer and Netscape will provide more control over the interface and give improved access to all user interface object via scripting which will allow much better control of flow from pages, but without data connectivity, database applications still suffer from lack of a data link.
- Interface generated by the server
If data access is required the HTML page is always loaded with data loaded from the server. Each time the data is updated on the server needs to reload the browser page and repopulate the data on the form.
- No easy way to print output
All output generated is HTML and there's no graceful way to print it other than using the Browsers Print option, which also has the side effect of re-loading the current page or script. HTML does not map very well to output generated by the report generator. There are some third party workarounds such as using Adobe Acrobat to print output and then viewing the output in a reader or ActiveX control. But none are natively supported.
Mostly non-visual Development
For the most part Web based application development is non-visual. While you can use visual HTML editors like FrontPage, Visual InterDev or WebEdit to build the HTML pages you display on browsers, the actual application code you write is usually transaction based involving mostly straight database code (queries, validations, inserts etc) and either generating the HTML via code or loading HTML pages from disk to evaluate embedded logic.
Server based programming
Web application request handlers are basically server scripts, which are non-interactive and transaction based. Each link, or form request generates a hit on the server which in turn runs the request handler in response. Each request needs to be fully self contained and establish its own environment as HTTP is a stateless request that does not provide for maintaining your state. For example, a simple operation such as going to the next record requires that you pass a request to the server with a record ID of the current record so the request can figure out to go to the current record, then SKIP to the next record, create the full updated HTML page and send it back to the browser, where in a standalone application you'd do nothing more than a SKIP and THISFORM.Refresh().
Web programming usually involves:
- Establishing state from Server/Browser variables
- Validating user input on forms
- Running queries
- Generating HTML (or loading and outputting scripted HTML from disk)
How The Active Web works
Figure 1.1 shows how Microsoft's Active architecture binds the client and server sides together. When looking at the figure keep in mind the strict separation between the client and server sides. Although some of the components like ActiveX controls and ISAPI extensions are Microsoft specific, the frameworks by Netscape and others look surprisingly similar.
Figure 1.1 - Browser and Web Server relationship. Note the distinct line between the two.
The Browser provides the Active interface
The Web browser provides the interface to an application or page viewer. The browser provides the interactive, visual face of a Web application. The interface consists mostly of HTML text along with graphics and basic input forms that can be embedded inside of HTML documents. All data input is handled via HTML formatted pages that are interpreted and then displayed by the browser.
ActiveX Controls and Java Applets can be used to enhance Web pages with high power add-ins or operating system specific components. The controls can be manipulated using VBScript using a typical Property, Event, Method mechanism.
ActiveX and Java
Web Server provides data/application connectivity
On the other end of the connection sits the Web server. The Web servers main responsibility is, well, to serve content. Traditionally this content has been static HTML pages served from disk, but the Web servers role is expanding to provide the basic logic to interact with backend applications via the ISAPI interface.
The Web server is responsible for providing the database access and the connectivity to the actual processing application. The Web server itself knows nothing about applications and calls helper scripts (ISAPI extensions) to do the work for it.
ISAPI is the building block for server side extensions
Typically the server calls a ISAPI or Common Gateway Interface (CGI) extension script which is responsible for returning HTTP compliant output. The Internet Server API (ISAPI) is a highly efficient Windows based API that allows extensions to run in the Web server's address space, which make them very fast and resource friendly. ISAPI extensions can either be self-contained and create the required output on their own or act as a connector and call another application to perform the actual request processing for it.
Keep in mind that ISAPI and CGI both are interfaces only and do not comprise a specific language implementation. ISAPI can be implemented in any language that can create true Win32 DLLs. CGI can be implemented by any language that supports creation of EXE files and can read and write to and from Standard Input and Output.
Whats important to remember is that Visual FoxPro never runs as an ISAPI application directly it lacks the ability to create an ISAPI extension directly! Rather an ISAPI extension interacts with Visual FoxPro via some sort of messaging mechanism (OLE Automation, DDE, file based messaging etc.). If implemented correctly this process can be extremely fast and efficient. Both FoxISAPI and Web Connection use this approach to let you use Visual FoxPro to build application logic.
Figure 1.2 shows how ISAPI is the building block of most of the Web server extensions that Microsoft is creating. All the major tools provided for IIS by MS including the MS Internet Database Connector, and the Active Server framework are implemented via the ISAPI interface. FoxISAPI and Web Connection which will be discussed later on are also implemented using ISAPI as the connector interface and qualify as custom connector applications on the chart.
Figure 1.2 - Microsoft's Internet Server API is the building block for Web server extensions
What you need to get started
Here's a short list of hardware and software required to run a Web application:
Fast Pentium box (133Mhz/32-64megs)
While you can get by with smaller machines I would recommend this as a good baseline installation. I've run several Web sites on hardware as low as a 486-66 with 32 megs running NT 3.51 with decent results, but if your site gets more than a few thousand backend hits day the above is a good minimum.
For high volume sites multiprocessor boxes are the preferred way to go. Dual processors allow the Web server and Visual FoxPro to not compete for CPU cycles as much. Also, additional memory (128megs or more) can provide dramatic improvements in database (especially Read operations) and Web server performance as NT uses the memory for disk caching.
Windows NT Server (recommended)
Windows NT Server is an excellent platform for running a Web server and for acting as an application server. Many Web servers will run on Windows 95 and NT Workstation, but Windows 95 Web services are noticeably slower and NT Workstation has some serious licensing limitations that make it unsuitable for public Web server use, although it provides the same performance as NT Server.
Included with NT Server 4.0:
- TCP/IP Network Protocol
TCP/IP must be loaded on the network or local machine in order to be able to take Web requests. Note: You don't need to have a network installed in order to test Web applications locally. You can tie TCP/IP to your network adapter, or if you don't have one to your Dialup Networking service.
- Web Server (MS IIS)
Naturally, you'll need a Web server. NT 4.0 ships with MS Internet Information Server 3.0, which is fast and powerful and hooked into Microsoft's Internet strategy. Other good servers available include Commerce Builder by the Internet Factory, Website by O'Reily and Purveyor by Process Software. All of these servers support ISAPI which will be the focus of our discussion here.
Note: The following examples are for IIS. The Internet Database Connector and the Active Server Page are available only for IIS, while FoxISAPI works on all ISAPI based Web servers.
You'll also need a 'connector' application that handles tying your FoxPro data the Web server. A connector application is a script tool that provides services for accessing another application or a scripting engine via an ISAPI DLL.
Active Server Pages uses an ISAPI extension to provide the interpreting of .ASP files in a specialty DLL. FoxISAPI and Web Connection both use an explicit connector DLL that is called directly of an HTML link.
In order to test your application you need a Web browser. I'll be using Internet Explorer 3.0 here, but any late browser should do. Try and get one of the latest browsers from Microsoft or Netscape as they make up over 90% of the browser market and provide the most advanced features that you will encounter when cruising the Web.
Basic HTML skills
You knew this was coming, right? Yes, in order to build Web applications you need to have at least a passing acquaintance with HTML. While it's possible to build HTML graphically using HTML editors like FrontPage, PageMill, NetObject etc, it's often required to generate HTML that gets inserted into existing documents - in order to do that you have to know a little about the various tags associated with text formatting. It's easy and best picked up by looking at the source of existing HTML pages.
Everything can run on one box!
For development purposes you can set up a single machine to serve as your Web server and development platform. You don't need to be on network as you can access the Web server via its local IP address (127.0.0.1 or localhost).
Active Server Pages
Figure 1.3 Active Server is composed of a number of objects that are managed by the ASP.DLL ISAPI extension.
Tight integration with IIS
ASP is actually a part of Internet Information server and installs when you upgrade IIS 2.0 to 3.0. The integration into the server itself is very smooth by implementing an ISAPI extension with a script map (a mechanism that maps the .ASP extension to the ASP.DLL) to provide transparent support for interpreting any page with .ASP extension. This mechanism provides the illusion to the developer of simply editing an HTML page and enhancing it with dynamic script code.
Keep in mind the difference between client and server side scripting. Active Server is server side scripting which means the .ASP is evaluated by the server before it is sent back to the browser. This means that even though you're using VBScript any browser can see the resulting evaluated HTML that gets sent back to the browser (assuming you generated compliant HTML). Client side scripting occurs on the Browser and is browser specific. You can mix Client and Server side scripting by using some special tags in the HTML <SCRIPT> tag.
Great care has been taken to provide an efficient environment for Active Server. The ISAPI extension handles multi-threading of requests and connection pooling for ODBC connections to provide fast access to dynamic content.
Object Based Architecture
The entire Active Server framework is based on components that interact to provide access to the entire environment to the developer. Figure 1.3 shows the base components that ship with Active Server. The various components provide access to the vital pieces that are required to build sophisticated Web applications.
Heres a list of the base framework components and what they do:
- Request Object
The Request object provides information about the current Web request. It returns information about form variables captured on an HTML form, information about the server and its status, and access to the HTTP header to retrieve information such as HTTP Cookies and Authentication information. Some of the collections available are Form, QueryString, Cookies and ServerVariables.
- Response Object
Where the Request object provides information about the input from the Web server, the Response object is responsible for creating output for the Web server. ASP pages interpret HTML as usual, but in addition the Reponse object allows generating output under code control. The Response.Write method allows writing output directly. You can also manipulate the HTTP header, set the HTTP Cookie collection and write entries to the IIS log.
The following retrieves a few form variables from an HTML form submission and echos whether the user is a new visitor or repeat:<% lcName=Request("Name") 'Retrieve an HTML form variable lcAddress=Request("Address") 'Reading Cookies if Request.Cookies("wwVisitor") = "" then Response.Cookies("wwVisitor") = now ' and write Response.Write("New Visitor") else Response.Write("Repeat Visitor") end if %>
- Session/Application Objects
This pair of objects provides a mechanism for keeping state between requests. Web applications are typically stateless, which means that each request needs to be fully self contained. The Session and Application objects allow keeping state between requests. Active Server creates these two predefined objects and allows adding property values to them.
The Session object manages a user session and has persistence for the duration of this users visit on the site. When a user hits your site a new session object is created and the Session.OnStart method in GLOBAL.ASA is fired. From then on the session object is accessible for each request. You can add variables to the session object and reference these variables between requests. The session object is implemented via HTTP Cookies and will not work with non-Cookie capable browsers (although MS had promised support for non-cookie browsers)!
The Application object is similar to the Session object except that the Application object is persistent for the lifetime of the application a specific Web server virtual root directory. As with the Session object the Application object has OnStart and OnEnd methods and the ability to attach properties to the Application object. Since the Application is a global and accessible from simultaneous Web requests that are multithreaded access to these global properties needs to be tightly controlled by using the Lock/Unlock methods to prevent simultaneous overwriting of values by two sessions.
The following creates a new database connection or reuses an existing one based on whether a connection object was previously attached to the Session object by this user:<% '*** Open the Connection - create a new object or reuse existing IF IsObject(Session("goConn")) THEN SET Conn=Session("goConn") ELSE Set Conn = Server.CreateObject("ADODB.Connection") Set Session("goConn")=Conn END IF %>
Database Connectivity with Active Data Object (ADO)
Active Server provides built-in database connectivity via Active Data Objects or ADO. This ODBC based server component (yes, its a COM based engine much like VBs DAO or RDO). It provides the following features:
- Lightweight ODBC Connector
Although ADO is an OLE based component that gets created from within an Active Server page, it is a very lightweight data interface that provides excellent performance by implementing only a thin layer ontop of the native ODBC interface. ADO shares a similar interface with VBs Remote Data Objects (RDO), but provides an even leaner interface and much better performance. Make no mistake about it ADO is fast and provides good performance to FoxPro data with the VFP ODBC driver.
- Implements OLE DB (ODBC 3.0)
ADO works with ODBC 3.0 and the new OLE DB interface specification that promises to provide access to any kind of data including non-structured, non-database data.
- Based on Visual Basics Remote Data Object
ADO shares a similar interface with VBs Remote Data Objects (RDO), but provides an even leaner interface and much better performance.
- Its fast especially when tied to a persistent connection object!
Make no mistake about it even though ADO uses ODBC it is fast and provides good performance to FoxPro data with the VFP ODBC driver.
- Implemented as Automation Object
An ADO connection is created by using CreateObject and instantiating a COM based Automation object. This means that you can use ADO from within Visual FoxPro either for testing or for actual data access data through ADO.
Heres an example of using ADO for logging custom log information logged into a Visual FoxPro database with one link and then retrieving the log with another:'*** ODBCLOG.ASP '*** Logging page - this page logs visitor info into ' a VFP table via ADO <% '*** If visitor doesn't have a cookie log the hit as a new visitor if Request.Cookies("wwVisitor") = "" then '*** First time hit - create a cookie for the user Response.Cookies("wwVisitor")=now '*** Open the connection to the RASLOG ODBC database Set Conn = Server.CreateObject("ADODB.Connection") Conn.Open("dsn=RasLog") sql="INSERT INTO wwPageLog (Page, TimeStamp, Browser, Referer, IP, Other)" sql=sql + "VALUES ('Default.asp'," sql=sql + "datetime()," sql=sql + "'" & Request.ServerVariables("HTTP_USER_AGENT") & "'," sql=sql + "'" & Request.ServerVariables("HTTP_REFERER") & "'," sql=sql + "'" & Request.ServerVariables("REMOTE_HOST") & "'," sql=sql + "'" & Request.Cookies("wwVisitor") & "')" ' Response.Write(sql) ' debug Conn.Execute(sql) Conn.Close SET Conn = nothing end if %> <html> <head> <title>Logging</title> </head> <body bgcolor="#FFFFFF"> <h1>This could be your custom logged homepage</h1> <hr> This request has been logged into a VFP database using the ADO ODBC connector. To view the list of recent requests click on one of the links below: <UL> <li><a href="ShowOdbcLog.asp">Show hits on this page...</a> </UL> </body> </html> '*** ShowODBCLog.ASP - Display the results from the log <% '*** This code always loads the table with the log '*** Open the Connection Set Conn = Server.CreateObject("ADODB.Connection") Conn.Open("dsn=RasLog") '*** Retrieve the date from the Input form lcDate=Request("LogDate") '*** Handle ALL selection IF UCASE(lcDate)="ALL" Then lcWhere="" ELSE '*** If Emtpy default to today's date IF lcDate="" THEN '*** On Empty date show all entries lcDate=cDate(Date) lcWhere= "WHERE ttod(timestamp) = ctod('" & lcDate & "')" ELSE '*** Otherwise filter to today's date lcWhere="WHERE ttod(timestamp) = ctod('" & Request("LogDate") & "')" END IF END IF '*** Build the SQL Statement And Execute into a RecordSet SQL = "SELECT TimeStamp, Referer, Browser, IP FROM wwPageLog " & _ lcWhere & " GROUP BY 1 ORDER BY 1 DESCENDING" Set rs = Conn.Execute(sql) %> <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> <html> <head> <title>Show Hits to Default Page</title> </head> <body bgcolor="#FFFFFF"> <h2>Show Hits to Default.asp</h2> <form action="ShowODBCLog.asp" method="POST"> <p><strong>Date:</strong> <input type="text" size="8" name="LogDate" value="<% =lcDate%>" > <p> <input type="submit" name="btnSubmit" value="Display Log"> </p> </form> <table border="1" cellpadding="3" width="100%" bgcolor="#EEEEEE" border="3"> <tr> <td align="center" bgcolor="#FFFDCA"><strong>Time</strong></td> <td align="center" bgcolor="#FFFDCA"><strong>Referer</strong></td> <td align="center" bgcolor="#FFFDCA"><strong>Browser</strong></td> <td align="center" bgcolor="#FFFDCA"><strong>IP Address</strong></td> </tr> <% Do While Not RS.EOF %> <tr> <td><% =rs("Timestamp")%></td> <td><a href="<% =rs("Referer")%>"><% =rs("Referer")%></a></td> <td><% =rs("Browser")%></td> <td><% =rs("IP")%></td> </tr> <% RS.MoveNext Loop '*** Now Calc the total sql="SELECT COUNT(*) as TotalHits FROM wwPageLog " & lcWhere SET rs=Conn.Execute(Sql) %> <tr> <td align="center" colspan="3" bgcolor="#FFFDCA" align="RIGHT"><b>Total number of hits:</b></td> <td align="center" bgcolor="#FFFDCA"><b><% =rs("TotalHits")%></b></td> </tr> <% RS.Close Conn.Close %> </table> </body> </html>
External Object Creation with the Server Object
The Server object is used for server configuration and for the creation of Automation objects to extend Active Server and provide interfaces to system services.
One of the most exciting aspects of Active Server is it's ability to instantiate OLE Automation servers directly from within the HTML scripted code. You can use the Server.CreateObject() method to create an object instance to any Automation server available on the server machine.
Figure 1.4 shows how external objects are managed by the server. This is a crucial piece for interfacing Visual FoxPro code into Active Server by allowing the Server object to create and invoke Visual FoxPro Automation servers directly from within an ASP script.
Figure 1.4 - The ActiveX Server framework allows creation of Automation objects from within VBScript. Once created the object can be accessed using Automation server methods and properties the results of which can be displayed in the HTML script page.
The following Active Server page calls a VFP Automation server that implements a counter variable thats increased on each hit. The IncCounter() method uses VFP code to access and write the registry with the updated counter value. The CustList() method accesses a Visual FoxPro table and generates an HTML table that shows the contents of the table which is returned to the ASP page as a text string result.<html> <head> <title>Active Server Automation</title> </head> <body bgcolor="#FFFFFF"> <h2>Visual FoxPro Automation Server called from an Active Server Page</h2> <hr> <p>This page has been hit:<strong> <% SET oServer = Server.CreateObject("wwWebtools.AspTools") Response.Write(oServer.IncCounter("ASPDemoCounter")) lcCompany=Request("txtCompany") %> </strong>times.</p> <p>The following table was generated by Visual FoxPro: </p> <form method="POST"> <p>Enter a Name: <input type="text" size="24" name="txtCompany" value="<% =lcCompany%>"> <input type="submit" name="btnSubmit" value="Filter List"></p> </form> <p align="center"><% =oServer.CustList( (lcCompany) )%></p> </body> </html>
This code is very straight forward. However, in order to really optimize access to Automation servers its crucial to persist servers across requests. Without this loadtime for the server is excessive and makes use of Automation servers inappropriate. To persist a server you can use the ASP Session object.
Replace the CreateObject code above with:IF IsObject(Session("soServer")) THEN SET oServer = Session("oServer") ELSE SET oServer = Server.CreateObject("wwWebtools.AspTools") SET Session("oServer")= oServer END IF
The server reference is now active for the duration of the users session and access speed to the server is drastically improved.
Automation Server limitations
Be aware of some serious implications of Automation servers that are used with ASP. Active Server wants to work with DLL based Automation objects, but Visual FoxPro will allow you to only use one DLL based server per Web server as VFP only supports a single copy of the runtime in memory of any single process (in this case the Web server). The single copy can only serialize calls to itself, so if you run a long query any other hits on that same server are blocked.
By default ASP doesn't even run with EXE servers you need to create a registry value and set it to DWORD with a value of 1. Create it as:
You run can multiple EXE servers at the same time, but be aware that any EXE based Automation server calls will block all other EXE Automation calls at the same time! This has some serious implications on performance as everything is bottled down to a single thread while your server executes. Hence, it's not recommended that you use Automation servers for any operations that are lengthy.
True multi-threaded operation for Automation servers is possible only with Free Threaded servers or those that have a threading model of 'Both'. None of the current high level development tools provides this kind of interface at the moment and you're required to write scalable components directly against the native COM interfaces using low level languages such as C++ or Delphi.
Active Server Summary
- Tight Integration with IIS
Because Active Server is tightly integrated into IIS it provides excellent performance thats tuned to the Web server and its multithreaded environment. Thread management and ODBC connection pooling is managed for you to an extent. If you stick with VBScript and ODBC only (no Automation servers) ASP is very efficient and fast especially against SQL server backends.
- No hassle configuration
Since the code and HTML both reside in the same document, setup for building an Active page is minimal. If data acess is required only a ODBC connection needs to be configured and youre off and running.
- Very easy for simple active content
Scripting in HTML is straightforward and VBScript is reasonably powerful to do the basics language handling. ADO provides data quick access with a minimal object model that can be picked up fairly quickly.
- Extensible with Automation objects
- Think of Active Server as the HTML driving the application
- Code Management
Building complex logic inside of HTML pages can be tedious even when using a tool like Visual InterDev. Trying to dig through a long HTML document and finding code inside of it is no fun. Structured coding also goes out the window in many cases as code follows the HTML output more than it necessarily follows the program logic.
- Limited Development Environment
Although Visual InterDev provides a rich shell and a number of very useful Wizards for building Active Server Pages, development support for coding and debugging is very primitive. Theres no way to step through code and see variable values in the development environment for example. Error handling support in VBScript is a far cry from sophisticated where errors cannot be delegated but must be fully anticipated and managed at the time of error. The alternative is an ugly VBScript or ODBC runtime error message.
- Automation Server Scalability
ASPs ability to call external Automation servers is extremely powerful. Unfortunately, when calling Visual FoxPro servers calls to any single VFP server are serialized since VFP can only load one instance of a particular server at a time and process that request on its single application thread. True multi-threading of Automation servers is supported currently only from pure COM based servers that are built in low level languages like C++ or Delphi.
What this means is that if your Web server gets busy enough to require more than one instance of your Automation server you will have no way to scale up. You can rearrange your servers to break up the request load on each server by splitting into smaller servers, but even that might not be sufficient.
- Scripting Language Limitations
For building full featured applications the VBScript language is fairly limited. VBScript is a subset of VB for Applications and misses a number of that VB subsets features. Theres no access to the WinAPI or even to VBs file handling functions for many features that are commonly required in any programming language youll be required to call Automation objects to help out. With the overhead involved this might not always be a good option.
- ODBC Data Management
Data access via ADO to VFP tables is straightforward and speed is acceptable. However, if you need to handle maintainence tasks to your data that require Exclusive database access its next to impossible to implement these via a Web based interface. Since ODBC keeps tables open and multiple requests might hit your pages simultaneously its unlikely you can ever get an Exclusive lock on a table to say clear a file (log files can get big) or even do things as simple as APPENing data from an external file. True Exclusive access to VFP data requires shutting down the Web server and running a VFP program against the data.
Visual FoxPro's new capability to create Automation servers has brought about another slick option for implementing Visual FoxPro based Web applications. What if you could use an Automation server to respond to a request placed from a Web page to handle the data processing and HTML page generation? With a cool called FoxISAPI that's provided by Microsoft with Visual FoxPro you can do just that.
Figure 1.5 - FoxISAPI uses an ISAPI DLL to create an instance of an Automation server and call the specified method in the server.
Connect VFP Automation servers to HTML HREF or form links
FoxISAPI is used by calling the FoxISAPI.dll from an HTML link or form. A typical HTML link looks like this:
ISAPI DLL instantiates persistent Automation Automation object
FoxISAPI works by calling an ISAPI script from an HTML HREF or form link. The FoxISAPI.dll creates an object reference to the object passed as part of the URL and calls the specified method. The object is persistent so repeated access to the object is very quick as the object is never reloaded unless explicitly unloaded (more on that below).
In the example above FoxISAPI parses out the ClassId and method call to instantiate the Tserver.Tclass Automation server (equivalent to doing CREATEOBJ("Tserver.Tclass") in VFP) and then goes ahead and calls the Tmethod() method (equivalent to lcResult = oServer.Tmethod(cParm1,cParm2,nParm3)). FoxISAPI expects your method to return an HTTP compliant result string - in most cases this result will be an HTML document, but could also be an HTTP request for Authentication or Redirection.
Passes form variables as parameter and server variables in an INI file
When FoxISAPI calls your Automation server it passes along 3 parameters to each request method that is called. Your server method that respond directly to request must support these three parameters:
The first parameter contains any 'parameters' passed on the Query string as described in the example above or any form variables retrieved from an HTML form via an HTTP GET or POST.
The format of the parameter string contains a key/value pair for each of the form variables or 'parameters' passed. The keys in the above example are UID and Name the values are 1111 and Rick. These key value pairs are what is known as URL encoded, which means they follow a specific protocol of encoding to allow passing special characters on the URL or via form variables. It works something like this:
- Key value pairs are separated by &.
- Spaces are converted to +.
- All 'extended' characters are converted to Hex escape codes. The escape code uses a percent sign plus a hex number to store the ASCII code of the characters. For example a carriage return (ASC(13)) is encoded as %0D.
- The EMPLOYEE example in your \VFP\SAMPLES\SERVERS\FOXISAPI directory includes a decoding algorithm as does the starter FoxISAPI class provided on the DevCon CD (FoxISAPI::DecodeURL).
This parameter contains the path to an INI file that contains all the server, browser and system variables. You can retrieve these with calls to the GetProfileString API call (or use the CDs FoxISAPI::GetCGIVar(cVarname,cSection)).
This parameter determines whether the reference to the Automation server will be kept or released. By default it's a good idea to keep the reference to keep a connection in order to minimize load time of the server. The parameter is passed in by reference so changing it in your code will effectively
0 - Keep Server Reference (default)
1 - Release Server Reference
Must return HTTP compliant output
Once your code gets control you can use VFP as you see fit to run queries or run any other kind of transaction or logic operation using FoxPro code. The end result of each exit point of your method must be a HTTP compliant string.
In most cases the output will be an HTML document, but you have to be sure to add an HTTP header to the top of it. Output should look like this:HTTP/1.0 200 OK Content-type: text/html <HTML> <H1>Hello World</H2> </HTML>
The HTTP header and Content-type are important since not all browsers will support headerless results. Note each line of the header must be separated by a CHR(13)+CHR(10) and the final header line must be followed by a blank line containing only the CHR(13)+CHR(10).
HTTP headers for authentication, redirection, Cookies etc.
While you will almost always return an HTML document it's possible to generate standard HTTP header responses as well. For example, if you wanted to cause Authentication to occur you might pass:HTTP/1.0 401 Not Authorized WWW-Authenticate: basic realm=west-wind.com <HTML> Get out and stay out!!! </HTML>
This would cause an authentication box to be thrown up by the browser. You can then check the password entered as passed back in a the Authenticated User CGI variable to determine whether to allow the user in (actually NT will first fail the user if the user is not valid as per the User Manager).
A simple FoxISAPI Server code example
The following code snippet shows the simplest Automation server you can build and run with FoxISAPI. While this is not a very functional example it does show the basic elements required when building methods for responding to FoxISAPI calls.
The script would be called with the following URL HREF link:HREF="/scripts/foxisapi.dll/TDevCon.TFoxIsapi.Helloworld?"
You'd create an Automation server named TDevCon with the following code:#DEFINE CR CHR(13)+CHR(10) DEFINE CLASS TFoxISAPI AS Custom OLEPUBLIC FUNCTION Helloworld LPARAMETER lcFormVars, lcIniFile, lnReleaseFlag LOCAL lcOutput *** HTTP header - REQUIRED on each request! *** System Defines lcOutput="HTTP/1.0 200 OK"+CR+; "Content-type: text/html"+CR+CR lcOutput=lcOutput+; "<HTML><BODY>"+CR+; "<H1>Hello World from Visual FoxPro</H1><HR>"+CR+; "This page was generated by Visual FoxPro...<HR>"+CR+; "</HTML></BODY>" RETURN lcOutput ENDDEFINE
Note that the HTTP header is created as part of the output and that the output, header and HTML document both, are returned by the function. FoxISAPI takes this output and sends it - as is - back to the Web browser.
Set up for Automation Servers called by Web services
Setting up FoxISAPI is the most tricky part of working with this tool. Especially when running under Windows NT 4.0 special steps have to be taken to make sure Automation servers can be properly created by the Web server process which owns the FoxISAPI process that calls your Automation servers. BTW, the same rules apply to Automation servers called by Denali!
Automation Server must be registered
The first step is to make sure your Automation server is registered on the Web server machine. If you build your project on the same box then the server is already registered for you when VFP built the project.
If you're only copying the Automation server to this machine make sure you run:
- EXE Server
- DLL Server
\winnt\system32\regsvr32.exe Myserver.dll to register the server in the Web server's registry.
Copy FoxISAPI.dll into script dir
- Default server script dir is c:\inetsvr\wwwroot\scripts/scripts typically its mapped just off the Web root:
- If you create a new directory you must map it and set Web Server Execute rights to allow executing the ISAPI dll.
Run DCOMCnfg on NT 4.0
On Windows NT 4.0 DCOM configuration is necessary for all EXE servers launched by the Web server process. DLL servers also need to have at least the default rights set to include the Web server user account with launch permissions.
Add IUSR_ account to default rights
The IUSER_Machine account is the user account used by IIS while running a Web request. This account needs to be added to provide default launch and access rights for all OLE servers:
Go to Default Security
Add IUSR_ to Default Access Permissions
Set user to Interactive User on the specific server
EXE require that the server is defined for interactive use. To set this:
- Go to the Applications Tab
- Select the Class (TFoxIsapi in the example)
- Click Properties
- Go to the Identity tab
- Click The Interactive User
- Click Apply
Re-run whenever server is rebuilt
Repeat steps 4 - 10 whenever rebuilding the server on the local machine, which rebuilds the Automation Ids and blows away the DCOM settings.
FoxISAPI Server Instancing
As with all Automation Servers instancing can have a big effect on how your application performs. For Web applications instancing is even more important a speed is crucial and multiple simultaneous requests need to be processed.
- Very fast
- Requests are queued 1 request at a time
- Only 1 VFP server can be InProcess
Automation Servers share the VFP runtime DLL and only one instance of this DLL is allowed at any one time in the same process.
- DLLs cannot be unloaded except by shutting the Web server down.
- DLL servers cannot be remoted via DCOM or Remote Automation
MultiUse (Out of Process)
- Slightly slower
- Requests are queued
- Multiple different servers
One renamed FoxISAPI.DLL for each Automation Server would allow requests to be processed simultaneously, but only for different Automation Servers.
SingleUse (Out of Process) (recommended)
- Speed same as MultiUse
- Requests are queued
- Multiple Different Servers
- Ability to create multiple instances of same server with built in Pool Manager.
Multiple instances via Pool Manager
Note: You must download the latest version of FoxISAPI.DLL from the MS Web site. VFP 5.0a did not ship with the final build of this tool.
With multiple instances of the same server, requests are served to all server instances as needed. If the first instance is busy the second instance will take the request. If all servers are busy and the pool of servers is exhausted the request is queued. In order to use the internal Pool Manager the server must be single use EXE!
The pool manager built into FoxISAPI's DLL can load multiple instances of your Automation server and access another instance whenever one is busy.
Multiple servers are configured via the FoxISAPI.INI startup config file:[foxisapi] busytimeout = 5 releasetimeout=13 statusurl = Status reseturl= Reset SingleModeModeUrl = SingleMode MultiModeUrl = MultiMode[wwFoxISAPI.TFoxISAPI] wwFoxISAPI.TFoxISAPI=2 R_FoxIsapi.T_R_Foxisapi=1[foxis.employee] foxisr2.employee=2 foxisr1.employee=1 foxis.employee = 2
The first keys in the [FOXISAPI] section determine how you can manage servers. The various URL keys allow you to customize the commands used on the URL to run that command:
The Status URL displays a list of all servers that are currently loaded. Reset releases all servers. SingleMode releases all servers but the first so you can run maintainenence operations that require EXCLUSIVE access to the data.
The actual Automation servers are configured with a separate section in FoxISAPI.INI. The section serves as a map to translate your ClassID passed on the URL to translate to the actual ClassIDs that you want to call. Since you can call both the same server or a local and remote server. Since locals and remote must have different class ids this mechanism allows you transparently to load the remote with the same URL as the local.
In the [foxis.employee] example above FoxISAPI would first load the FoxISr2 server when starting up. Once this server gets busy it would load another instance since the key value is 2. When both of these are busy foxisr1 would get loaded. Finally, a local copy gets started and two instances of that can be running before requests start queuing.
Typically, you'd want to load the local servers first, but it depends on your server load.
Configuring Remote Servers
Be careful with Remote servers. While they run reasonably fast there are a number of important issues to deal with.
Essentially, in order to build remote servers you need to make sure you build your server with some conditional logic for each location that it'll run in in order to properly point at the data and the Web Server for retrieving HTML templates.
The following issues have to be dealt with:
- File pathing
In my experience I've had to compile in a set of #DEFINEs for each location that establishes the startup path (Automation servers have no default start path), the data path and the HTML page path.
- Getting the Server Variables INI file
The server variables files goes to the Web server, but the Web server returns c:\TEMP\FOX01.INI for example. You'll need to translate that path to the actual path from your remote machine to get at it.
- Security Configuration
If you thought configuration for local Automation Servers under Automation was a drag think again! In order to run a remote server from the Web service you need to do some more configuration.
(The following provided from the Web Connection manual):
- In this setup the Web server is the Client (Web Connection DLL manages multiple client instances of your server) and the remote machine is the Server.
- If you want to mix local and remote servers you need to create separate EXE files with separate OLE ClassIds for each remote server. This is so DCOM can properly determine which machine to run the server on based on the entries in the registry from the single machine that wc.dll runs on. For example, you might have a local server called wcDemoOle.wcDemoServer. You can then create wcRemote1.wcRemoteServer1, wcRemote2.wcRemoteServer2 etc. In VFP 5 the server name corresponds to the EXE/DLL name and the class corresponds to the OLEPUBLIC class that your code exposes (your wwOLEServer subclass in this case).It's important that the Class names are different or else you will run into conflicts in the DCOMCNFG utility discussed above!
- Build the server and make sure it's registered on the remote machine. It's best to test the server locally on the remote before attempting to run it over DCOM. Make sure you apply the DCOMCNFG settings described in the previous section. To see the server come up locally try code like the following:
- Copy the .VBR files created when you build your Automation server to a directory on your Web Server for each Automation server you want to remote. The remote EXE file is not needed on the Web Server.
- Run CliReg32 (in your \VFP directory) and pass it the name of each of the VBR files. Choose DCOM for the transport and set the appropriate IP address for the remote server! Important: the server must be registered on the remote or load will always fail.
- At this point you should test the server from the Web server machine. Fire up VFP and see if you can instantiate the remote object on the remote machine. Use the CREATEOBJECT code a few steps back.If the server comes up on the remote box your server is installed correctly for operation under DCOM.
- On the Remote machine start DCOMCNFG and add the IUSR_ account of the Web server to the Default Access and Launch permissions on the Default Security tab. If you don't use a Domain Server you'll need to explicitly create new account/password and match it to the Web Server's account and password. With IIS the IUSR_ password is a random value so you'll have to change it both in User Manager and in the IIS configuration utility. If you don't want to hassle with the IUSR_ account you can also use the EveryBody group to allow access for all, but keep in mind that this is a potential security risk.
- You should now be ready to load the server through Web Connection. Add the remote servers to wc.ini:
This setting will load two local servers and one remote server.
- If all is well, the server will come up on the remote box as expected. Note that load time for remote server is significantly slower than local servers as is the time it takes to release the server. Operation of method calls however, is swift. If the server fails to load, you'll see a COM error message returned.
- For debugging this setup I suggest you log on to your Web Server box as your IUSR_ account then fire up VFP to try and create the remote object. This allows you to interactively change the DCOM settings plus get the COM error messages properly returned to you by VFP. It's much easier to handle and respond to the errors in VPFs interactive environment than trying to handle the Web server and its error messages.
Starter FoxISAPI class provided on the CD
The DevCON CD contains a starter FoxISAPI class that contains a few useful methods for handling typical tasks when handling FoxISAPI requests. This class is by no means extensive, nor does it provide all the features you need, but it's a starting point for writing your own library. For a more complete implementation framework of class you might want to check out Web Connection's OLE connector library.
Following is a list of methods and properties available in the FoxISAPI class:cOutput Temporary holding property that contains all text output to be returned from the request Method Send/SendLn() Send text to output StandardPage() Generates a full HTML page ContentTypeHeader() Adds HTTP header StartRequest() Called to set up a request. Decodes input vars and clears the cOutput property. GetFormVar() Retrieves a form variable passed in with the first parameter. GetCGIVar() Retrieves a server/browser Variable from the INI file. ReleaseServer() Standard method that releases OLE server.
Here's another complete, simple example server that uses most of the above methods (note this method should be in the same class as the example described above the class header is provided here for completeness of the sample only):DEFINE CLASS TFoxISAPI AS FoxISAPI OLEPUBLIC FUNCTION TestMethod LPARAMETER lcFormVars, lcIniFile, lnReleaseFlag LOCAL lcOutput *** Decode the Form Vars and assign INI file to class property THIS.StartRequest(lcFormVars,lcIniFile) *** Don't Release the server * lnReleaseFlag=0 && 0 - Don't release (default) 1 - Release *** Must always add a content Type Header THIS.HTMLContentTypeHeader() *** Grab HTML Form Variables lcUserId=THIS.GetFormVar("UserId") lcName=THIS.GetFormVar("UserName") *** Start HTML Generation THIS.SendLn("<HTML><HEAD><TITLE>Hello from FoxISAPI</TITLE><HEAD><BODY>") THIS.SendLn("<H1><FONT=Arial COLOR=#800000>Hello World from Visual FoxPro</FONT></H1>") *THIS.SendLn("This page was generated by Visual FoxPro using FOXISAPI. ") *THIS.SendLn("The current time is: "+time()+"<p>") THIS.SendLn("<b>Encoded Form/URL variables:</b> "+lcFormVars+"<BR>") THIS.SendLn("<b>Decoded UserId:</b> "+ THIS.GetFormVar("UserId")+"<br>") THIS.SendLn("<b>Decoded UserName:</b> " +THIS.GetFormVar("UserName")+"<P>") *** Show the content of the FOXISAPI INI server/browser vars IF !EMPTY(lcIniFile) AND FILE(lcIniFile) CREATE CURSOR TMemo (TFile M ) APPEND BLANK APPEND MEMO TFile from (lcIniFile) THIS.SendLn("Here's the content of: <i>"+lcIniFile+; "</i>. You can retrieve any of these with <i>THIS.GetCGIVar(cVarname,cSection)</i>:<p>") THIS.SendLn([THIS.GetCGIVar("HTTP_USER_AGENT","ALL_HTTP"): ]+; THIS.GetCGIVar("HTTP_USER_AGENT","ALL_HTTP")) THIS.Send("<PRE>") THIS.SendLn(Tmemo.Tfile) THIS.SendLn("</PRE>") USE in TMemo ENDIF THIS.SendLn("<HR></HTML></BODY>") RETURN THIS.cOutput ENDDEFINE
- Full support for Visual FoxPro
You can take advantage of your existing Visual FoxPro skills to build Web logic in record time. Visual FoxPros data centric and object oriented language is uniquely geared toward building data centric applications that are typical of the Webs transaction based processing of database data.
- Real Development Environment
By using Visual FoxPro you have access to the full development environment using the editor debugger and all of your existing Visual FoxPro tools to build and generate code quickly.
- Excellent performance
The Visual FoxPro data engine is still the fastest way to access data by a huge margin. Unless the amount data in question is gigantic VFP will outperform even SQL servers by a wide margin in most cases.
- Difficult First Time Configuration
Setup for Automation servers is a little tricky with the requirement to set permission on the Automation objects and making sure that data and HTML paths are set up correctly. Since VFP handles the underlying logic it needs to know where all the various pieces are located explicitly via hardcoded paths (including a startup directory).
- No Web specific code support
FoxISAPI provides little in the way of HTML generation or request parsing routines. The sample applet and this FoxISAPI class presented here provide the basics, but its limited.
- No Interactive Debugging support
FoxISAPI does not support interactive debugging of Web requests. You can simulate Web requests by creating requests with the appropriate FormVars passed via string and creating dummy INI files with server variables, but you cant debug a live server.
- Doesnt run on non-ISAPI servers or Windows 95
FoxISAPI requires an ISAPI compliant Web server. Currently FoxISAPI does not run reliably on Windows 95. This might change with the release of DCOM for Windows 95.
West Wind Web Connection
FoxISAPI provides basic Web connectivity features that are relatively easy to build using Visual FoxPro as a back end server. Web Connection expands on the basic functionality of FoxISAPI by providing an entire application framework and set of tools that make it easy to build Web based backend applications in Visual FoxPro.
Extensive Visual FoxPro framework for Web development
Web Connection provides host of functionality that is specific to Web development. The Web Connection framework provides a number of predefined objects that greatly simplify the process of Web application development by abstracting all the Web specific logic into class libraries so you can focus on your application and not on how to get and output information to and from the Web server.
Applications are built in an application class that has built-in objects for retrieving server and HTML form variables from the Web server and an extremely powerful and flexible HTML class that allows for on the fly HTML HTML
Creation using both low level and high level methods. For example, you can display data with a single call to the ShowCursor() and output the current table to HTML. HTML based scripting with Visual FoxPro code is available via the ShowHTMLFile() method which allows embedding of Visual FoxPro expressions and even entire code blocks directly into the HTML to be displayed. Or if you need low level control you can use the Send() method to output text directly to the server. The HTML class contains over 50 methods for helping you create HTML quickly and providing full control over the HTTP output including built-in support for HTTP Headers, Cookies, graphing via ActiveX
The application framework also provides solid error handling and reporting, parameter parsing, displaying HTML pages from disk via script mapping, form variable decoding, sending SMTP email, FTP and domain name lookup and much more.
Simulated multi-threading with multiple VFP sessions
Web Connection natively supports running multiple instances of any Web Connection server either on the local machine or across the network for maximum scalability. Web Connection has been clocked at close to 200,000/day Visual FoxPro backend hits using two instances of Visual FoxPro Automation servers running on a live commerce site (http://www.surplusdirect.com/). Up to 450,000 hits have been tested in an offline testing environment and even that hasnt maxed out a single machine (Single processor Ppro 200 with 64 megs average request time: 0.12 secs).
Under OLE Automation the Web Connection DLL manages multiple persistent instances of the same Automation server and services requests round robin optimizing throughput through servers. With file based messaging multiple EXEs or standalone applications can be started and run transparently against the same set of data. Scaling up with multiple server instances is totally transparent to the implementation!
Why run multiple servers? Visual FoxPro doesn't support multi-threading but you can simulate this feature by running multiple Visual FoxPro sessions each running a copy of the Web data server. Multiple servers typically dont enhance overall performance, but they can provide better responsiveness to the users of your site by making it possible to process short requests
A single processor can handle two sessions without much overhead, although more session than that will start showing drastic slowdowns as the CPU load gets too large with the simultaneous queries. Multi-processor machines allow additional sessions to scale closer to 90% per session throughput that is acceptable.
Scalable across multiple machines over the network
Web Connection can also scale across the network to further expand processing power if you've maxed out the local processor(s). Web servers are multi-threaded and are fairly CPU intensive especially on single processor machines. Moving processing off the local machine to a network machine frees up the Web server for providing better response which is important for keeping sites running smoothly.
Keep in mind though that scaling over the network incurs a slight performance hit. Local disk access is usually much quicker than requests and result pages travelling over the network. Still, in combination with local data servers, remote servers can greatly enhance the Scalability of a site.
Online code updates without shutting down Web server
Web Connection supports live code updates by providing an update mode for Automation servers that hold all requests and then update the Automation server EXE from an update path. Files can be uploaded to the server via the update path and then hot swapped via an HTML link.
Real time, live debugging
Like FoxISAPI Web Connection leverages ISAPI and OLE Automation, but Web Connection also supports a file based interactive mode that can run on non-ISAPI Web servers as well as providing a real time live development environment inside an interactive Visual FoxPro session, so you can debug your server in real time from real Web requests.
You can receive a request from the Web server and set a breakpoint and step through the code when the request hits. This allows you to test in a live environment where you see the actual hit incoming from the Web server rather than depending on simulated test data manually passed to the server.
The code, compile, debug, fix code cycle is also much quicker since you can fix errors at the source when the code crashes where you can immediately fire up the debugger and examine the status of the application.
One of the strengths of Web Connection is that the framework is geared towards helping automating server addministration. Web Connection can automatically log each incoming Web Request and provide both HTML and VFP based viewers for the data. Sessions can be managed both via an HTML based administration page or via settings in an INI file for startup settings. Its possible to start and stop servers remotely and get information on which servers are running and their load. In addition maintaincence modes are supported so all but one server can be shut down to allow exclusive access to data.
How Web Connection works
Web Connections approach to creating Web application is similar to FoxISAPIs although the overall implementation is a little more complex as the framework uses a number of interrelated objects to provide the additional functionality described above.
With FoxISAPI your interface to the FoxPro application is direct: You call a method in an Automation server directly. While simple this also means that each and every method call in FoxISAPI needs to call various methods/functions on each hit to provide form variable parsing, header creation, set up for HTML output etc.
Web Connection expands on that scheme by having a generic server object (which is the OLEPUBLIC object) take each Web hit and delegating it to the proper program/class. Each hit creates a new class and calls a pre-defined method (Process()). By doing so all the Web specific processing is not splattered into the application code but handled by the generic server object. You get a central entry point that allows you to hook logic that applies to every Web request.
The actual method to handle your request is then parsed out of the URL command line. A typical HREF link looks like this:
Figure 1.6 - The Web Connection data server uses the framework classes to call a method in a CGIProcess object. Adding request handlers is as easy as adding a method to the class.
The CGIProcess object which contains the custom code methods creates both a CGI and HTML object when created and preconfigures these for immediate use. A typical program file that contains the logic to create a new process object and response method looks like this:*** This routine creates the object and calls the Process() method PROCEDURE MyPRG LPARAMETER loCGI#INCLUDE WCONNECT.H #DEFINE HTMLPAGEPATH "h:\http\wconnect\" #DEFINE DATAPATH "wwdemo\" *** Now create a process object loCGIProcess=CREATE("webConnectDemo",loCGI) IF TYPE("loCGIProcess")#"O" *** All we can do is return... WAIT WINDOW NOWAIT "Unable to create CGI Processing object..." RETURN .F. ENDIF *** Call the Process Method that handles the request loCGIProcess.Process RETURN *** Application Class Implementation DEFINE CLASS DevConDemo AS wwCGIProcess && Baseclass Custom FUNCTION CustomerList LOCAL loCGI, loHTML *** Easier reference loCGI=THIS.oCGI loHTML=THIS.oHTML *** Retrieve the name the user entered - could be blank lcCustname=loCGI.GetFormVar("Search") lcBrowser=loCGI.GetBrowser() && Retrieve Browser name/User Agent *** Get all entries that have time entries (expense=.F.) SELECT tt_cust.Company ; FROM TT_Cust ; WHERE tt_cust.company=lcCustname ; ORDER BY Company ; INTO CURSOR TQuery IF _TALLY < 1 *** Return an HTML response page *** You can subclass ErrorMsg to create a customized 'error page' THIS.ErrorMsg("No Matching Records Found",; "Please pick another name or use fewer letters "+; "to identify the name to look up<p><HR>") RETURN ENDIF *** Create HTML document header *** - Document header, a Browser title, Background Image loHTML.HTMLHeader("Customer List","Web Connection Customer List",; "/wconnect/whitwav.jpg") loHTML.SendLn("<b>Returned Records: "+STR(_TALLY)+"</b>") loHTML.SendPar() loHTML.SendLn("<CENTER>") && Set Display to table or <PRE> list - Optional loHTML.SetAllowHTMLTables(loCGI.IsHTML30()) *** Show entire result set as an HTML table loHTML.ShowCursor() *** Center the table loHTML.SendLn("</CENTER>") loHTML.HTMLFooter(PAGEFOOT) USE IN TQuery ENDFUNC * EOF CustomerList
FUNCTION MyMethod2 ... ENDFUNC
Rather than passing all parameters explicitly the wwCGIProcess object - the class DevConDemo is derived from - the CGIProcess object contains an embedded CGI object property named oCGI. This object property encapsulates all the information passed by the Web server as contained in the INI content file that is created by the ISAPI dll. The format of the INI file is similar to the FoxISAPI implementation, but it follows the Windows CGI specification rather than using a proprietary format used by FoxISAPI.
The CGI object encapsulates the CGI variables and provides predefined methods for the most common server variables. Here are a few: GetBrowser, GetServerName, GetPreviousURL, IsSecure, IsHTML30, GetCookie, SetOLEClass just to name a few. You can also parse out 'parameters' from the URL using the GetCGIParameter method which takes a numeric argument to return an entry from the Querystring. For example, if you have wwcgi.dll?MyPrg~MyMethod~Parm1~Parm2 and you wanted to retrieve the Parm2 parameter you'd use loCGI.GetCGIParameter(4) to retrieve the value ("Parm2" in this case).
The CGI object also retrieves form variables that were entered on HTML forms. To retrieve form variables you simply call GetFormVar("UserId") to retrieve a form variable by the name of UserId. Multiple selections are parsed into an array and can be retrieved with loCGI.GetFormMultiple(aValues,"MultiFieldname").
In order to simplify HTML output an HTML class is provided that abstracts the output mechanism. As with the CGI object an HTML object property is automatically created with the CGIProcess object when your processing routine is called. It can be referenced as THIS.oHTML and is already set to output to the appropriate output device. The HTML class provides many high and low level methods to handle output of HTML.
At the low level the Send() method is used for ALL output including any output sent by the internal methods. This is done so you can change HTML output mechanism without changing any code. The HTML class supports output to file, string and optimized string (dumps to file when the buffer gets too big which can drastically speed string processing on large strings).
Many high level methods of the HTML class make short work of creating HTML under program control. You can see a few examples in the code snippet. HTMLHeader() creates a starter page header including HTTP header, Title and a standard HTML header. The CGIProcess Class' ErrorMsg() method creates a freestanding HTML page with a single method call. Simply pass a header and message text to be displayed and a full page is sent back to the Web server.
ShowCursor() is a very handy method for displaying an entire table/cursor as an HTML table with a single call. ShowMemoPage() can display HTML documents stored on disk or in a memo field. The pages can also contain embedded Visual FoxPro character expressions which are expanded as the page is loaded and sent back to the Web server for display. This feature is very handy and important as you can use standard HTML editors to create your HTML layout, then simply embed fields and characters variables, and even entire expressions and code blocks directly into the page. Here's an example:##FOXCODE PUBLIC wcClientName wcClientName=UPPER(poCGI.GetFormVar("ClientName")) SELECT Company, Phone FROM TT_CUST ; WHERE upper(company)=wcClientName ; INTO Cursor Tquery ORDER BY COMPANY *** Always return a string RETURN ""## <HTML> <HEAD><TITLE>Client List</TITLE></HEAD> <BODY BACKGROUND="/wconnect/whitwav.jpg"> <H1>Queried for: ##wcClientName## </H1> <HR> First Record Company: ##Tquery.company##<BR> First Record Phone: ##Tquery.Phone##<p> <CENTER> <!-- HTML Method to display a table in full --> ##poHTML.ShowCursor(,,,.T.)## </CENTER><HR> </BODY></HTML> ##FOXCODE *** Cleanup Code -Always release PUBLIC VARIABLES RELEASE wcClientName USE IN TQUERY *** MUST ALWAYS RETURN STRING! RETURN "" ##
Any character expressions can be embedded within the ## pairs. If the expression contained within is invalid an error message is embedded in the document instead. Note the call to poHTML.ShowCursor() - UDF() calls are valid and the poHTML object is available in ShowMemoPage() parsed pages to help with inline HTML generation.
##FOXCODE can be used to embed entire code blocks into pages - however, doing so is rather slow and not recommended - it's much more efficient to create a separate UDF() and call it from the page rather than using the inline code block. Expressions work with all versions of Visual FoxPro but the FOXCODE option requires the Development version.
- Check out Active Server for sophisticated server scripting
and connectivity to VFP via Automation servers
- For more control use Visual FoxPro as a Web data server
- FoxISAPI provides powerful Automation connectivity with an easy interface
- For a complete Fox based Web environment check out Web Connection
This page has been visited times since November 9, 1996
Last updated: 02/10/97
[Back to West Wind Technologies]