Using Windows Component Services (COM+) with Visual FoxPro

download Using Windows Component Services (COM+) with Visual FoxPro

of 50

description

Using Windows Component Services (COM+) with Visual FoxPro

Transcript of Using Windows Component Services (COM+) with Visual FoxPro

  • Using Windows Component Services (COM+) with

    Visual FoxPro

    Craig Berntson 3M Health Information Systems

    3757 South 700 West #21 Salt Lake City, UT 84119

    Voice: 801-699-8782 www.craigberntson.com

    Email: [email protected]

    Overview This session gives an overview of COM+ services, beginning with the basics of COM and how component design changes under COM+. It then moves into creating COM+ components and how to administer COM+ applications and security. It then moves onto transaction management and transacting VFP tables using Compensating Resource Managers. Asynchronous events such as Queued Components and Loosely Coupled Events are covered.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 3 of 49

    Reviewing COM For many years, COM has been the method used to integrate and automate applications under Windows. For example, if you automate Word, Excel, or Outlook from your Visual FoxPro application, you do it through COM.

    COM is an acronym for the Component Object Model. It is a specification for writing reusable software that runs in component-based systems. It turns out there are two types of COM components, in-process and out-of-process.

    An in-process component is compiled as a DLL and requires an EXE to host it. When you call the component, it is hosted by your application EXE, thus it runs in your application s memory space on the same computer. An out-of-process component is compiled as an EXE and runs in its own memory space. There are pros and cons of each method. If the in-process DLL should crash, it could corrupt the memory space of your EXE and cause it to crash too. This isn t the case with a COM EXE. Because it runs in its own memory space, it is considered safer to run. However, the data transfer across the process boundaries from your EXE to the COM EXE takes longer than from your EXE to the COM DLL, making the EXE slower.

    The application that uses COM component is called the client and the COM component is called the server. Do not confuse this with Client/Server databases. Visual FoxPro provides the capability to create clients and both in-process and out-of-process servers, with one caveat. VFP in-process components do not support user interfaces, so you can t get any screen output from them.

    Whether you compile an in-process DLL or an out-of-process EXE, what happens under the hood is pretty much the same. You application never directly talks to the COM component. Instead, it talks to a proxy. The data is communicated across the process boundary to a stub, which then passes communication on to the COM component (Figure 1). You don t have to worry about creating the proxy and stub or a communication channel for the data. Windows does all that for you. The work that Windows does for this is frequently called plumbing (after the plumbing in your house).

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 4 of 49

    Figure 1. COM components talk to each other across a process boundary.

    When you use a COM component, you typically connect to it, do lots of work with it, then disconnect at the very end. This is a stateful connection, meaning the component exists (has state) for a lengthy period of time. Creating a COM component At this point, you may be wondering how to create a COM component. The following steps walk you through creating a COM component. Create a new project called BizRules Add a new program file named MathClass.prg. Enter the code in Listing 1 into the editor.

    Listing 1. This code is used to create a COM component

    DEFINE CLASS Math AS SESSION OLEPUBLIC nNumber1 = 0 nNumber2 = 0

    FUNCTION Add(tnOne AS Integer, tnTwo AS Integer) AS Integer LOCAL lnResult

    WITH This IF PARAMETERS() = 2 .nNumber1 = tnOne .nNumber2 = tnTwo ENDIF

    lnResult = .nNumber1 + .nNumber2 ENDWITH

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 5 of 49

    RETURN lnResult ENDFUNC

    FUNCTION Multiply(tnOne AS Integer, tnTwo AS Integer) AS Integer LOCAL lnResult

    WITH This IF PARAMETERS() = 2 .nNumber1 = tnOne .nNumber2 = tnTwo ENDIF

    lnResult = .nNumber1 + .nNumber2

    ENDWITH RETURN lnResult ENDFUNC ENDDEFINE

    Save and close the program and select Build from the Project Manager. The Build Options dialog is displayed Select Single-threaded COM server (DLL) then click OK. Visual FoxPro compiles the program and creates the DLL.

    Notice the OLEPUBLIC keyword. This tells the compiler to create a COM component. If you design the class visually, check OLE Public on the Class Info dialog (Figure 2).

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 6 of 49

    Figure 2. The Class Info dialog is used to set information for the entire class.

    The Build Option dialog gave three choices for building a COM component, as listed in Table 1. Table 1. The Build dialog contains three options for creating a COM component Build Action Description Win32 Executable / COM server (EXE) Creates an out-of-process EXE COM server Single-threaded COM server (DLL) Creates a single-threaded in-process DLL COM server Multi-threaded COM server (DLL) Creates a multi-threaded in-process DLL COM server

    A single-threaded COM server can only be instantiated one time. Subsequent calls will be blocked and will wait until the COM server is available. For example, if you have a COM server that runs a lengthy process and multiple users attempt to call it at the same time, the first user will get access and other users will wait their turn. A single-threaded COM server is useful when the component is installed locally on the workstation.

    A multi-threaded COM server gets around this problem. Multiple calls are not blocked. Use multi-threaded COM server when the component should be installed on a server for multi-user access. This is the type of COM DLL you will compile when creating COM+ components.

    When VFP builds a COM component, it generates several registry entries that point to the directory and file that was compiled. This way, when the component is used, VFP can find it when it s run on the development machine. In addition, a type library is created. This file contains the same root name as the DLL but with a .tlb extension. If you distribute the COM DLL, you need to include the type library and the proper VFP runtime libraries.

    Now that you ve created the COM component, it s time to use it. The following steps show you how to do this. In the Command Window enter MODIFY COMMAND TestLocal Enter the code in listing 2. Save and run the program.

    Listing 2. Code to instantiate and use a COM component.

    CLEAR ox = CREATEOBJECT( BizRules.Math ) ? ox.Multiply(2, 3) ox.nNumber1 = 5 ox.nNumber2 = 6 ? ox.Add()

    When you run the program, first the screen is cleared, the COM component is instantiated. VFP looks up BizRules.Math in the registry to get the location of the COM file. Then, the Multiply method is called and the result of 6 is displayed. The two

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 7 of 49

    numbers are then set to new values, the Add method is called, and the result of 11 is displayed. When the program ends, the component is released.

    What is DCOM? Under the Distributed Component Object Model (DCOM), the component is installed on a remote computer, typically a server. The component runs in memory on the server, rather than the work station. An in-process DLL still needs to be hosted by an EXE. In the case of DCOM, this is typically DLLHost.exe.

    The plumbing for a DCOM application is very similar to that of a COM application, except that the communication is across a computer boundary (Figure 3). Windows still provides all the plumbing for this to happen. However, you have to do some configuration so the workstation knows on which server the component is installed. You have probably already guessed that DCOM is slower than COM because the communication from the application to the component needs to go across the network.

    Figure 3. DCOM components communicate across computer boundaries.

    One advantage of DCOM over COM is that the component is centrally located on the server. You don t need to distribute the component to each user when it is updated. For example, you can create a component that contains business rules for generating invoices. If those rules change, you need only to install a new DLL on the server without touching the client workstations. DCOM applications are also typically stateful.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 8 of 49

    So, what are Component Services? Windows Component Services, also called COM+, is a set of services that host COM components for DCOM-type communication. At a basic level, COM+ works the same way as DCOM (See Figure 3). However, unlike DCOM, COM+ applications get much of their scalability and extensibility from stateless connections. When using stateless connections, you should connect to the component, run the method, then disconnect.

    In addition to increased scalability, there are a number of additional services provided by COM+. These services include:

    Administration

    Security

    Transaction management

    Just-in-time Activation

    Object Request Broker (ORB)

    Connection Pooling

    Loosely Coupled Events

    Queued Components

    Dynamic Load Balancing

    One other difference between DCOM and COM+ is that you should only use in-process components in COM+. These DLLs are hosted by the COM+ runtime. Before learning how to create a COM+ component, you need to understand a couple of other concepts, context and threading.

    The COM+ runtime is capable of hosting multiple instances of the same or different COM DLLs. Each DLL is isolated of each other by running each one in its own context. The DLL has access to the context to learn about the environment where it is running.

    Threading refers to how many operations at a time the application or component is capable of running. There are many types of threading, but I will only discuss three here, single, multi, and apartment-model.

    Single threading means that an application or component can only do one thing at a time. Visual FoxPro is mostly single threaded. It uses multiple threads internally for some things, but it does not give you the capability to create more than one thread. When you build a Win32 Executable / COM Server (exe) or a Single-threaded COM server (dll), you get single threading. When an application is multi-threaded, it is capable of doing several things at the same time. A VFP COM DLL that is compiled using the Multi-threaded COM server (dll) uses a special type of multi-threading called apartment-model threading. Under apartment model threading, each thread lives in its own apartment and is unaware of other apartments. Conflicts with global data are avoided by giving each apartment its own copy of the global data. You don t have to worry about apartment model threading as it happens automatically with VFP COM DLLs.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 9 of 49

    Creating a COM+ Component Now it s time to look at how to create a COM+ component. Technically, you could use the DLL created earlier, but it will have no connection to its context. The code in Listing 3 builds on the DLL by adding some code to interact with its context.

    1. Create a new project called FoxCOMPlus Add a new program file named MathClass.prg. Enter the code in Listing 3 into the editor.

    Listing 3. A simple COM+ component

    DEFINE CLASS Math AS SESSION OLEPUBLIC nNumber1 = 0 nNumber2 = 0

    FUNCTION Add(tnOne AS Integer, tnTwo AS Integer) AS Integer LOCAL loMtx, loContext, lnResult

    loMtx = CREATEOBJECT("MTXAS.APPSERVER.1") loContext = loMtx.GetObjectContext()

    WITH This IF PARAMETERS() = 2 .nNumber1 = tnOne .nNumber2 = tnTwo ENDIF

    lnResult = .nNumber1 + .nNumber2 ENDWITH

    loContext.SetComplete() loContext = NULL loMtx = NULL

    RETURN lnResult ENDFUNC

    *****************

    FUNCTION Multiply(tnOne AS Integer, tnTwo AS Integer) AS Integer LOCAL loMtx, loContext, lnResult

    loMtx = CREATEOBJECT("MTXAS.APPSERVER.1")

    loContext = loMtx.GetObjectContext()

    WITH This IF PARAMETERS() = 2 .nNumber1 = tnOne .nNumber2 = tnTwo ENDIF

    lnResult = .nNumber1 * .nNumber2 ENDWITH

    loContext.SetComplete() loContext = NULL loMtx = NULL

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 10 of 49

    RETURN lnResult ENDFUNC ENDDEFINE

    Save and close the program and select Build from the Project Manager. The Build Options dialog is displayed. Select Multi-threaded COM server (DLL) then click OK. Visual FoxPro compiles the program and creates the DLL.

    Before you can use the new component, you need to install it in the COM+ runtime. You will want to install it locally for testing. For deployment, you will need to place the DLL, the type library, and the VFP runtime files on the server.

    Installing the component in the COM+ runtime COM+ organizes components into applications. The following steps walk you through creating the COM+ and application installing the component into the new application.

    1. From Windows Administrative Tools, select Component Services. The Component Services Manger is displayed (Figure 4).

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 11 of 49

    Figure 4. The Component Services Manager is used to administer COM+ applications.

    Expand the Component Services Node to Computers -> My Computer -> COM+ Applications. Right-click on COM+ Applications and select New -> Application from the context menu. The COM+ Application Install Wizard is displayed. Click Next to move past the welcome page. On the Install or Create page, you want to create a new empty application (Figure 5).

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 12 of 49

    Figure 5. Add a new COM+ application with the Install Wizard

    On the Create Empty Application page (Figure 6), enter the name for your application. For this example, use My COM+ App. Make sure Server Application is selected, then click Enter.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 13 of 49

    Figure 6. When you create an empty application, you need to name the application.

    The next page, Set Application Identity (Figure 7), allows you to set the Windows user identity that this component runs under. The default is Interactive User. It is not a good idea to set the component to run under this identity as someone needs to be logged on something not common for a server. It is recommended that you setup a Windows user specifically for your component and assign the specific rights to that user that will be needed. For the purpose of this example, select Interactive User, then click Next. The Thank You page is displayed. Click Finish to create the COM+ Application. Now you need to add the FoxCOMPlus.dll to the new COM+ application. Expand the My COM+ App node in the Component Services Manager. Right click on Components and select New -> Component from the context menu. The Welcome page of the COM+ Component Install Wizard is displayed. Click Next. On the Import or install a component page (Figure 8), click Install new component(s). A file picker dialog is displayed.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 14 of 49

    Figure 7. COM+ applications can be set to run under a specific user account.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 15 of 49

    Figure 8. After creating a new COM+ application you need to add the components.

    Navigate to the proper folder and select both FoxCOMPlus.dll and FoxCOMPlus.tlb. The Install new components page is displayed (Figure 9). Click Next. The Thank You page is displayed.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 16 of 49

    Figure 9. Use the Install new components page to specify the DLLs to add to the application.

    Click Finish to complete the installation.

    To test the component, run the same test program that you ran earlier (Listing 3). To verify that you actually instantiate the COM+ component, select the My COM+ App -> Components node in the Component Services Manager (Figure 10). When the ball in the right-hand panel is spinning, the component is currently instantiated.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 17 of 49

    Figure 10. After adding a component you can manage it using the Component Services Manager.

    Just In Time Activation and Statelessness When you instantiate the component, COM+ uses Just In Time Activation (JITA) to load and create the component. The CREATEOBJECT() function never really instantiates the component. Instead, it connects to the COM+ runtime, which in turn, instantiates the component, through the use of a proxy. When SetComplete() or SetAbort() is called, COM+ releases the component, but the proxy remains. Your application does not know that the component no longer exists. The COM+ runtime activates the component on the next call. In fact, the Init method of the component will be called again because the component has been instantiated again. You may notice that the second call to the component takes less time. That is because JITA keeps a copy of the component in memory for a period of time after you re done using it. The default time is 15 minutes, but can be changed on the Pooling & Recycling page (Figure 11) of the application s Property dialog in the Component Services Manager.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 18 of 49

    Figure 11. The application timeout is set on the application's property sheet Pooling and Recycling page.

    The following steps walk show how JITA works. 1. Create a new project named JITA.pjx.

    Add a new program using the code in Listing 4.

    Listing 4. Code showing Just in Time Activation

    #DEFINE CRLF CHR(13) + CHR(10)

    DEFINE CLASS JITA AS Session OLEPUBLIC oCom = NULL cFile = "c:\complus\log.txt" cProp = "Original Val"

    PROCEDURE Init STRTOFILE("Init: " + This.cProp + CRLF, This.cFile, 1) This.oCom = CREATEOBJECT("MTXAS.APPSERVER.1") ENDPROC

    PROCEDURE Destroy STRTOFILE("Destroy" + CRLF, This.cFile, 1) ENDPROC

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 19 of 49

    PROCEDURE SetProp(tcNewValue) loContext = This.oCom.GetObjectContext() STRTOFILE("SetProp (before): " + This.cProp + CRLF, This.cFile, 1) This.cProp = tcNewValue STRTOFILE("SetProp (after): " + This.cProp + CRLF, This.cFile, 1) loContext.SetComplete() ENDPROC

    PROCEDURE GetProp() loContext = This.oCom.GetObjectContext() STRTOFILE("GetProp: " + This.cProp + CRLF, This.cFile, 1) loContext.SetComplete() ENDPROC ENDDEFINE

    Save the new program as JITA.prg. Compile the project as a Multi-threaded COM DLL. Add the new DLL to the My COM+ App application in the Component Services Manager. Create a new program. Use the code in Listing 5. Do not include this program in the project.

    Listing 5. Activating and using the Just in Time Activation component.

    SET SAFETY off ERASE Log.txt CLEAR ox = CREATEOBJECT("jita.jita") ox.SetProp("New Value") ox.GetProp() ox = NULL MODIFY FILE Log.txt

    Save the new program as TestJITA.prg, then compile and run it.

    As you can see from the example, the value of cProp gets reset to its original value because the Init method is run again when the component is called the second time. This example also shows how COM+ is stateless. The value, or state, of cProp was not maintained between calls. Stateless execution is how COM+ components are able to scale to thousands of users. However, this comes at a price. If you have a lot of code in the Init or Destroy methods, it will have to run on each call to the component, decreasing performance.

    To better understand this JITA and statelessness, modify JITAExample.prg and comment out the SetComplete() line in the SetProp() method. Compile the DLL, then rerun TestJITA. You will see that the value of cProp has its new value and that the Init and Destroy methods were not called until GetProp() was called. At this point you may be thinking that it may be better to not call SetComplete() until the end of your application. I advise you not to do this as it hurts scalability, one of the biggest features of COM+.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 20 of 49

    Note You may not be able to rebuild the COM DLL. This is because the COM+ runtime still has the component in memory, even though you destroyed your instance of the component. The component will stay in memory for the length of the timeout, specified in the Component Services Manager. If you find you cannot rebuild the component, right-click on the application in the Component Services Manager and select Shut down from the context menu, then try again to rebuild the component.

    Client Installation You ve seen how to install the component on the server. However, in a production environment, the client at this point knows nothing about where this component is located. You need to install the proxy information on the client. The Component Services Manager provides a wizard for you to create the client install package. The following steps walk you through this.

    1. In the Component Services Manager, right click on My COM+ App and select Export. The COM+ Application Export Wizard is launched. Click Next. The Application Export Information page is displayed (Figure 12).

    Figure 12. You can export a COM+ application to install it on another server.

    Enter the path and file name (C:\COMPlus\ClientInstall) for the install file to be created. Select Application proxy Install on other machines to enable access to this machine. Click Next then Finish.

    Two files are created, ClientInstall.msi and ClientInstall.msi.cab. Install one of these files (you don t need both) on each client computer or include them in your setup program. The file VFPCOMPlus.dll is not included in this install. The client doesn t need it, as the

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 21 of 49

    component is referenced from the client and run on the server. What is created and installed on the client is the proxy information the client needs to find and instantiate the component.

    Security Security is an important part of any application. Setting up security under COM+ is easy because the COM+ runtime handles security for you. You configure security in the Component Services Manager.

    COM+ security is role-based. You assign users to a specific role, then assign that role to the particular COM+ application, component, or even a specific method. This type of security is often called declarative. However, rather than individually assign users directly to a role, it is considered best practice to add the users to a Windows security group, then add the group to the COM+ security role.

    In addition, COM+ allows you to use query the context of the component to determine to which role the user belongs. For example, you may have a component that both a manager or clerk can run, but base the processing on which role (manager or clerk) is currently accessing the component. For information on programmatic security, consult the COM+ documentation on the MSDN website.

    The following steps show how to setup declarative security. 1. Create a new Windows Security group named COM+ Test Group.

    Add your Windows login to this group. In the Component Services Manager, expand the node under My COM+ App. Right click on Roles and select New Role from the context menu. Enter Test Role for the role name, then click OK to save the role. Expand the Test Role node, right click on Users and select New User. right click on the My COM+ App application and select Properties from the context menu. Select the Security page on the properties dialog.

    Transactions Imagine you are saving invoice data. Typically this requires updating multiple tables, invoice header, invoice detail, customer information, inventory, shipping, and possibly other tables. What would happen if only one of some of those tables get updated? Well, the current data would be invalid. That s where transactions come in. Transactions ensure that the data is saved properly.

    A complete transaction must follow four rules, called ACIDity:

    Atomicity: This means the entire transaction commits or nothing commits.

    Consistency: The transaction is a correct transformation of the system state

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 22 of 49

    Isolation: Transactions are protected by seeing each other s partial or uncommitted results

    Durability: Committed updates to managed resources can survive failures. For example, if the server should lose power before all the tables have been updated, the partial updates are automatically rolled back.

    You are probably used to using VFP transactions with BEGIN/END TRANSACTION and committing or rolling back with COMMIT/ROLLBACK. However, VFP transactions only work for VFP data and are not managed by COM+. In addition, VFP transactions are not ACIDic because they are not durable.

    For transactions to participate under COM+, the database needs to have a Resource Manager. VFP does not have a Resource Manager, but VFP data can participate in COM+ transactions by using a Compensating Resource Manager, discussed later in this document.

    COM+ transactions have several advantages over regular VFP transactions, or even standard transactions in SQL Server, Oracle, and other databases. These advantages include:

    Updates to multiple databases. For example, a single transaction can span multiple databases, even from multiple vendors.

    Support for major databases such as SQL Server and Oracle.

    Internet capable through TIP (Transaction Internet Protocol), a protocol developed by Microsoft, Compaq, Hitachi, and other companies.

    Bring Your Own Transaction (BYOT). This is supported via Compensating Resource Managers and TIP.

    The COM+ runtime uses the Microsoft Distributed Transaction Coordinator (MSDTC) to handle transactions. This means that the transaction is handled outside the database. This may sound risky, but because the transaction is done in a two-phase commit, it works well.

    In phase one, the MSDTC queries each database, asking it if it can save the data. Once each database has replied, phase two starts. If each database has replied that it can do the transaction, then the MSDTC tells each database to commit the data. If any of the databases replies that it can not do the transaction, the MSDTC issues a rollback to each database.

    Earlier examples used SetComplete and SetAbort. I explained that these methods told COM+ that you are done with the component. They also tell the DTC to commit or abort the transaction. However, sometimes you want to commit or abort without releasing the component. This is accomplished with Transaction Voting.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 23 of 49

    Under Transaction Voting, there are two bits that you use to handle the transaction and the release of the component. The first, the Consistency bit is set when you want to commit the transaction. The second, the Done bit, is set when you are ready to release the component. The Context object provides four interface methods to help manage these bits.

    SetDeactivateOnReturn Sets the Done bit

    GetDeactivateOnReturn Gets the value of the Done bit

    SetMyTransactionVote Sets the Consistency bit

    GetMyTransactionVote Gets the value of the Consistency bit

    In addiction to voting on the transaction, you can set the transactional support for the component using the Component Services Manager. You will see exactly how to set this in the upcoming example. The four settings are:

    Requires a transaction The component must run under a transaction. If there is not currently a transaction, a new one is started.

    Requires a new transaction The component must run under a transaction. A new one is always started.

    Supports transactions The component will use a transaction if one is in progress, but it not, a new one is not started.

    Does not support transactions

    The component doesn t care if a transaction is running or not.

    There is one more item to cover before getting to the example. COM+ applications typically run business and data components. Business components are setup as requires a transaction, requires a new transaction, or does not support transactions. Data components are setup as requires a transaction or does not support transactions.

    Now, on to the example. Here you will see how to add transactional code to the component and set the automatic transaction support provided by COM+. The example is shown in three parts, the user interface, the business component, and the data components. In order to show that the transaction works across different databases, the Pubs and Northwind sample databases from SQL Server are used.

    The user interface is shown in Figure 13. An example form for testing COM+ transactions..

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 24 of 49

    Figure 13. An example form for testing COM+ transactions.

    To simplify the example, a cursor to hold the data is created in Load method (Listing 6) of the form.

    Listing 6. The Init() method code from the Transactions form.

    CREATE CURSOR cXact (Fname C(10), LName C(20), Address C(40), ; City C(15), State C(2), Zip C(5), Country C(15), Phone C(12)) SELECT cXact APPEND BLANK

    The Save button calls the SaveData method (Listing 7) of the form. Listing 7. The SaveData() method code from the Transaction form.

    LOCAL lcXML, loBiz, llRetVal, lcMessage

    CURSORTOXML("cxAct", "lcXML", 1, 0, 0, "1") loBiz = CREATEOBJECT("xActBiz.xActBiz") llRetVal = loBiz.InsertData(lcXML)

    IF llRetVal MESSAGEBOX("Congratulations! Insert succeeded!", 64, "COM+ Save Results") ELSE MESSAGEBOX("Bad news. Insert failed", 48, "COM+ Save Results") ENDIF

    This code is quite easy to follow. First, the cursor is converted to XML to be passed to the business component. Next, the business component, which will be installed in a new COM+ application, is created, then the InsertData method is called. Finally, a message is displayed to indicate if the data was successfully inserted or if it failed.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 25 of 49

    1. Now it s time to create the business component. Create a new project called xActBiz and add a new program file called xActBiz.prg. Listing 8 shows the code.

    Listing 8. This code is used to create the Transaction example business object.

    DEFINE CLASS xActBiz AS Session OLEPUBLIC oMtx = NULL oContext = NULL oContextState = NULL

    PROCEDURE Init * Create a reference to the MTS object This.oMtx = CREATEOBJECT("MTXAS.APPSERVER.1")

    * Create a reference to the Context object This.oContext = This.oMtx.GetObjectContext()

    * Get an interface to the Context state This.oContextState = GETINTERFACE(This.oContext, "iContextState") ENDPROC

    ***********************

    PROCEDURE InsertData(tcData AS String) AS String LOCAL loPubsData, loNWData, lnPubsRetVal, ; lnNWRetVal, llRetVal

    llRetVal = .F. llPubsRetVal = .F. llNorthwindRetVal = .F.

    loPubsData = This.oContext.CreateInstance("xActData.PubsData") loNWData = This.oContext.CreateInstance("xActData.NWData")

    lnPubsRetVal = loPubsData.InsertAuthor(tcData) lnNWRetVal = loNWData.InsertEmployees(tcData)

    IF lnPubsRetVal > 0 AND lnNWRetVal > 0 This.oContextState.SetMyTransactionVote(0) llRetVal = .T. ELSE This.oContextState.SetMyTransactionVote(1) llRetVal = .F. ENDIF

    * Handle activation setting (Done) * .T. = Deactivate, .F. = Leave activated This.oContextState.SetDeactivateOnReturn(.T.)

    RETURN llRetVal ENDPROC

    ********************

    PROCEDURE Error(tnError, tcMethod, tnLine) This.oContextState.SetMyTransactionVote(1) This.oContextState.SetDeactivateOnReturn(.T.)

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 26 of 49

    COMRETURNERROR(tcMethod, "Error: " + ALLTRIM(STR(tnError)) ; + ", Line: " + ALLTRIM(STR(tnLine))) ENDPROC ENDDEFINE

    There are a few things to note about InsertAuthor method. Instead of instantiating the data components with a standard VFP CreateObject(), it uses CreateInstance() method of the Context object. This creates the data objects in the same context as the business component. This is required for the transaction to work correctly. After calling the data components, the status of inserts is checked and the entire transaction is committed or aborted.

    It is also important to note the Error method. Should an error occur, the transaction is aborted and the error returned to the client.

    The business object should be compiled as a Multithreaded-COM DLL. You ll see how to setup the COM+ application after looking at the data components.

    Again, to simplify the example, both the data objects will be created in the same project. In a production environment, each database would be serviced by separate components and each table would have its own class. To start the data component, create a new project called xActData. The code for PubsData.prg is shown in Listing 9. Listing 9. Pubs database transaction example data object code.

    DEFINE CLASS PubsData AS Session OLEPUBLIC oMtx = NULL oContext = NULL oContextState = NULL

    PROCEDURE Init * Create a reference to the MTS object This.oMtx = CREATEOBJECT("MTXAS.APPSERVER.1")

    * Create a reference to the Context object This.oContext = This.oMtx.GetObjectContext()

    * Get an interface to the Context state This.oContextState = GETINTERFACE(This.oContext, "iContextState") ENDPROC

    ***********************

    PROCEDURE InsertAuthor(tcXML AS String, toContextState) AS Boolean LOCAL lnHandle, lcSQL, lnRetVal

    XMLTOCURSOR(tcXML, "cAuthors") lcSQL = "INSERT INTO dbo.authors (au_id, au_lname, au_fname, ; phone, address, city, state, zip, contract) ; VALUES ('999-99-9999', '" ;

    + cAuthors.LName + "', '" + cAuthors.FName + "', '" ;

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 27 of 49

    + cAuthors.Phone + "', '" + cAuthors.Address + "', '" ;

    + cAuthors.City + "', '" + cAuthors.State + "', '" ;

    + cAuthors.Zip + "', 1)"

    lnHandle = SQLSTRINGCONNECT("driver=SQL Server; ;

    + server=(local);database=pubs") lnRetVal = SQLEXEC(lnHandle, lcSQL) SQLDISCONNECT(lnHandle)

    IF lnRetVal > 0 * Handle Transaction Setting (Consistency)

    * 0 = commit, 1 = abort

    This.oContextState.SetMyTransactionVote(0) ELSE This.oContextState.SetMyTransactionVote(1) ENDIF

    USE IN cAuthors RETURN lnRetVal ENDPROC

    *********************

    PROCEDURE Error(tnError, tcMethod, tnLine) IF !ISNULL(This.oContextState) This.oContextState.SetMyTransactionVote(1) ENDIF

    COMRETURNERROR(tcMethod, "Error: " + ALLTRIM(STR(tnError)) ; + ", Line: " + ALLTRIM(STR(tnLine))) ENDPROC ENDDEFINE

    The first thing you ll see in this code is that the COM+ Services and Context objects are instantiated, just as they were in the business component. However, because the data component was instantiated by COM+, this component will run in the same context.

    A SQL INSERT statement is then created and sent to the database via SQL Pass Through. Note that the Au_Id field value, which is the Pimary Key, is hard coded. The transaction voting is then made, based on the success or failure of the INSERT.

    The code for the Northwind database is nearly identical. Create a new program called xActNorthwind and enter the code in Listing 10.

    Listing 10. Northwind database data object code.

    DEFINE CLASS NWData AS Session OLEPUBLIC oMtx = NULL oContext = NULL oContextState = NULL

    PROCEDURE Init * Create a reference to the MTS object This.oMtx = CREATEOBJECT("MTXAS.APPSERVER.1")

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 28 of 49

    * Create a reference to the Context object This.oContext = This.oMtx.GetObjectContext()

    * Get an interface to the Context state This.oContextState = GETINTERFACE(This.oContext, "iContextState") ENDPROC

    ***********************

    PROCEDURE InsertEmployees(tcXML AS String) AS Boolean LOCAL lnHandle, lcSQL, lnRetVal

    XMLTOCURSOR(tcXML, "cEmployees") lcSQL = "INSERT INTO dbo.employees (LastName, FirstName, ;

    + HomePhone, Address, City, Region, PostalCode) ; + VALUES ('cEmployees.LName + "', '" + cEmployees.FName + "', '" ;

    + cEmployees.Phone + "', '" + cEmployees.Address + "', '" ;

    + cEmployees.City + "', '" + cEmployees.State + "', '" ;

    + cEmployees.Zip + "')"

    lnHandle = SQLSTRINGCONNECT("driver=SQL Server; ;

    + server=(local);database=northwind") lnRetVal = SQLEXEC(lnHandle, lcSQL) SQLDISCONNECT(lnHandle)

    IF lnRetVal > 0

    * Handle Transaction Setting (Consistency)

    * 0 = commit, 1 = abort This.oContextState.SetMyTransactionVote(0) ELSE

    This.oContextState.SetMyTransactionVote(1) ENDIF

    USE IN cEmployees RETURN lnRetVal ENDPROC

    *********************

    PROCEDURE Error(tnError, tcMethod, tnLine) IF !ISNULL(This.oContextState) This.oContextState.SetMyTransactionVote(1) ENDIF

    COMRETURNERROR(tcMethod, "Error: " + ALLTRIM(STR(tnError)) ; + ", Line: " + ALLTRIM(STR(tnLine))) ENDPROC ENDDEFINE

    The Primary Key for the Employees table is generated by SQL Server.

    Now that you have the code entered, compile the xActData project as a Multi-Threaded COM DLL. Now you need to create a new COM+ application in the Component Services Manager. Name this application xAction and add the xActBiz and xActData

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 29 of 49

    components to the new application. Notice that there was no code to begin, commit, or rollback the transaction, only code voting on the outcome. The transaction management will be setup in the Component Services Manager and handled by COM+. The following steps show you how to do this.

    1. Right-click on the xActBiz component in the Component Services Manager and select Properties from context menu. The Properties dialog is displayed.

    Select the Transactions tab in the Properties dialog (Figure 14).

    Figure 14. Set transaction management on the Transaction page of the Properties dialog.

    Select Required setting for Transaction support. Click OK to save the changes and close the Properties dialog Repeat this step for the other two components.

    Run the xAct form, enter some data, and click Save. The data is added to the databases. Now, run the form a second time. Because a record with a duplicate au_id is attempted to be added to the Authors table, the entire transaction is rolled back. You can verify this by running a query on both the Authors and Employees tables.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 30 of 49

    Compensating Resource Managers The COM+ transactions that you have seen up until now require a Resource Manager (RM) to handle the transaction. This RM is tightly integrated into the Distributed Transaction Coordinator. It turns out that RMs are difficult to write and not all data stores are supported. For example, what if you want to transactionally handle the deleting of files? There is no RM to support this. Like wise, there is no RM to support Visual FoxPro data.

    The solution to this is a Compensating Resource Manager (CRM). The CRM provides a quick and easy way to integrate application resources into the DTC. However, a CRM does not provide the isolation capabilities of a RM.

    A CRM consists of a pair of COM servers. The first, the CRM Worker, writes the durable log of action to be taken in case of recovery and performs the main action, like inserting a record. The second, the CRM Compensator, is instantiated by the CRM system at the completion of the transaction to handle the commit or abort. The entire process of using a CRM is shown in Figure 15.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 31 of 49

    Figure 15. This figure shows the flow of information in a Compensating Resource Manager.

    The following steps explain what happens when using a CRM. 1. The application instantiates the business object. This business object is running inside COM+. 2. The business object instantiates the CRM Worker. 3. The CRM Worker instantiates the Log Control. The Log Control is part of the COM+ runtime.

    The name of the COM server for the Compensator is passed as a parameter to the Log Controller.

    The business object calls methods of the COM+ Context object to either commit or abort the transaction. The COM+ runtime calls the Log Controller to commit or abort. Because the CRM Compensator is hooked into the Log Controller events, it is automatically called and handles the actual commit or abort.

    The following example code demonstrates a CRM. The example consists of four parts, the UI, a COM+ business objects that calls the CRM worker, the CRM Worker itself, and the CRM Compensator. The example uses the Northwind Customers table that ships with VFP.

    Creating the CRM User Interface Modify the form that you have been using in previous examples and save it as CRM.frx. Figure 16 shows the modified form.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 32 of 49

    Figure 16. The Compensating Resource Manager form.

    You ll notice the Commit and Abort buttons. A real world application would not have these, but they facilitate the example by making it easier to demonstrate those features.

    Now you need to update the code for the form. The Load() method code (see Listing 11), should remain unchanged. Listing 11. The Load event method of the CRM Example form.

    CREATE CURSOR cXact (Fname C(10), LName C(20), Address C(40), ; City C(15), State C(2), Zip C(5), Country C(15), Phone C(12)) SELECT cXact APPEND BLANK

    The Save button simply calls the custom SaveData() method of the form. This code changes a bit from previous code, but it is still simple (see Listing 12). Listing 12. The SaveData method from the CRM Example form.

    LOCAL lcXML, llRetVal

    CURSORTOXML("cxAct", "lcXML", 1, 0, 0, "1") IF TYPE("ThisForm.oBiz") != "O" OR ISNULL(ThisForm.oBiz) ThisForm.oBiz = CREATEOBJECT("CRMBiz.sesBiz") ENDIF

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 33 of 49

    llRetVal = ThisForm.oBiz.InsertData(lcXML)

    This code creates a reference to the CRMBiz object, then calls the InsertData method and passes the XML converted data.

    Once you have created and saved form, you can create the CRM business object.

    The CRM Business Object The CRM business object is similar to previous business objects I have used in this document. It passes the data onto the data object, which in this case is the CRM Worker. Create a new project called CRMBiz and a new program file named CRMBiz.prg. The code in Listing 13 should be added to the new program file.

    Listing 13. The CRM Business object.

    DEFINE CLASS sesBiz AS session OLEPUBLIC

    PROTECTED oMTX, oContext

    oMTX = NULL oContext = NULL

    *********************

    PROTECTED PROCEDURE Cleanup

    This.oContext = null

    This.oMTX = null ENDPROC

    *********************

    PROCEDURE CommitData()

    IF VARTYPE(This.oContext) = "O" This.oContext.SetComplete()

    ENDIF

    This.CleanUp() ENDPROC

    *********************

    PROCEDURE AbortData()

    IF VARTYPE(This.oContext) = "O"

    This.oContext.SetAbort()

    ENDIF

    This.CleanUp() ENDPROC

    *********************

    PROCEDURE Destroy

    This.Cleanup() ENDPROC

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 34 of 49

    *********************

    PROCEDURE InsertData(tcXML)

    LOCAL loWorker, llRetVal

    llRetVal = .F.

    This.oMTX = CREATEOBJECT("MTXAS.APPSERVER.1")

    This.oContext = This.oMTX.GetObjectContext()

    loWorker = CREATEOBJECT("CRMData.sesWorker")

    llRetVal = loWorker.InsertData(tcXML)

    IF !llRetVal

    THIS.AbortData()

    ENDIF

    RETURN llRetVal ENDPROC ENDDEFINE

    The business object code is similar to what you have seen before. The CRM Worker object is created and the data passed to the InsertData() method. Compile this code into a Multi-threaded COM DLL. You will create the COM+ application for after creating the CRM Worker and Compensator.

    The CRM Worker Here s where things start to get complicated, but follow along and you should be fine. Create a new project called CRMData and add the code in Listing 14 as a new program file, CRMWorker.prg.

    Listing 14. The CRM Worker.

    DEFINE CLASS sesWorker AS session OLEPUBLIC

    PROTECTED oCrmLogControl

    oCrmLogControl = ""

    *********************

    PROTECTED PROCEDURE RegisterCRMCompensator LOCAL lcProgIdCompensator, lcDescription, lnErr, llRetVal

    llRetVal = .T.

    lcProgIdCompensator = "CRMData.sesCompensator"

    lcDescription = "Sample VFP CRM"

    IF VARTYPE(This.oCrmLogControl) != "O"

    This.oCrmLogControl = CREATEOBJECTEX("CrmClerk.CrmClerk.1", "", "")

    ENDIF

    TRY

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 35 of 49

    FOR lnI = 1 TO 10

    lnErr = This.oCrmLogControl.RegisterCompensator( ; lcProgIdCompensator, lcDescription, 7)

    ENDFOR

    CATCH TO loErr

    llRetVal = .F.

    FINALLY

    ENDTRY

    RETURN llRetVal ENDPROC

    ****************************

    PROTECTED PROCEDURE RegisterCRMLog(tcXML) LOCAL laLogRecord[1], llRetVal

    llRetVal = .T.

    laLogRecord[1] = tcXML

    TRY

    * Create and write durable log of action to take (create file)

    * Must call - zero based array

    COMARRAY(This.oCrmLogControl, 0)

    * Write out the log record -- array of variants

    This.oCrmLogControl.WriteLogRecordVariants(@laLogRecord)

    * Write out log

    This.oCrmLogControl.ForceLog()

    CATCH

    This.oCrmLogControl.ForceTransactionToAbort()

    llRetVal = .F.

    FINALLY ENDTRY

    RETURN llRetVal ENDPROC

    ****************************

    PROCEDURE InsertData(tcXML)

    LOCAL llRetVal

    llRetVal = .T.

    TRY * Register the CRM Compensator

    This.RegisterCRMCompensator()

    This.RegisterCRMLog(tcXML)

    CATCH TO loErr This.oCrmLogControl.ForceTransactionToAbort()

    llRetVal = .F.

    FINALLY

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 36 of 49

    ENDTRY

    RETURN llRetVal ENDPROC ENDDEFINE

    This code requires some explanation. The InsertData() method first calls the RegisterCompensator() method. The CRM Clerk is a COM+ object that handles the interfacing between the CRM Worker and the CRM Compensator and the durable log. It must be created with CREATEOBJECTEX() because it does not support an IDispatch interface. After the clerk is instantiated, the RegisterCompensator method is called so that it knows what component to use as the CRM Compensator. Three parameters are passed. First, is the ProgID of the CRM Compensator that you will create in the next section. The second parameter is a string that contains the name of what to call the log. This string is used by the monitoring interfaces. The third parameter is the number 7. This tells the clerk that the referenced compensator will be used in all three phases of the CRM transaction. The three phases are prepare, commit, and abort.

    Next, the InsertData() method calls the RegisterCRMLog() method, passing the XML data as a parameter. Think of this step similar to the SQL Server transaction log as it writes out a durable log. The CRM interfaces support passing an array to hold any data you need to get from the Worker to the Compensator. In the example, the array will carry the data. However, it must be passed as a zero-based array, hence the call to COMARRAY(). The array is passed to the log, then the log is written to disk. Do not compile the CRMData project at this time. You will do that after adding the compensator code.

    The CRM Compensator The last piece of code is the CRM compensator. Add a new program file, CRMComp.prg (see) to the CRMData project. Listing 15. The CRM Compensator

    EXTERNAL ARRAY pLogRecord DEFINE CLASS sesCompensator AS session OLEPUBLIC PROTECTED oCrmLogControl

    oCrmLogControl = NULL

    IMPLEMENTS ICrmCompensatorVariants IN "comsvcs.dll"

    PROCEDURE ICrmCompensatorVariants_SetLogControlVariants(pLogControl ; AS VARIANT) AS VOID ; HELPSTRING "method SetLogControlVariants"

    LOCAL lcTransactionUOW This.oCrmLogControl = pLogControl ENDPROC

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 37 of 49

    ***************************

    PROCEDURE ICrmCompensatorVariants_PrepareRecordVariants(pLogRecord ; AS VARIANT) AS LOGICAL ; HELPSTRING "method PrepareRecordVariants"

    * Setting This to .T. causes CommitRecordVariants not to get called RETURN .F. ENDPROC

    ***************************

    PROCEDURE ICrmCompensatorVariants_BeginPrepareVariants() AS VOID;

    HELPSTRING "method BeginPrepareVariants" ENDPROC

    ***************************

    PROCEDURE ICrmCompensatorVariants_EndPrepareVariants() AS LOGICAL; HELPSTRING "method EndPrepareVariants"

    * Vote on the outcome of the transaction. Return .F. to rollback RETURN .T. ENDPROC

    ***************************

    PROCEDURE ICrmCompensatorVariants_AbortRecordVariants(pLogRecord ; AS VARIANT) AS LOGICAL ;

    HELPSTRING "method AbortRecordVariants"

    * Code for aborting the transaction goes here RETURN .T. ENDPROC

    ***************************

    PROCEDURE ICrmCompensatorVariants_CommitRecordVariants(pLogRecord ; AS VARIANT) AS LOGICAL;

    HELPSTRING "method CommitRecordVariants"

    LOCAL llRetVal

    llRetVal = .T.

    TRY

    XMLTOCURSOR(pLogRecord[1], "cCustomer")

    SET EXCLUSIVE OFF

    USE \complus\Data\Customers IN 0

    INSERT INTO Customers (CustomerID, CompanyName, ContactName, ; Address, City, Region, PostalCode, Country, Phone) ;

    VALUES (cCustomer.CustomerID, cCustomer.CompName,

    cCustomer.Contact, cCustomer.Address, cCustomer.City, ; cCustomer.State, cCustomer.Zip, ;

    cCustomer.Country, cCustomer.Phone)

    CATCH TO loErr llRetVal = .F.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 38 of 49

    FINALLY CLOSE DATABASES ALL ENDTRY RETURN llRetVal ENDPROC

    ***************************

    PROCEDURE ICrmCompensatorVariants_BeginCommitVariants(bRecovery ; AS LOGICAL) AS VOID ; HELPSTRING "method BeginCommitVariants" ENDPROC

    ***************************

    PROCEDURE ICrmCompensatorVariants_EndCommitVariants() AS VOID;

    HELPSTRING "method EndCommitVariants" ENDPROC

    ***************************

    PROCEDURE ICrmCompensatorVariants_BeginAbortVariants(bRecovery ; AS LOGICAL) AS VOID;

    HELPSTRING "method BeginAbortVariants" ENDPROC

    ***************************

    PROCEDURE ICrmCompensatorVariants_EndAbortVariants() AS VOID;

    HELPSTRING "method EndAbortVariants" ENDPROC ENDDEFINE

    As you see, this code implements the ICompensatorVariants interface in COMSvcs.dll. There are three methods of interest here.

    The first is ICrmCompensatorVariants_AbortRecordVariants(). The return value from this method determines whether other databases in the transaction abort or commit.

    The second, ICrmCompensatorVariants_AbortRecordVariants() is where you place code to run when an abort occurs. In my example, there is nothing to do, so no code is present.

    Finally, ICrmCompensatorVariants_CommitRecordVariants() is where the action is. The code gets the XML data from the array that was passed in from the CRM Worker. The XML is converted to a cursor, then used to update the Customers table.

    Save the code and compile the CRMData project as a Multi-threaded COM DLL.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 39 of 49

    Setting up the CRM in COM+ Now that you have the components, you still need to set them up in COM+ and test them. The following steps walk you through doing this.

    1. Create a new COM+ application called VFPCRM. 2. Add both CRMBiz.dll and CRMData.dll as new components in the application. 3. On the Advanced tab (see Figure 17) of the properties dialog for VFPCRM application, check

    Enable compensating resource managers.

    Figure 17. Turn on CRMs on the Advanced tab of the application's properties dialog.

    On the Transactions tab of the properties dialog for both the business component and the CRM Worker component, select Required. Configure the Compensator component. On the Transaction page of the Properties dialog, make sure Not supported is selected. Select the Activation page and uncheck Enable Just In Time Activation. Select the Concurrency page and select Not supported. Run the CRM form, enter some data, then click Save and Commit. The data is saved to the Customers table. Try it again, but this time click Save and Abort. No data is written to the table.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 40 of 49

    This example only used one table, but it should be pretty simple to add additional tables and even UPDATE and DELETE commands.

    Queued Components In the examples I have shown you thus far, you have seen how to get synchronous results back from a COM+ component. However, there are times when you may need to use asynchronous components. COM+ provides two methods for this. The first, Queued Components is presented in this section. I present the second, Loosely Coupled Events, in the next section.

    Queued Components provide several advantages to application design. For example, by using Queued Components, you can minimize the effects of a workstation or server being disconnected from the network or you can use them to balance the processing needs across the day.

    Queued Components are often called Messaging Components because messages are sent to and read from a queue.

    To better understand this, look at Figure 18, which shows a typical synchronous application. In the diagram, the workstation calls the Orders component, which writes the Orders database and then in turn calls the Shipping component, that writes to the Shipping database. The entire process is rolled up into a single transaction. But, what happens if the Order component or database can t be accessed? In this case, the order can t be taken because the transaction will fail.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 41 of 49

    Figure 18. This figure shows how a typical application wraps all updates in a single transaction.

    Compare this to Figure 19, where Queued Components are used. You will notice that several transactions are involved. What happens is the workstation writes messages intended for the Orders component into a queue. When the Orders component becomes available, it picks up the message, begins a transaction, and writes the data to the Orders database. It also writes to another queue that is read by the Shipping component to update the Shipping database. Each queue is also transactional, assuring delivery of the messages, even when a component is not available.

    Figure 19. Queued Components use multiple transactions to ensure data is delivered.

    As another example, image that the company needs to ensure enough computing power to handle peak order times from 9:00 AM to Noon. By using Queued Components, the Shipping processes will not run during those times, but will be started when the peak order hours are over.

    You may be wondering what the yellow bars are in Figure 19. A closer look at what is happening is needed. Instead of talking directly to the component, the application talks to a recorder (Figure 20). When the Orders component activates, a listener hears that a message has been recorded and passes it on to a player. The player plays the message to the component. The application thinks it has been talking directly to the component and the component assumes it has been talking directly with the application. The COM+ infrastructure supplies the recorder, listener, and player. The communication from the Order component to the Shipping component happens exactly the same way.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 42 of 49

    Figure 20. A close-up view of how Queued Components work.

    Because Queued Components use Microsoft Message Queue (MSMQ) under the hood, you need to make sure you have MSMQ running. The following steps show how to do this.

    1. Right click on My Computer on the Start menu and select Manage. The Computer Management snap-in for the Microsoft Management Console (MMC) is displayed (Figure 21). Expand Services and Applications node.

    2. Verify that Message Queuing is listed. If Message Queuing is not listed, you ll need to add it from Add/Remove Programs in Windows

    Control Panel.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 43 of 49

    Figure 21. Message Queuing is managed in the Computer Management snap-in.

    This example will use the form from the earlier discussion on Compensating Resource Managers, so it will look like Figure XX. The difference will be the code in the SaveData method of the form. Use the following steps to make changes to the form.

    1. In the command window, type MODIFY FORM CRM. The Form Designer is opened. Select File > Save As from the menu. In the Save As dialog, enter QC.FRM and click Save Delete all the code from the SaveData method of the QC form and replace it with the code in Listing 16.

    Listing 16. The SaveData() method of the Queued Component form.

    LOCAL lcXML, loCatalog, loBiz

    CURSORTOXML("cXact", "lcXML", 1, 0, 0, "1")

    loCatalog = CREATEOBJECT("COMAdmin.COMAdminCatalog") loCatalog.Connect("") loCatalog.StartApplication("QC1") loBiz = GetObject("queue:/new:FoxQC.sesQC")

    loBiz.InsertData(lcXML)

    loCatalog.ShutdownApplication("QC1")

    Close and save changes to the form.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 44 of 49

    The following steps walk you through creating the component. 1. Create a new project called FoxQC.

    Add a new program to the project. Use the code in Listing 17.

    Listing 17. This code is used to create a Queued Component.

    DEFINE CLASS sesQC AS Session OLEPUBLIC PROCEDURE InsertData(tcXML) XMLTOCURSOR(tcXML, "cCustomer")

    SET EXCLUSIVE OFF USE Data\Customers IN 0 SHARED INSERT INTO Customers (CustomerID, CompanyName, ContactName, Address, ; City, Region, PostalCode, Country, Phone) ; VALUES (cCustomer.CustomerID, cCustomer.CompName, cCustomer.Contact, ;

    cCustomer.Address, cCustomer.City, cCustomer.State, cCustomer.Zip, ; cCustomer.Country, cCustomer.Phone) CLOSE DATABASES ALL ENDPROC ENDDEFINE

    Save the code as QC.prg. Compile the project as a Multi-threaded COM DLL.

    All that is left now is to configure COM+ and run the application. As with previous examples, COM+ security will be turned off to simplify the example.

    1. Create a new COM+ application called QC. Note this is the same name used the application in the SaveData method of the QC form.

    Add the FoxQC.dll component to the application. Right-click on the QC application and select Properties. The Properties dialog is displayed. Select the Security tab. Make sure Enforce access checks for this application is not checked. Change the Authentication level for Calls drop down is set to None. On the Queuing tab, make sure the checkboxes Queued This application can be reached by MSMQ queues and Listen This application when activated will process messages that arrive on its MSMQ queue are both checked.

    As you can imagine, there are drawbacks to Queued Components. For example, all parameters to the component s methods are input only. You can t expect a return value because you don t know when the component will actually run or the requestor could terminate before the server processing completes. Not only do you need to handle this situation, but you need to be able to handle errors that can occur. There are three ways you can do this:

    Pessimistic

    This is the same methods you ve seen previously, using synchronous components.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 45 of 49

    Optimistic You trust the infrastructure and assume everything works. I don t recommend this method.

    Less Paranoid Using this method, you generate messages selectively using exception protocols. In other words, send the exceptions to a log for someone to handle.

    Now it s time too look at the other way to get asynchronous communication in COM+, Loosely Coupled Events.

    Loosely Coupled Events Like Queued Components, Loosely Coupled Events allow asynchronous calls. The same issues of getting a message back exist. The big difference is that Loosely Coupled Events use a Publish and Subscribe method where one component publishes events and one or more components subscribe to those events. The COM+ infrastructure handles the publish and subscribe processes and delivers the event messages from the publisher to the subscriber. There are two types of subscriptions, persistent and transient.

    Under persistent subscriptions, administration is handled through the Component Services Manager. Subscribers are not created until the event is published and a single published event can cause multiple subscribers to respond. You can also have filtering conditions so that a subscriber is only sent events that meet specific criteria. Additionally, persistent subscriptions will survive system shutdown.

    With transient subscriptions, setup of the subscriber must be done through the COM+ administration objects. The subscriber is activated and in memory, waiting for the event. Transient subscriptions have better performance than persistent subscriptions, but will not survive system shutdown. Additionally, you can have only one subscriber per event. I think that transient subscriptions are not as useful because they aren t persistent. Because of this, I do not address transient subscriptions in this article.

    Persistent Subscriptions The following example shows how to use persistent subscriptions. It consists of the same form that s been used before and two MTDLLs, a publisher and a subscriber. The Load() event of the form is the same that you ve seen in the previous examples. Listing 18 shows the SaveData() method. Listing 18.

    LOCAL lcXML, loPublisher

    CURSORTOXML("cxAct", "lcXML", 1, 0, 0, "1") loPublisher = CREATEOBJECT("PersistPub.sesPublisher") loPublisher.InsertData(lcXML) loPublisher = NULL

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 46 of 49

    The publisher code is simple. Because it s an interface into the subscriber, it contains only stubs for the same methods in the subscriber. Create a new project, Persistpub.pjx, and a new program, Persistpub.prg that contains the code in Listing 19, then compile it as a MTDLL.

    Listing 19.

    DEFINE CLASS sesPublisher AS Session OLEPUBLIC PROCEDURE InsertData(tcData AS String) ENDPROC ENDDEFINE

    All the functionality is in the subscriber code. Create a new project named Persistsub.pjx and a new program named Persistsub.prg. Add the code in Listing 20, then compile the project as a MTDLL. Listing 20.

    DEFINE CLASS sesSubscriber AS Session OLEPUBLIC IMPLEMENTS IsesPublisher IN PersistPub.sesPublisher

    PROCEDURE IsesPublisher_InsertData(tcData AS String) XMLTOCURSOR(tcData, "cCustomer")

    SET EXCLUSIVE OFF

    USE \complus\Data\Customers IN 0

    INSERT INTO Customers (CustomerID, CompanyName, ContactName, Address, ;

    City, Region, PostalCode, Country, Phone) ;

    VALUES (cCustomer.CustomerID, cCustomer.CompName, cCustomer.Contact, ;

    cCustomer.Address, cCustomer.City, cCustomer.State, cCustomer.Zip, ;

    cCustomer.Country, cCustomer.Phone)

    CLOSE DATABASES ALL ENDPROC ENDDEFINE

    This code implements the interface from the publisher. You could add additional subscribers to update other databases or systems. Just create another project and program, that also implements the subscriber interface. If you need additional methods, don t forget to add them to both the publisher and subscriber. All that s left now is to setup the events and test the application. The following steps show you how to setup the COM+ event system.

    1. Create a new COM+ application named PersistPub. Add a new component to the application, but do not select Install new component(s) on the first screen of the wizard (Figure 22). Instead, select Install new event class(es). A file picker is displayed.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 47 of 49

    Figure 22. When installing an event class, use Install new event on the Component Install Wizard.

    Select Persistpub.dll and click Open. The Install new components page is displayed. Click Next, then Finish. Create a new COM+ application named PersistSub. Add Persistsub.dll as a new component to the application. Drill down to the Persistsub.sesSubscriber component in the PersistPub application to reveal the Subscriptions node. Right-click on Subscriptions and select New Subscription from the context menu. Click Next on the welcome screen of the Wizard. The Select Subscription Method(s) page (Figure 23) is displayed.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 48 of 49

    Figure 23. This page is used to select the proper interfaces of an event class.

    Check Use all interfaces for this component (Figure 23). Click Next. The Select Event Class page (Figure 24) is displayed. Select the persistpub.sesPublisher interface ID in the list box and click Next. The Subscription Options page (Figure 25) is displayed.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 49 of 49

    Figure 24. Options such as name and when to enable the subscription are entered in the New Subscription Wizard.

    Enter the name for the subscription and check Enable this subscription immediately then click Next and Finish. Run the Persist form and test the components.

    There are some interesting options available for persistent events. Figure 25 shows the Options page of the Properties dialog for the Subscription VFP Persistent Events. The most interesting one is the Filter criteria class. You can use the filter to have multiple subscribers, each handling specific needs.

  • Using Windows Component Services (COM+) with Visual FoxPro

    Craig Berntson, 2005 Page 50 of 49

    Figure 25. Optional subscription options are entered on the Options page of the Properties dialog.

    Summary Windows supplies many services that make your applications more robust and easier to design, develop, manage, and configure. Features such as security, transactions, queued components, and loosely coupled events can be used to ensure that only the proper users enter data that gets delivered to the database. While the future is .Net-based solutions using Indigo, it is merely a wrapper around COM+ and other Windows services. That means that COM+ will be here for many years to come.

    Craig Berntson is a Microsoft Most Valuable Professional (MVP) for Visual FoxPro, a Microsoft Certified Solution Developer, and President of the Salt Lake City Fox User Group. He is the author of CrysDev: A Developer s Guide to Integrating Crystal Reports , available from Hentzenwerke Publishing. He has also written for FoxTalk and the Visual FoxPro User Group (VFUG) newsletter. He has spoken at Advisor DevCon, Essential Fox, DevEssentials, the Great Lakes Great Database Workshop, Southwest Fox, DevTeach, FoxCon, Microsoft DevDays and user groups around the country. Currently, Craig is a Senior Software Engineer at 3M Health Information Systems in Salt Lake City. You can reach him at [email protected],, www.craigberntson.com, or his blog at www.craigberntson.com/blog.

  • This document was created with Win2PDF available at http://www.daneprairie.com.The unregistered version of Win2PDF is for evaluation or non-commercial use only.