Many companies have adopted Web-based solutions because of the advantages of using a browser in the presentation tier. HTML and HTTP let you create scalable applications that are both cross-platform and self-distributing. Web-based applications also let companies move beyond the LAN and WAN to reach new audiences. A client needs only a Web browser, a TCP/IP address, and an Internet connection.
When you design a Web-based application, you must make assumptions about your user base. In particular, you must decide between developing an application for the majority of users on the Internet and developing an intranet-style application. If you can assume that every client will run a recent version of Microsoft Internet Explorer, you can develop your application using browser-specific features such as ActiveX controls and ActiveX documents. However, this generally limits your audience to intranet users.
This section focuses on cross-platform development for the Web. If you want to reach clients using all types of browsers, you must avoid techniques that involve client-side COM. However, cross-platform development doesn't prevent you from running COM code on the server. You can create Web-based applications that let clients running different browsers on Macintosh and UNIX platforms use your MTS applications just as clients running Internet Explorer and Windows can.
Internet Information Server (IIS) makes it relatively easy to create Web-based applications that interact with your server-side COM components. The key to making everything work is ASP. An ASP page is an HTML page that can contain server-side processing instructions. Once you understand how ASP works, you can write Web pages that create Visual Basic objects and run MTS transactions.
<%@ LANGUAGE="VBSCRIPT" %> <HTML> <!-- MyPage.asp --> <!-- Use VBScript for inline scripts --> <HEAD> <TITLE>My ASP Page</TITLE> </HEAD> <BODY> <!—- Inline server-side script --> <% Dim sMsg sMsg= "The current time is " & FormatDateTime(Now(), vbLongTime) Response.Write sMsg %> </BODY> </HTML>
This example shows how easy it is to write a simple ASP page. Note that the ASP filter recognizes the inline tags <% and %> as the beginning and the end of a server-side script. When you create an ASP page, you can program against the five built-in ASP objects: Request, Response, Session, Application, and Server. I won't cover the fundamentals of ASP programming here because many excellent resources are available on this topic. I'll simply assume that you have a basic knowledge of writing ASP pages, and I'll show examples only of the VBScript code that you can write inside an ASP script.
The Windows NT 4 Option Pack provides a new level of integration between IIS and MTS. This integration amounts to a melding of the two run-time environments. Figure 12-3 shows a diagram of the IIS Web server process (inetinfo.exe). As you can see, the Web server process loads the MTS Executive (Mtxex.dll). The installation of the Windows NT 4 Option Pack automatically creates an MTS library package named IIS In-Process Applications. If you locate this package in the MTS Explorer, you can see that it is preconfigured with a set of Web application manager (WAM) components. WAM components are registered with the MTS Explorer, which means that WAM objects are MTS objects. Each WAM object has a context wrapper and can talk to the MTS run time through its object context.
Figure 12-3. A WAM object is responsible for creating an ASP filter when it processes an ASP page request. This filter parses server-side scripts from the page and runs them before returning control to the client.
IIS uses WAM objects to handle incoming requests. (The manner in which WAM objects work is undocumented, so my descriptions here contain some degree of speculation.) Although Figure 12-3 shows a simplified picture of a single WAM object creating an ASP filter, in most cases a client request for an ASP page passes between several WAM objects before being processed. WAM objects are also very sophisticated in their use of thread pooling and thread dispatching. While the low-level details of how WAM objects work shouldn't concern you, you should understand that a WAM object is ultimately responsible for creating an ASP filter when a client requests a Web page with an .asp extension.
IIS 4 also lets you configure an ASP application (a virtual directory) to run in its own dedicated process. You can thus isolate your ASP application from the IIS Web server process as well as from other ASP applications. The way to configure an ASP application to run as a separate process is by using the Internet Service Manager and selecting the Run In Separate Memory Space (Isolated Process) option in the Properties dialog box for the ASP application's virtual directory.
Running an ASP application in isolation can provide greater isolation and fault tolerance in a production environment. Running in isolation can also be quite handy during development as well. When you create Visual Basic DLLs for an ASP application, you'll find that you need to unload the application before rebuilding the DLL. This is a real pain if you've loaded a DLL into the IIS Web server process because you have to shut down two separate IIS services before you can rebuild your DLL. However, the Properties dialog box for an isolated ASP application gives you the option of unloading the ASP application's process from memory. Once you unload the ASP application, you can rebuild any DLLs that it has loaded.
When you configure your ASP application to run in an isolated process, IIS launches your ASP application in a new instance of the MTS container application (Mtx.exe), as shown in Figure 12-4. IIS also creates a new MTS package dedicated to the isolated ASP application. For example, if you mark a virtual directory named Market to run in isolation, a package named IIS-Default Web Site//Root/Market will be created for you automatically.
Whether you run your ASP applications in isolation or in the IIS Web server process, a WAM object is responsible for processing each request for an ASP page. When a WAM object processes an ASP request, it creates a COM object that serves as the ASP filter. We'll call this object an ASP scripting object. The scripting object scans through the ASP page and parses out any server-side processing instructions. Once these server-side processing instructions run, IIS returns an HTML stream to the client.
Figure 12-4. When you run an ASP application as an isolated process, it's launched using the MTS container application (Mtx.exe). IIS redirects incoming requests from WAM objects running in the IIS Web server process to a WAM object running in the isolated ASP application.
You enjoy a few benefits when you move your business logic and data access code out of ASP pages and into a Visual Basic DLL. First, ASP is great for creating small programs, but managing an application that contains thousands of lines of code becomes increasingly difficult. Using a class-based object-oriented programming language such as Visual Basic offers much higher levels of encapsulation and code reuse. Second, writing code in the Visual Basic IDE offers enhancements such as IntelliSense and compile-time type checking that you don't get when writing VBScript code. This can make a huge difference when you're writing lots of data access code against the ActiveX Data Objects (ADO) type library. Third, today's multitier designs encourage the use of business objects to decouple the user interface code from the data access code. One of the benefits of this separation is that it eliminates the need for SQL in your ASP code. Finally, COM-based DLLs created with Visual Basic can take full advantage of the integration between IIS and the MTS run-time environment. Using MTS components in an ASP application gives you an infrastructure for building a reliable Web-based OLTP system.
The easiest technique for creating a Visual Basic object from an ASP page is to use the CreateObject method of the built-in ASP Server object:
<% Dim obj Set obj = Server.CreateObject("MyDll.CMyClass") Response.Write obj.MyMethod() %>
Server.CreateObject takes a ProgID and creates a new object. You should note that this ASP call is similar to calling the CreateInstance method in the ObjectContext interface in MTS. A call to Server.CreateObject always creates an MTS with its own context wrapper.
Once you've created an object from an ASP page using Server.CreateObject, you can then invoke methods and access properties on the object as usual. Figures 12-3 and 12-4 show how you can run an MTS transaction from an ASP page. If you want to run an MTS transaction in an isolated ASP application, you should install your MTS components in the special MTS server package that was created for you when you marked the application to run in isolation. If you want to run an MTS transaction in the IIS Web server process, you should install your MTS components in a library package.
When you run an MTS transaction from an ASP page, you might consider running the transaction in a separate MTS application for security reasons. When you run your transaction in the same process as the ASP code, you rely on IIS to authenticate each user. If the ASP application has been configured to allow anonymous access, any Internet user can run your transactions. If you run your transactions in a separate MTS application, you can use the MTS role-based security model to give you more control over which users can run your transactions.
You should note a few strange things about writing ASP code with VBScript. First of all, all variables must be variants. This takes some getting used to. Second, VBScript can currently retrieve output parameters only when they are declared as variants. If you try to call a method on a Visual Basic object that has an output parameter defined as String or Double, the VBScript code will fail and generate a fairly confusing error message.
Let's look at an example of running a transaction to submit an order in an ASP page. The following ASP code does three things: First, it scrapes the user's input off the HTML form using ASP's built-in Request object. Second, it creates an instance of a Visual Basic component named CBroker and calls the SubmitOrder method. You should assume that this component and all the secondary components it uses have been properly written and registered with the MTS Explorer. Third, the code checks to see whether an error was raised during the call to SubmitOrder and sends the appropriate response to the client using ASP's built-in Response object.
<% On Error Resume Next ' Get input from user form. Dim Customer, Product, Quantity Customer = Request.Form("txtCustomer") Product = Request.Form("cboProduct") Quantity = Request.Form("txtQuantity") ' Submit order to Visual Basic object. Dim Broker, OrderStatus Set Broker = Server.CreateObject("WebMarket.CBroker") Broker.SubmitOrder Customer, Product, Quantity, OrderStatus ' Test to see whether transaction was successful. If Err.Number = 0 Then Response.Write OrderStatus Else Response.Write "Your order submission was not accepted<br>" Response.Write "Error: " & Err.Description End If %>
Error handling isn't as graceful in VBScript as it is in Visual Basic. You must conduct error handling by using an On Error Resume Next statement and checking the error number after each call. This is cumbersome, but it works. VBScript has no problem handling the errors generated by the root object inside a transaction. In Chapter 10, I suggested a way to write the methods for the root object in an MTS transaction. When you can commit the transaction, you should return the call without raising an error. If you abort the transaction, the root object should raise an error back to the caller with an informative error message. The ASP code above assumes that the SubmitOrder method follows this convention.
The integration between ASP and MTS allows you to mark an ASP page with a transaction support property, just as you can do with a registered MTS component. This means that an ASP scripting object can serve either as the root object or as a secondary object inside an MTS transaction. ASP also adds another built-in ASP object named ObjectContext, which gives you the ability to call SetComplete or SetAbort directly from your VBScript code. A transactional ASP page even provides two events that you can use to handle the conditional logic when a transaction has been committed or rolled back. Here's a simple example of a transactional ASP page:
<%@ TRANSACTION=Required LANGUAGE="VBScript" %> <% Main Sub Main() ' Execute some ADO code. ' Execute some more ADO code. If (ThingsAreGood) Then ObjectContext.SetComplete Else ObjectContext.SetAbort End If End Sub ' ASP supplies events for handling conditional commit/abort logic. Sub OnTransactionCommit() Response.Write "transaction succeeded" End Sub Sub OnTransactionAbort() Response.Write "transaction failed: " & Err.Description End Sub %>
While transactional ASP pages give you yet another transaction-programming option, they don't offer you the same advantages of writing your transactions with Visual Basic. You will experience far more control and much better performance with compiled Visual Basic components that have been registered with the MTS Explorer. Transactional ASP pages are most valuable to Web programmers who want the benefits of MTS transactions without having to resort to using a compiled language, such as Visual Basic or C++. Since you've made it to the last chapter of this book, I assume that you're more than capable of creating MTS components with Visual Basic and that transactional ASP pages won't be of much interest to you.
When you program against the five built-in ASP objects in an ASP page script, you are actually calling methods on an implicit scripting object. However, you can also program against these built-in ASP objects from inside your Visual Basic DLLs. Figure 12-5 shows the lines of communication that you must set up between the scripting object and a Visual Basic object. Once you create the connection from your Visual Basic object back to the scripting object, you can program against built-in ASP objects such as Request and Response from inside your Visual Basic DLL.
Figure 12-5. When you create a Visual Basic object from an ASP page using Server.CreateObject, the Visual Basic object can program against the built-in ASP objects.
An ASP scripting object exposes an interface named ScriptingContext, which lets you get at the five built-in ASP objects. However, before you can use any ASP objects in your Visual Basic project, you must reference the ASP type library. Figure 12-6 shows the ASP type library as seen through the Object Browser. As you can see, the built-in ASP objects are exposed as COM objects.
Figure 12-6. When you program against the ASP type library, you have access to all the built-in ASP objects from a Visual Basic DLL.
IIS 3 was the first version of IIS that let you program against the built-in ASP objects. The trick is to use the scripting object's ScriptingContext interface, which lets you navigate to the five other ASP objects. However, you need a little assistance to get to the ScriptingContext interface.
When you call Server.CreateObject from an ASP page, IIS creates the object and attempts to call into it through a special method named OnStartPage. If the call to OnStartPage succeeds, IIS passes a ScriptingContext reference to the new object. If the object doesn't implement a method named OnStartPage, the call fails and IIS ignores the error. If you implement this method, you can cache and use this ScriptingContext reference. The following code shows a Visual Basic class that does exactly this.
Private ScriptCtx As ScriptingContext Sub OnStartPage(sc As ScriptingContext) ' Cache reference when called by IIS. Set ScriptCtx = sc End Sub Sub MyMethod() ' Retrieve built-in ASP objects. Dim req As Request, rsp As Response Set req = ScriptCtx.Request Set rsp = ScriptCtx.Response ' Get input from HTML form. Dim Customer As String Customer = req.Form("txtCustomer") ' Write reply back to browser. rsp.Write "Thanks for your business" End Sub
This example shows how to retrieve the Request object and the Response object from the ScriptingContext interface. Once you retrieve a reference to a built-in ASP object, you can program against it as if you're in an ASP page. However, Visual Basic provides many advantages over writing ASP code with VBScript. The Visual Basic IDE provides IntelliSense and compile-time type checking because you're programming against a type library. Your code will also run faster than VBScript code because it is compiled.
The technique I've just shown you, of using OnStartPage to get at the built-in ASP object, became obsolete with the release of IIS 4. Now you can get at the five built-in ASP objects without using the ScriptingContext interface. The built-in ASP objects are available through MTS's ObjectContext interface.
When you call Server.CreateObject from an ASP page, the new object is created as an MTS object with a context wrapper and an object context. Server.CreateObject also populates the object context's Item collection with the five built-in ASP objects. The following code shows how to retrieve the Request object and the Response object using the ObjectContext interface.
Sub MyMethod() Dim ObjCtx As ObjectContext Set ObjCtx = GetObjectContext() ' Retrieve built-in ASP objects. Dim req As Request, rsp As Response Set req = ObjCtx.Item("Request") ' Longhand Set rsp = ObjCtx("Response") ' Shorthand ' Get input from HTML form. Dim Customer As String Customer = req.Form("txtCustomer") Dim Product As String Product = req.Form("txtProduct") Dim Quantity As Long Quantity = req.Form("txtQuantity") ' Process client's request. ' Write response message back to browser. rsp.Write "Your order number is 1783" End Sub
You can also retrieve the ASP Session object and the Application object using the same technique. However, you should avoid placing Visual Basic objects in either session variables or application variables. Shared Visual Basic objects create problems that severely hamper the thread dispatching architecture of IIS. Stick to using Visual Basic objects that are created and destroyed within the scope of a single ASP request.
Visual Basic 6 has a new Web-based productivity enhancement called the IIS Application Designer (IISAD). This extension of the Visual Basic IDE provides a new type of component known as a Web class. The way things work behind the scenes in an IISAD application is similar to what I just described. The IISAD lets you create cross-platform Web applications by programming against the built-in ASP objects from inside the Visual Basic IDE, as shown in Figure 12-7. However, IISAD brings even more to the table.
Figure 12-7. Web classes make it simple to program against built-in ASP objects from within the Visual Basic IDE. The IISAD provides many productivity enhancing features, such as imported HTML templates with find-and-replace capabilities and integrated debugging.
One of the biggest benefits of using the IISAD and Web classes is that they hide many of the tedious details of ASP programming from the Visual Basic developer. For example, the IISAD automatically creates the ASP page that will serve as an entry point into your ASP application. This ASP page creates an instance of a Web class manager object that controls the entire IISAD application. The ASP page calls upon the Web class manager object to create instances of your Web classes. As you can see, IISAD is a framework that is layered on top of ASP. What's more, the Web class type library adds a productive layer on top of the objects and interfaces in the ASP type library. The IISAD even gives you a way to debug your ASP code inside the Visual Basic IDE.
Another nice feature of the IISAD is that it lets you work with HTML templates. Using these templates, you can keep the HTML layout details separate from your Visual Basic code. Once you import an HTML template into your project, you can perform a search-and-replace on certain tags and stream the results back to the client. This is a great way to create an entire Web site with a single project using the Visual Basic IDE.
Because I want to finish this book sometime in 1998, I'm not going to cover the nuts and bolts of using the IISAD and Web classes. However, you should look into them if you plan to develop large-scale Web applications. Keep in mind that the IISAD framework adds complexity to an ASP application. You must plan the time to become familiar with this framework before you can expect to be productive. In some ASP applications, the complexity of this framework might be more trouble than it's worth. Remember that you still have the option of creating an ASP application with Visual Basic DLLs without the IISAD, as I showed you earlier.