VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "Queue"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = True
Attribute VB_Description = "APE Job Manager"
Option Explicit
'-------------------------------------------------------------------------
'The Class is public, creatable, multi-use.  It is provide as an OLE interface
'for the Client applications to call, adding Service Requests to the Queue
' It implements the IQueue interface
'-------------------------------------------------------------------------

Implements APEInterfaces.IQueue

Public Enum APECallbackNotificationConstants
    apeCallbackModeNone = giNO_CALLBACK
    apeCallbackModeRegisterEveryRequest = giUSE_PASSED_CALLBACK
    apeCallbackModeRegisterOnce = giUSE_DEFAULT_CALLBACK
    apeCallbackModeUseRaiseEvent = giRETURN_BY_SYNC_EVENT
End Enum

Private moDefaultCallback As APEInterfaces.IClientCallback   'See DefaultCallback property comments
Private moEventObject As Object
Private mbHaveEventObject As Boolean

'******************
'Public Properties
'******************

Public Property Set IQueue_DefaultCallBack(ByVal oCallback As APEInterfaces.IClientCallback)
Attribute IQueue_DefaultCallBack.VB_Description = "Set the callback object to use when apeCallbackModeRegisterOnce is passed to the Add method as the callback mode."
    '-------------------------------------------------------------------------
    'Purpose:   The property allows a client to set a default
    '           callback specific to the Queue class object
    '           that is referenced by the client.  By setting
    '           this property a client can omit the CallBack parameter
    '           of the Queue.Add method and QueueMgr will use
    '           the default callback if a call back is required
    'In:
    '   [oCallback]
    '           Must be a valid callback object having a callback method
    'Effects:
    '   [moDefaultCallback]
    '           Class level variable is set equal to the passed object
    '-------------------------------------------------------------------------
    
    If oCallback Is Nothing Then Err.Raise giINVALID_CALLBACK + vbObjectError, Err.Source, LoadResString(giINVALID_CALLBACK)
    Set moDefaultCallback = oCallback
End Property

Public Property Get IQueue_DefaultCallBack() As APEInterfaces.IClientCallback
    Set IQueue_DefaultCallBack = moDefaultCallback
    
End Property

'*****************
'Public Methods
'*****************

Private Function IQueue_Add(ByVal sCommand As String, ByVal sServiceID As String, _
        ByVal iCallBackMode As APEInterfaces.APECallbackNotificationConstants, Optional ByVal vData As Variant, _
        Optional ByVal CallBack As APEInterfaces.IClientCallback) As Boolean
    '-------------------------------------------------------------------------
    'Purpose:   Called by client Applications to add a Service request to the
    '           Queue.
    'IN:
    '   [sCommand]
    '           The string that will be given to a worker with the passed data
    '           The worker uses this string to determine what OLE Server to
    '           use as a Service provider.  Part of the string is passed to the
    '           Service provider from the Worker.
    '   [sServiceID]
    '           An ID that identifies the service request
    '   [iCallBackMode]
    '           Defines if and how data is returned to client calling this function
    '   [vData]
    '           (Optional) Variant data that will be given to the Worker also.
    '           The Worker and the QueueMgr do not know what type of data this is
    '           The Worker will just pass it the the Service provider
    '   [Callback]
    '           (Optional) Callback object.  If present, it will be immediately passed
    '           to the Expediter.  The expediter will use it to callback
    '           delivering results that the Worker gives to it
    'Return:    True if the Service request was processed
    '           else returns false.
    'Effects:
    '           The Expediter will get called if a Callback is passed or bUseDefaultCallback
    '           is true
    '   [gbBusyAdding]
    '           is true during this procedure
    '   [gcQueue]
    '           will get a clsService class object, storing the Service request
    '           information.
    'Assumptions:
    '   [gcQueue]
    '           Is a valid collection object
    '-------------------------------------------------------------------------
    Dim oService As clsService      'clsService class object which is filled
                                    'with Service request data and added to collection
    Dim bDataPresent As Boolean     'Flag that data is present
    Dim oCallback As APEInterfaces.IClientCallback    'Callback object that will be passed to Expediter
    
    Dim lCount As Long              'gcQueue.count
    
    On Error GoTo AddError
    
    IQueue_Add = False
    
    'Exit sub if Stopping Queue
    If gbStopTest Then Exit Function
    
    gbBusyAdding = True
    'Check if the QueueMgr is too busy to process request
    If gcQueue.Count >= glMaxQueueSize Then Err.Raise giQUEUE_MGR_IS_BUSY
    'Check if data was passed
    If IsMissing(vData) Then bDataPresent = False Else bDataPresent = True
    'Validate that the Expediter was created successfully
    If iCallBackMode <> giNO_CALLBACK Then
        If gbFailedToCreateExpediter Then Err.Raise giCOULD_NOT_CREATE_EXPEDITER
    End If
    'Validate callback object
    Select Case iCallBackMode
        Case giUSE_PASSED_CALLBACK
            If CallBack Is Nothing Then
                Err.Raise giINVALID_CALLBACK
            Else
                Set oCallback = CallBack
            End If
        Case giUSE_DEFAULT_CALLBACK
            If moDefaultCallback Is Nothing Then
                Err.Raise giINVALID_CALLBACK
            Else
                Set oCallback = moDefaultCallback
            End If
        Case giRETURN_BY_SYNC_EVENT
            If Not mbHaveEventObject Then Err.Raise giFIRST_GET_WITHEVENTS_OBJECT
    End Select
    
    'Iterate count of this method call
    glAddCallCount = glAddCallCount + 1
    
    'Update U/I if form is visible
    If gbShow Then frmQueueMgr.lblCount = glAddCallCount
    
    Set oService = New clsService
    
    LogEvent giADD_RECEIVED, sServiceID
    
    'Create the put the Service request values
    'in the clsService object
    With oService
        .ID = sServiceID
        .Command = sCommand
        .CallBackMode = iCallBackMode
        Select Case iCallBackMode
            Case giUSE_PASSED_CALLBACK, giUSE_DEFAULT_CALLBACK
                Set .CallBack = oCallback
            Case giRETURN_BY_SYNC_EVENT
                Set .EventObject = moEventObject
        End Select
        .DataPresent = bDataPresent
        'Check what data type vData return is
        'in order to determine how to handle it
        If bDataPresent Then
            Select Case VarType(vData)
                Case vbEmpty, vbNull
                    .Data = Null
                Case vbObject, vbError, vbDataObject
                    Set .Data = vData
                Case Else
                    .Data = vData
            End Select
        End If
    End With
    'Add oService to Queue using ID as Key
    gcQueue.Add oService, sServiceID
    
    'Display stats
    lCount = gcQueue.Count
    If gbShow Then frmQueueMgr.lblQueue = lCount
    If lCount > glPeakQueueSize Then
        glPeakQueueSize = lCount
        If gbShow Then frmQueueMgr.lblPeak = glPeakQueueSize
    End If

    If gbStopTest And Not gbBusyGetServiceRequest And Not gbBusyGetServiceResults Then StopQueue
    
    'Flip the status flag right before the calling client is released
    'A worker should not be allowed to take an activity request
    'until the client is released.  This keeps the the expediter
    'from calling the client with Service results before the client
    'is released with the return value, the Service ID
    oService.Status = giWAITING_FOR_WORKER
    Set oService = Nothing
    gbBusyAdding = False
    
    IQueue_Add = True

    Exit Function
AddError:
    Select Case Err.Number
        Case giQUEUE_MGR_IS_BUSY
            gbBusyAdding = False
            Err.Raise Err.Number + vbObjectError, Err.Source, LoadResString(Err.Number)
            Exit Function
        Case Is > giERROR_THRESHOLD
            LogError Err, gsNULL_SERVICE_ID
            gbBusyAdding = False
            Err.Raise Err.Number + vbObjectError, Err.Source, LoadResString(Err.Number)
            Exit Function
        Case RPC_E_CALL_REJECTED
            'Collision error, the OLE server is busy
            Dim iRetry As Integer
            Dim il As Integer
            Dim ir As Integer
            'First check for stop test
            If gbStopTest And Not gbBusyGetServiceRequest Then StopQueue: Exit Function
            If iRetry < giMAX_ALLOWED_RETRIES Then
                iRetry = iRetry + 1
                ir = Int((giRETRY_WAIT_MAX - giRETRY_WAIT_MIN + 1) * Rnd + giRETRY_WAIT_MIN)
                For il = 0 To ir
                    DoEvents
                Next il
                LogEvent giCALL_REJECTED_RETRY, sServiceID
                Resume
            Else
                'We reached our max retries
                LogError Err, sServiceID
                gbBusyAdding = False
                Err.Raise Err.Number, Err.Source, Err.Description
            End If
        Case giCOULD_NOT_CREATE_EXPEDITER
            LogError Err, sServiceID
            gbBusyAdding = False
            Err.Raise giCOULD_NOT_CREATE_EXPEDITER + vbObjectError, Err.Source, LoadResString(giCOULD_NOT_CREATE_EXPEDITER)
        Case giFIRST_GET_WITHEVENTS_OBJECT
            LogError Err, sServiceID
            gbBusyAdding = False
            Err.Raise giFIRST_GET_WITHEVENTS_OBJECT + vbObjectError, Err.Source, LoadResString(giFIRST_GET_WITHEVENTS_OBJECT)
        Case giINVALID_CALLBACK
            LogError Err, sServiceID
            gbBusyAdding = False
            Err.Raise giINVALID_CALLBACK + vbObjectError, Err.Source, LoadResString(giINVALID_CALLBACK)
        Case ERR_OVER_FLOW
            LogError Err, sServiceID
            If glAddCallCount = glMAX_ID Then glAddCallCount = 0
            Resume
        Case Else
            LogError Err, sServiceID
            gbBusyAdding = False
            Err.Raise Err.Number, Err.Source, Err.Description
            Exit Function
    End Select
End Function

Public Function IQueue_GetEventObject() As Object
Attribute IQueue_GetEventObject.VB_Description = "Returns the event source object that a client must respond to when apeCallbackModeUseRaiseEvent is passed to the Add method as the callback mode."
    If Not mbHaveEventObject Then
        Set moEventObject = goExpediter.GetEventObject
        mbHaveEventObject = True
    End If
    Set IQueue_GetEventObject = moEventObject
End Function

'*******************
'Private methods
'*******************

Private Sub Class_Initialize()
    CountInitialize
End Sub

Private Sub Class_Terminate()
    CountTerminate
End Sub
