Concurrency

The example schedules developed in this section are simple, yet these schedules show not only the use of Fork and Join shapes, but also some useful debugging and monitoring techniques. The steps to build and run these examples are described in detail; however, for examples in later sections, the basic steps or techniques covered here are skipped.

Although the examples in this section are designed to demonstrate the use of the OR and the AND Join shapes, the examples also serve to illustrate the following important points:

  • Developing and using COM+ configured components in a schedule

  • Using a COM component with No Instantiation option and verifying the component's behavior in a running schedule

  • Using the BizTalk Orchestration Engine object model

  • Building the business process, binding the port implementation, and constructing the data page associated with an XLANG schedule

We will construct two schedules and describe a third one briefly. The first schedule features the AND Join shape; the second one modifies this schedule to use the OR Join shape. The third schedule is an extension of the second which demonstrates how the ambiguity of execution paths can be resolved when an OR Join is used.

Using the AND Join Shape

We will first construct a simple schedule containing two actions that may be performed concurrently; however, in this example, both actions must complete before the schedule can complete its task. In the section “Using the OR Join Shape” later in the chapter, you will see a schedule that completes when either of the actions completes. The use of the Fork and Join shapes allows you to construct a business process with concurrently executing pathways. See Chapter 9 for the concepts behind using these shapes. The schedule also uses a COM component, called AdvOrchUtil.CMsg. This component has a single method to display a message box.

The section describes the schedule, provides detailed construction help, and finally describes how it is run and monitored. The schedule, after it is constructed, will look as shown in Figure 13.1. The schedule is called Concur1.skv.

Figure 13.1. Example for AND Join: business process for Concur1.skv.


Figure 13.1 shows that Action 1, through Port_1, is calling a method ShowMsgBox on an instance of AdvOrchUtil.CMsg class. The Action 2 is also calling the same method but over a different port and hence is using another instance of the class. This is necessary to keep the parallel process flows isolated from each other. For this example, the methods simply display a message dialog box.

Later in this section, we will run the schedule, and you will see the two messages displayed. When the schedule runs, actions defined in the business process are performed. An action results in sending or receiving a message via a port connected to an implementation, in this case a COM component (see Chapter 10). The name of the message used is shown in the port. The contents of the message are determined by the Data page. This page looks as shown in Figure 13.2.

Figure 13.2. Example for AND Join: Data page for Concur1.skv.


Figure 13.2 shows that fields of the messages are populated by port references, and the message title is provided by a constant string. Port references are simply used as display strings here; their appropriate usage is described in the section “Dynamic Ports” later in this chapter. Now that we've described the schedule, let's start building it.

Creating the Concur1 XLANG Schedule

To construct this example, we can start by constructing the business process flow. Make sure that the Flowchart stencil is visible (it is not shown in Figure 13.1). Drag the Fork shape from the stencil onto the drawing surface to the left of the separator bar and place it under the Begin shape. Next drag two oval Action shapes onto the drawing surface and place them as shown. Finally, drag the Join shape and the End shape and place them as shown.

Use the connector tool to join these shapes as shown in Figure 13.1 (the connector tool is not shown in the figure; see Chapter 10). When you select the connector tool, the pointer icon changes to include the connector graphic under the arrow. As you move the arrow over the shapes, you will notice that a red rectangle pops up when the arrow is near a connection point. When you see this rectangle, you can click the mouse and drag the arrow to the destination connection point. At the destination, you see the red rectangle pop up again, indicating that it may be possible to terminate the connection there. Using this connector tool repeatedly, you can quickly complete the connections joining the flowchart shapes. These connections are depicted by heavy black arrows in Figure 13.1.

At this point, you can optionally save the interim XLANG schedule drawing as Concur1.skv.

Creating the AdvOrchUtil.CMsg Component

The next phase is to construct the AdvOrchUtil.CMsg class. To do this, we start Microsoft Visual Basic 6.0. Refer to a book on developing COM components using Visual Basic 6.0 or refer to the Visual Basic 6.0 product documentation. You do not have to use Visual Basic 6.0; you may use any other tool to create COM components—for example, the section “Dynamic Ports” later in the chapter has a sample on creating a script component.

In this example, we want to create an ActiveX DLL. After starting VB6 and choosing ActiveX DLL as the project type, rename the project AdvOrchUtil and the default class created by the project wizard CMsg. Currently for this class, we will go along with the default properties; however, we will modify the MTSTransactionMode to be NoTransactions. Listing 13.1 shows the full code listing for the class; however, let's discuss the design first.

The AdvOrchUtil.CMsg class has a single method called ShowMsgBox. This method takes two arguments, which it uses to show as the message and title of a message box. Another design goal for the class is to facilitate monitoring and testing the class.

Middle-tier components, such as this class, are notoriously difficult to test. Microsoft Visual Basic 6.0 provides a good emulation for COM+ services, yet there are situations when testing or monitoring requires a less intrusive approach. To keep tabs on the code execution of your components, you can generate traces to the application event log, or to a file or to the system debug output. Visual Basic 6.0 provides an easy mechanism of logging events to the application event log using the App.LogEvent call, although it does have some limitations compared to using the Win32 API directly. Here we will use a simple approach to post messages to the system debug output. To reiterate, do use the native debugging facilities of Visual Basic 6.0 IDE; however, we do not discuss the IDE here.

We will use the Debug Monitor utility available as DbMon.exe, and it comes with the Microsoft Platform SDK. The utility is also available with the other visual developer tools, such as Visual C++, and as code sample. The Platform SDK can be downloaded from the Microsoft developer Web site, although it is a large download.

The Debug Monitor runs in its own console window and displays messages sent to the system debug output via the Win32 function OutputDebugString. See additional documentation available on the Microsoft Developer Network (MSDN) about this system call and the DbMon utility.

Without further ado, Listing 13.1 shows the entire AdvOrchUtil.CMsg class.

Listing 13.1. The AdvOrchUtil.CMsg Class
Option Explicit

Private Declare Sub OutputDebugString Lib "kernel32" Alias "OutputDebugStringA" (ByVal
 lpOutputString As String)

Private Sub Trace(sMsg As String)
    OutputDebugString "AdvOrchUtil.CMsg: " & sMsg & vbCrLf
End Sub

Sub ShowMsgBox(ByRef sMsg As Variant, ByRef sTitle As Variant)
    Trace "ShowMsgBox(): Msg=" & CStr(sMsg) & " Title=" & CStr(sTitle)
    MsgBox CStr(sMsg), vbOKOnly, CStr(sTitle)
End Sub

Private Sub Class_Initialize()
    Trace "Class_Initialize()"
End Sub

Private Sub Class_Terminate()
    Trace "Class_Terminate()"
End Sub

At the head of Listing 13.1, there is a declaration for the Win32 call OutputDebugString—we are using the ANSI version for this example. The next procedure is used to generate the trace statements using the OutputDebugString API call. We prefix all strings with the class name so that the trace output can be properly interpreted. The sole public method of the class is ShowMsgBox, which takes two arguments by reference. We are using variants for testing so that numbers and other data types could also be shown if necessary. The next two procedures are private to the class, and these are called when the class is constructed or destroyed. We add trace statements to all the procedures so we can keep an eye on what is going on.

Now you are ready to save and compile the project. Figure 13.3 shows the project properties.

Figure 13.3. AdvOrchUtil project properties.


Using the Visual Basic 6.0 IDE, open the project properties dialog box by using the Project menu and selecting the AdvOrchUtil Properties. In the project properties dialog box, under the General tab shown in Figure 13.3, there are two check boxes of interest. For our testing, we will choose the defaults; however, consider checking off Unattended Execution and Retained In Memory for your production code. On the Component tab, compile the project with Binary Compatibility. You will need a reference type library or DLL, so first compile your project with No Compatibility and then switch to binary compatibility using the compiled DLL. The Compile tab has options to Create Symbolic Debug Info and to remove any optimization by checking off No Optimization. You will need to use these options if you choose to use debuggers other than the one integrated with the Visual Basic IDE.

Note

If you build your own AdvOrchUtil.CMsg and you download the sample from the Sams Web site, the type libraries for the two DLLs will be different. The sample schedule on the Web site will contain a reference to the downloaded DLL, not to your DLL. You have at least three options to avoid conflicts:

  • Build the entire example all by yourself.

  • Use the downloaded DLL as a reference type library to build a compatible DLL.

  • Repair the schedule to use your component.

To do this, you must rerun the COM Component Binding Wizard and pick the correct component. You can verify the type library by using the menu Tools and Refresh Method Signatures in the BizTalk Orchestration Designer.


Configuring AdvOrchUtil.CMsg in COM+

After compiling the class, you could use it directly in the XLANG schedules; however, we will install the class in a COM+ application. For this example, we are not using any specific COM+ services; however we will be able to see the instance activations when the schedule runs. Therefore, create a new COM+ application using the Component Services administrative console. Create an empty server application by right-clicking on the COM+ Applications node of the console tree and starting the COM Application Install Wizard. You will reach this node by expanding, in turn, Component Services, Computers, and My Computer nodes. You may call this application TestCOM+ App. The application created is a server application with interactive user as its identity. See Chapter 10 for more background on COM+ and how it relates to BizTalk Orchestration.

Next, locate the TestCOM+ App under COM+ Applications. Expand this node and locate the Components node. You can install the AdvOrchUtil.CMsg class into COM+ in one of two ways. You can drag and drop the AdvOrchUtil.dll on the right pane of the administrative console, with the Components node selected on the tree in the left pane. Alternatively, you can right-click on the Components node and select the New Component menu item. Do this, and we will proceed to complete the XLANG schedule. Figure 13.4 later in the chapter shows the COM+ administrative console.

Figure 13.4. Component Services: TestCOM+ App Status view.


Using AdvOrchUtil.CMsg in the Schedule

Now go back to, or reopen the Concur1.skv schedule you were working on. Make sure that the Implementation stencil is now visible. Drag and drop the COM Component shape onto the drawing surface to the right of the separator bar and align it approximately with the elevation of Action 1. The COM Component Binding Wizard starts.

On the first page of the wizard, the default name of Port_1 is fine. On the second page of the wizard, choose the Static instantiation option. On the third page, you have to locate the AdvOrchUtil class. Expand the tree node; locate and select the CMsg class. The class has only a single interface with a single method and hence the wizard skips to the sixth page where you can choose the Advanced Port Properties. The defaults are fine for now. (See Chapter 10 for details on the wizard.) Repeat this process for Port_2; see Figure 13.1 for port placement.

Now, with the connector tool, connect Action 1 to the Port_1. The Method Communication Wizard starts. On the first page, choose Initiate a Synchronous Method Call. You will be creating a new message based on the name of the only method of our class; the wizard therefore skips the second page. The last page shows the message and the fields that will be created on the Data page. Repeat this process by connecting Action 2 and Port_2.

Now shift your attention to the Data page, shown previously in Figure 13.2. There are two message pairs, corresponding to the two method calls, and a message each for Port References and Constants.

Double-click the Constants message. You see a dialog box showing Constants Message Properties. Click the Add button to add a new constant. Name the constant Action1, select the type as string (it is the default data type), and give it the value of “Action 1.” Do the same for Action2 and give the value of “Action 2.” We use these strings for message box titles. The text message for the message box is taken from the Port References. As a side benefit of this example, we see the COM moniker strings that represent the two ports in our schedule. See Chapter 10 for the description of the wizards in the designer and on monikers used with BizTalk Orchestration.

Using the connector tool, connect the fields as shown previously in Figure 13.2. You are now ready to compile the schedule.

Tip

When developing schedules, out of habit, I also use the Tools menu to remove any unused messages or rules and check whether the type information for any COM components has changed. Most importantly, however, I also use the Shutdown All Running XLANG Schedule Instances menu item. This is equivalent to safely shutting down schedules using the button on the XLANG tab of the property pages of the XLANG Scheduler COM+ application—the application that hosts the XLANG engine. This action removes any cached instances from the XLANG engine's database, which ensures that your newly compiled schedule runs rather than a stale cached version.


Compiling and Running the Schedule

To compile the schedule, choose File, Make XLANG Concur1.skx from the menu. Now we are ready to run the schedule.

Before running the schedule, get your tools in place. Start the DbMon, the XLANG Event Monitor, and the Component Services console, which should look as shown in Figure 13.4. The XLANG Event Monitor is described in Chapter 11, “XLANG Orchestration Engine.”

Figure 13.4 shows the Status view for the components in the TestCOM+ App application. There is only one component there, and the column headings are for the runtime, dynamic metrics for this component.

Run the schedule using the Instance and Run menu of the XLANG Event Monitor. First select the group manager in which you want to run the schedule and then, in the dialog box shown by the Run menu, type in the full path to the Concur1.skx schedule.

Note

The COM+ application TestCOM+ App is not a group manager, although you could configure it to be such. The sample output and the figures in this chapter demonstrate the use of a group manager named XLANG Two, which is not the default group manager. Components used by a schedule do not have to run in the same COM+ application. Chapter 11 describes the steps to create a new group manager.

An important point to recall from Chapter 11 is that for a new COM+ application to function as a group manager, you must not only create the persistence database, but you must also install at least one COM component, even if it serves no purpose.


When you run the schedule, you should see something like what is shown in Figure 13.5.

Figure 13.5. Running Concur1.skx.


Figure 13.5 shows four windows in all. The first is the XLANG Event Monitor, followed by the two dialog message boxes displayed by the AdvOrchUtil.CMsg class, and finally the MMC console. The title in the dialog message box shows the action that is initiating the activity. The content of the message box is the port moniker for the port displaying the message. The port moniker is constructed using the instance ID of the running schedule. The XLANG Event Monitor shows the Concur1 schedule running and the instance ID of that schedule. The Component Services console shows that two objects are constructed and activated and are within a method call. The method call time is atrocious because the dialog boxes are holding up the completion of the schedule. The sphere next to the AdvOrchUtil.CMsg component should also appear to be rotating—you will see the plus indentation on the sphere cyclically change.

If you double-click the Concur1 running schedule instance in XLANG Event Monitor, you will see all the events for the schedule so far. In Figure 13.6, shows the display turned on for all the recorded events; this can be done from the View and Events Filter menus.

Figure 13.6. Events for Concur1.


Figure 13.6 shows, as expected, that both concurrent actions were initiated in the schedule. The middle column in the top pane names the actions executed; the right column shows the messages passed. The details for the highlighted event are shown in the lower pane. The event shown is that for a COM method call initiated by the XLANG schedule instance. Because both calls are blocked by the message box, the schedule has not progressed further, nor can it dehydrate. Note that the instance ID shown is the same as in Figure 13.5 earlier.

At this point, you can choose to acknowledge either of the message boxes. In the XLANG Event Monitor, you will notice that even though one action completes, the schedule still waits for the other action to complete. This is expected with the AND Join. When the remaining action is acknowledged, the schedule winds down and completes.

In the DbMon window, you will see something like Listing 13.2.

Listing 13.2. DbMon Output for Concur1.skx
2188: AdvOrchUtil.CMsg: Class_Initialize()
2188: AdvOrchUtil.CMsg: Class_Initialize()
2188: AdvOrchUtil.CMsg: ShowMsgBox(): Msg=sked://bztk.bztkdom.omicron.com !XLANG Two
/{763573CE-CD65-4B86-9AA5-76A2E53C6D2B}/Port_1 Title=Action1
2188: AdvOrchUtil.CMsg: ShowMsgBox(): Msg=sked://bztk.bztkdom.omicron.com !XLANG Two
/{763573CE-CD65-4B86-9AA5-76A2E53C6D2B}/Port_2 Title=Action2
2188: AdvOrchUtil.CMsg: Class_Terminate()
2188: AdvOrchUtil.CMsg: Class_Terminate()

The output in Listing 13.2 shows that two different objects were constructed, as expected. The traces for the method calls show the port reference from each of the actions. Careful observers will notice that the GUID is different in Listing 13.2 and in Figure 13.5. This is true; this trace is from another run of the schedule. The number on the left is the process ID of the TestCOM+ App that generated the trace.

This concludes the first example. You should now have practical experience in creating a schedule, compiling, running, monitoring, and testing a schedule. You could also use the application event log, performance monitor and other system tools; however, we did not cover these here.

In the example, we used the AND Join and verified the behavior of the schedule. Though we used a single action in the concurrent pathways, this is not a necessity. We could have had a sequence of actions in the concurrent pathways, both incoming and outgoing. We develop this example next.

Using the OR Join Shape

Now we look at using the OR Join shape. The OR Join shape is used to synchronize concurrent business processes; see Chapter 9 for additional information. The example also demonstrates the use of the runtime object model used by the XLANG engine and use of the No Instantiation option for COM ports. You will also see a schedule dehydrating and rehydrating. Recall from Chapter 9 that when using the OR Join, there can be at most a single action in the concurrent pathway and that this action must be a “receive message” action. We will build a schedule that meets these requirements.

We can reuse Concur1.skv from the previous example along with the AdvOrchUtil.CMsg component developed for that schedule. This allows us to devote more time on the features of BizTalk Orchestration than on ancillary development. If you skipped the previous section, you should at least configure AdvOrchUtil.CMsg in COM+ so that it is properly registered. Save the Concur1.skv file as Concur2.skv and open it for editing. The next section describes the steps necessary to modify this schedule to use the OR Join. In the end, the schedule should look as shown in Figure 13.7

Figure 13.7. Concur2.skv business process.


Modifying Concur2.skv to Use the OR Join

Perform the following changes to Concur2.skv. Double-click on the top COM port implementation and start the COM Component Binding Wizard. On the second page, select No Instantiation. On the third page, select the _CMsg interface exposed by the type library.

Next double-click the message communication between Action 1 and Port_1. The Method Communication Wizard starts. On the first page, switch to Wait for a Synchronous Method Call. In the text box below, enter a number greater than 180, say 200, denoting the time in seconds that the schedule can expect to wait for a message to arrive—in this case, an incoming method call. We will write a standalone Visual Basic application to make a call using this port. The schedule could be started and wait forever for the call to come in. By choosing a duration greater than 180 seconds, we are signaling the XLANG engine that it is okay to dehydrate the schedule and save system resources. Repeat this for the message communication between Action 2 and Port_2.

Double-click on the AND Join shape; you see the property setting for the Join. Change the Join type to OR. Note the comment displayed when you make the change: such a Join can only be used in Fork flows containing a single receive action. The schedule is already modified to do this.

On the Data page, remove the links connecting the Port References message to other messages. If you forget to do this, you will get compilation errors. Note that we changed the schedule to receive messages. Even if you make connections to outgoing fields, the XLANG engine makes no attempt to transport the field values out from within the schedule.

Save and compile the schedule. You can start the XLANG Event Monitor and use it to run the schedule. You will notice that the schedule starts up and then a few seconds later gets dehydrated. This is as it should be. The schedule will now be around forever. You can convince yourself that this is so by rebooting the machine, shutting down the System Application or XLANG Scheduler COM+ applications by brute force, or inducing other failures you can think of. This is an important feature of XLANG schedules; they represent long-running business processes that are protected from system failures.

If you open the XLANG tab of the XLANG Scheduler COM+ application's property page, you see two buttons: one for the controlled shutdown of all XLANG applications, and the other for restart of dehydrated XLANG applications. See Chapter 11 for additional information on this. You can use the controlled shutdown to allow the XLANG engine to save any working state information before stopping the process. However, you can ensure proper failure handling by incorporating transactions in the schedule. Examples for both short-lived and long-lived transactions are developed in following sections. When you click the other button to restart dehydrated schedules, you will notice in the XLANG Event Monitor that the schedule briefly becomes active (the icon changes from a blue snowflake to a green circle).

At this point, let the started Concur2.skx schedule lay dehydrated as we work out an application to call methods on its ports. In fact, start another instance; we will use it later in the example. At this point, you should have two instances of Concur2.skx running, each waiting for a message to arrive. The message arrives only when a call is made into the ports bound to a COM implementation. Until that time, the schedules remain dehydrated. This is also an important feature of BizTalk Orchestration; such quiescent schedules consume only minimal system resources as they wait for further activity. In this example, either schedule will rehydrate and complete if it receives a single message on either port. Only a single message is needed because of the OR Join. Next, we develop the application to send this message.

Build CallConcur2.exe to Send Messages

The application used to send messages to Concur2.skx appears as shown in Figure 13.8.

Figure 13.8. CallConcur2.exe application.


The two buttons, Action1 and Action2 in Figure 13.8, are each used to respectively call the ports Port_1 and Port_2 and send messages to them. The Find button at the bottom locates a started schedule having the module name specified in the text box next to it. The usage is to first find a schedule and then click an action button to make the call. Now, this scenario is a bit concocted for the purposes of an example. Usually, you will have the schedule or port moniker either provided to you at runtime as part of some document exchange, or retrieved from a database as part of some overall application.

Create a new Visual Basic project for an application CallConcur2.exe. Design the form to look like Figure 13.8. The names of the controls on the form are as shown in Table 13.1. The table also makes a note of events that you must add. The sample code is available on the publisher's Web site. You can follow along and build it all yourself, or you can get it from the Web site.

Table 13.1. Controls for CallConcur2
TypeCaption or TextNameNotes
ButtonAction1btnAction1Add click event handler.
ButtonAction2btnAction2Add click event handler.
ButtonFindbtnFindScheduleAdd click event handler.
Text boxConcur2txtModuleName 

To code the event handling code, you will need to reference certain COM type libraries. Add the references shown in Table 13.2 to the Visual Basic 6.0 project.

Table 13.2. References for CallConcur2
ReferenceDLL NameNotes
XLANG Scheduler System ManagerSkedsMgr.dllShows up as SYSMGRLib in the object browser
XLANG Scheduler Runtime Type LibrarySkedCore.dllShows up as COMRUNTIME in the object browser.
AdvOrchUtilAdvOrchUtil.dllThe component we wrote earlier in Listing 13.1.

Add the code shown in Listing 13.3 to the startup form in CallConcur2. We borrowed the tracing mechanism based on OutputDebugString from Listing 13.1. The other item to note is the declaration for oSch, which represents a schedule instance. We will set this variable later in the code to the schedule instance located by module name.

Listing 13.3. Form1 Code for CallConcur2
Option Explicit

Private Declare Sub OutputDebugString Lib "kernel32" Alias "OutputDebugStringA" (ByVal
 lpOutputString As String)

Dim oSch As COMRUNTIME.IWFWorkflowInstance

Private Sub Trace(sMsg As String)
    OutputDebugString "CallConcur2.exe: " & sMsg & vbCrLf
End Sub

Listing 13.4 shows how we locate the schedule instance.

Listing 13.4. Find Button, Click Event Handler
Private Sub btnFindSchedule_Click()
    Dim oMgr As SYSMGRLib.SysMgr
    Dim oGrp As COMRUNTIME.IWFGroupAdmin
    Dim bFound As Boolean

    bFound = False
    Set oMgr = New SYSMGRLib.SysMgr
    For Each oGrp In oMgr
        For Each oSch In oGrp
            If oSch.ModuleName = txtModuleName.Text Then
                bFound = True
                Exit For
            End If
        Next oSch
        If bFound Then
            Exit For
        End If
    Next oGrp

    If bFound Then
        Trace "btnFindSchedule_Click(): " & oSch.FullyQualifiedName
    Else
        Trace "btnFindSchedule_Click(): Schedule " &  txtModuleName & " not found"
        Set oSch = Nothing
    End If
End Sub

The description of the code in Listing 13.4 is as follows. First, we acquire a reference to the system manager on the local machine. We could just as well have used the moniker as follows:

Set oMgr = GetObject("sked://") 

If we wanted the System Manager from another machine, we would include the machine name in the moniker. See Chapters 10 and 11 for more information on the structure and use of monikers in BizTalk Orchestration. Next, we iterate through each group manager on the machine and each schedule instance in the group manager. We try to match the ModuleName property with the contents of txtModuleName. If multiple schedules are running with the same module name, we will just find the first one we encounter.

If a schedule is not found, we reset the oSch reference to Nothing. In either case, we generate a trace.

Listing 13.5 shows the code required to call a port in the Concur2 XLANG schedule instance. We have a private procedure that can be called by either action button in the form. The subroutine CallPort takes the name of the port as an argument.

Listing 13.5. Calling a Schedule Port in CallConcur2
Private Sub CallPort(sPortName As String)
    Dim sMsg As String
    Dim sTitle As String

    sMsg = "Message" & sPortName
    sTitle = "Title" & sPortName

    If Not oSch Is Nothing Then
        Dim oPort As COMRUNTIME.IWFProxy
        Dim oComp As AdvOrchUtil.CMsg
        Set oPort = oSch.Port(sPortName)
        Set oComp = oPort
oComp.ShowMsgBox sMsg, sTitle
        Trace "CallPort(" & sPortName & "): " & "Post Call Msg=" & sMsg & " Title=" & sTitle
    End If
End Sub

In Listing 13.5, we attempt to call on the port only if the variable oSch is a valid reference to an instance of a running schedule. Two other variables are declared. The variable oPort is the COM proxy for the port available from the running schedule instance. We acquire this proxy using the Port property of the oSch schedule instance. If the port named in sPortName is not present, a runtime error occurs.

The other variable is oComp for the component for which a proxy is available. Note the type declaration for this component cannot be IDispatch, hence you couldn't have used a script component for this example. This restriction is because Port property of the schedule instance returns only an IUnknown interface. This interface can be queried for either the port proxy interface or the IWFProxy interface. The shaded statement queries the desired component interface from the oPort proxy and is the crucial step in this example. See Chapter 10 for more information on interfaces and proxies. Also see Visual Basic 6.0 documentation on how to program using COM components and the use of the Set statement.

The next statement makes the call to the ShowMsgBox method of the component. Recall that, in making the call, the interface is provided by a proxy for the COM component used in the port of the running schedule instance. When this call is made, the call is intercepted by the XLANG Scheduler Engine, the passed message fields are inducted into the data flows on the Data page, and the actual call to the component is made using the passed-in arguments. On return from the component, the call and the arguments are conveyed back to the original caller, which is the statement making the ShowMsgBox call in Listing 13.5. The returned values are traced by the next statement. See Chapter 10 for additional discussion on message flow in a schedule.

We wrap up the code listings by adding the remaining code shown in Listing 13.6 to the example.

Listing 13.6. Click Event Handlers for Action Buttons
Private Sub btnAction1_Click()
    CallPort "Port_1"
End Sub

Private Sub btnAction2_Click()
    CallPort "Port_2"
End Sub

Listing 13.6 responds to the action button events by calling the CallPort private procedure to do the real work. Now, we are ready to see the schedule in action.

Sending Messages to Concur2.skx Running Instances

Save and compile the project. Start the XLANG Event Monitor, DbMon, and the Component Services console, if you haven't already done so.

Tip

Usually, it is also a good idea to keep an eye on the application and system event logs to detect any problems.


Start CallConcur2.exe. Two instances of the Concur2 schedules should already be running. Click on the Find button. In the DbMon console, you will see a trace something like the following:

2156: CallConcur2.exe: btnFindSchedule_Click():sked://bztk.bztkdom.omicron.com! XLANG

Scheduler/{FE4D6B18-AA46-4235-B306-8896E1334E07}

Now, click on the Action1 button. You will see a trace like the following:

2156: CallConcur2.exe: CallPort(Port_1): Post Call  Msg=MessagePort_1 Title=TitlePort_1

You will also see the schedule rehydrate and complete. Two points are worth making here: The schedule completed after one of the two actions completed; this is what we expect. The trace message is coming from CallConcur2.exe only; there are no trace messages from the AdvOrchUtil.CMsg class, even though we ostensibly acquired a reference to this class and called a method on it, as shown in Listing 13.5. This is because of the No Instantiation option selected for the port. You see such traces in the next steps, when we use the other port that uses static instantiation.

Click Find again, this locates the other schedule. If you did not have the second schedule running, you get a runtime error. Now click Action2. You see a dialog box displayed by AdvOrchUtil.CMsg class and an instance activated in the Component Services console. The traces in the DbMon console are similar to the following:

888: AdvOrchUtil.CMsg: Class_Initialize()
888: AdvOrchUtil.CMsg: ShowMsgBox(): Msg=MessagePort_2 Title=TitlePort_2
2156: CallConcur2.exe: CallPort(Port_2): Post Call  Msg=MessagePort_2 Title=TitlePort_2
							888: AdvOrchUtil.CMsg: Class_Terminate()
						

The shaded lines appear after you dismiss the dialog box. At this point, the schedule also completes. Note the class activation and deactivation traces—an instance of the component was actually created.

If you were to look at the event history for one of these completed schedules, you would recognize the schedule execution events.

With an OR Join, only one of the two messages will surely occur before the schedule progresses beyond the Join. To use the passed-in messages, you must first detect whether the desired message exists, using the __Exists__ system field. This is shown in Figures 13.9 and 13.10. This situation is briefly discussed next.

Figure 13.9. Concur3.skv business process.


Figure 13.10. Concur3.skv data page.


Business Process After the OR Join

The schedule Concur3.skv, shown in Figures 13.9 and 13.10, is an extension of the previous schedule Concur2.skv. The schedule shows how you can design a business process to follow the OR Join. The problem is that only one leg of the many possible paths terminating at the OR Join will have executed. So how do you design the subsequent schedule? The schedule Concur3.skv is available on the publisher's Web site.

Figure 13.9 shows that a Decision shape follows the OR Join. This shape checks for the existence of specific messages using rules. See Chapter 9 for more on Decision shapes and rules. The two rules used are as follows.

ShowMsgBox_in.[__Exists__] 
ShowMsgBox_in_2.[__Exists__]

The rules check for the existence of specific incoming messages. If the message exists, the corresponding action that caused that message to be created must have occurred. It is left as an exercise for you to construct and try out this variation.

This concludes our discussion of concurrency, along with basic techniques of building, running, monitoring, and testing XLANG schedules. We also looked at creating and using COM components. The next section looks at transactions.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset