This commit is contained in:
2018-06-29 19:47:36 +00:00
commit be7f501333
3769 changed files with 1425961 additions and 0 deletions

View File

@@ -0,0 +1 @@
CSLA.BatchQueue.xml

View File

@@ -0,0 +1,32 @@
Imports System
Imports System.Reflection
Imports System.Runtime.InteropServices
' General Information about an assembly is controlled through the following
' set of attributes. Change these attribute values to modify the information
' associated with an assembly.
' Review the values of the assembly attributes
<Assembly: AssemblyTitle("CSLA.BatchQueue")>
<Assembly: AssemblyDescription("CSLA .NET framework")>
<Assembly: AssemblyCompany("Rockford Lhotka")>
<Assembly: AssemblyProduct("Expert One-on-One VB.NET Business Objects")>
<Assembly: AssemblyCopyright("Copyright 2003 Rockford Lhotka. All rights reserved.")>
<Assembly: AssemblyTrademark("")>
<Assembly: CLSCompliant(True)>
'The following GUID is for the ID of the typelib if this project is exposed to COM
<Assembly: Guid("EC45E25E-E411-4477-95FE-1F10004502E0")>
' Version information for an assembly consists of the following four values:
'
' Major Version
' Minor Version
' Build Number
' Revision
'
' You can specify all the values or you can default the Build and Revision Numbers
' by using the '*' as shown below:
<Assembly: AssemblyVersion("1.3.*")>

View File

@@ -0,0 +1,49 @@
Imports System.Messaging
Imports System.Runtime.Serialization.Formatters.Binary
''' <summary>
''' Contains a list of holding, pending and active batch
''' queue entries.
''' </summary>
<Serializable()> _
Public Class BatchEntries
Inherits CollectionBase
''' <summary>
''' Returns a reference to an object with information about
''' a specific batch queue entry.
''' </summary>
Default Public ReadOnly Property Entry(ByVal index As Integer) As BatchEntryInfo
Get
Return CType(list.Item(index), BatchEntryInfo)
End Get
End Property
''' <summary>
''' Returns a reference to an object with information about
''' a specific batch queue entry.
''' </summary>
''' <param name="ID">The ID value of the entry to return.</param>
Default Public ReadOnly Property Entry(ByVal ID As Guid) As BatchEntryInfo
Get
Dim obj As BatchEntryInfo
For Each obj In list
If obj.ID.Equals(ID) Then
Return obj
End If
Next
End Get
End Property
Friend Sub New()
' prevent direct creation
End Sub
Friend Sub Add(ByVal Value As BatchEntryInfo)
list.Add(Value)
End Sub
End Class

View File

@@ -0,0 +1,225 @@
Imports System.Security.Principal
Imports System.Configuration
''' <summary>
'''
''' </summary>
Namespace Server
''' <summary>
''' A batch queue entry.
''' </summary>
''' <remarks>
''' Each batch queue entry consists of basic information about
''' the entry, a Principal object (if you are using CSLA .NET
''' security), the actual worker object containing the code
''' to be run and an optional state object containing arbitrary
''' state data.
''' </remarks>
<Serializable()> _
Public NotInheritable Class BatchEntry
Private mInfo As New BatchEntryInfo
Private mPrincipal As IPrincipal = GetPrincipal()
Private mWorker As IBatchEntry
Private mState As Object
''' <summary>
''' Returns a reference to the object containing information
''' about this batch entry.
''' </summary>
Public ReadOnly Property Info() As BatchEntryInfo
Get
Return mInfo
End Get
End Property
''' <summary>
''' Returns a reference to the
''' <see cref="T:CSLA.Security.BusinessPrincipal" />
''' object for the user that submitted this entry.
''' </summary>
Public ReadOnly Property Principal() As IPrincipal
Get
Return mPrincipal
End Get
End Property
''' <summary>
''' Returns a reference to the worker object that
''' contains the code which is to be executed as
''' a batch process.
''' </summary>
Public Property Entry() As IBatchEntry
Get
Return mWorker
End Get
Set(ByVal Value As IBatchEntry)
mWorker = Value
End Set
End Property
''' <summary>
''' Returns a reference to the optional state object.
''' </summary>
''' <returns></returns>
Public Property State() As Object
Get
Return mState
End Get
Set(ByVal Value As Object)
mState = Value
End Set
End Property
#Region " Batch execution "
' this will run in a background thread in the
' thread pool
Friend Sub Execute(ByVal State As Object)
Dim oldPrincipal As IPrincipal
Try
' set this thread's principal to our user
oldPrincipal = Threading.Thread.CurrentPrincipal
SetPrincipal(mPrincipal)
Try
' now run the user's code
mWorker.Execute(mState)
Dim sb As New Text.StringBuilder()
With sb
.Append("Batch job completed")
.Append(vbCrLf)
.AppendFormat("Batch job: {0}", Me.ToString)
.Append(vbCrLf)
.AppendFormat("Job object: {0}", CType(mWorker, Object).ToString)
.Append(vbCrLf)
End With
System.Diagnostics.EventLog.WriteEntry( _
BatchQueueService.Name, sb.ToString, EventLogEntryType.Information)
Catch ex As Exception
Dim sb As New Text.StringBuilder()
With sb
.Append("Batch job failed due to execution error")
.Append(vbCrLf)
.AppendFormat("Batch job: {0}", Me.ToString)
.Append(vbCrLf)
.AppendFormat("Job object: {0}", CType(mWorker, Object).ToString)
.Append(vbCrLf)
.Append(ex.ToString)
End With
System.Diagnostics.EventLog.WriteEntry( _
BatchQueueService.Name, sb.ToString, EventLogEntryType.Warning)
End Try
Finally
Server.BatchQueueService.Deactivate(Me)
' reset the thread's principal object
Threading.Thread.CurrentPrincipal = oldPrincipal
End Try
End Sub
#End Region
#Region " System.Object overrides "
Public Overrides Function ToString() As String
Return mInfo.ToString
End Function
Public Overloads Function Equals(ByVal Entry As BatchEntry) As Boolean
Return mInfo.Equals(Entry.Info)
End Function
Public Overrides Function GetHashCode() As Integer
Return mInfo.GetHashCode
End Function
#End Region
#Region " Constructors "
Friend Sub New(ByVal Entry As IBatchEntry)
mWorker = Entry
End Sub
Friend Sub New(ByVal Entry As IBatchEntry, ByVal State As Object)
mWorker = Entry
mState = State
End Sub
#End Region
#Region " Security "
Private Function AUTHENTICATION() As String
Return ConfigurationSettings.AppSettings("Authentication")
End Function
Private Function GetPrincipal() As System.Security.Principal.IPrincipal
If AUTHENTICATION() = "Windows" Then
' Windows integrated security
Return Nothing
Else
' we assume using the CSLA framework security
Return System.Threading.Thread.CurrentPrincipal
End If
End Function
Private Sub SetPrincipal(ByVal Principal As Object)
If AUTHENTICATION() = "Windows" Then
' when using integrated security, Principal must be Nothing
' and we need to set our policy to use the Windows principal
If Principal Is Nothing Then
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal)
Exit Sub
Else
Throw New System.Security.SecurityException("No principal object should be passed to DataPortal when using Windows integrated security")
End If
End If
' we expect Principal to be of type BusinessPrincipal, but
' we can't enforce that since it causes a circular reference
' with the business library so instead we must use type Object
' for the parameter, so here we do a check on the type of the
' parameter
If Principal.ToString = "CSLA.Security.BusinessPrincipal" Then
' see if our current principal is
' different from the caller's principal
If Not ReferenceEquals(Principal, System.Threading.Thread.CurrentPrincipal) Then
' the caller had a different principal, so change ours to
' match the caller's so all our objects use the caller's
' security
System.Threading.Thread.CurrentPrincipal = CType(Principal, IPrincipal)
End If
Else
Throw New System.Security.SecurityException("Principal must be of type BusinessPrincipal, not " & Principal.ToString)
End If
End Sub
#End Region
End Class
End Namespace

View File

@@ -0,0 +1,185 @@
Imports System.Environment
''' <summary>
''' Values indicating the status of a batch queue entry.
''' </summary>
Public Enum BatchEntryStatus
Pending
Holding
Active
End Enum
''' <summary>
''' Contains information about a batch queue entry.
''' </summary>
''' <remarks>
''' This object contains information about batch entry including
''' when it was submitted, the user that submitted the job and
''' which machine the user was using at the time. It also contains
''' the job's priority, status and the optional date/time until
''' which the job should be held until it is run.
''' </remarks>
<Serializable()> _
Public Class BatchEntryInfo
Private mID As Guid = Guid.NewGuid
Private mSubmitted As Date = Now
Private mUser As String = System.Environment.UserName
Private mMachine As String = System.Environment.MachineName
Private mPriority As Messaging.MessagePriority = Messaging.MessagePriority.Normal
Private mMsgID As String
Private mHoldUntil As Date = Date.MinValue
Private mStatus As BatchEntryStatus = BatchEntryStatus.Pending
''' <summary>
''' Returns the unique ID value for this batch entry.
''' </summary>
Public ReadOnly Property ID() As Guid
Get
Return mID
End Get
End Property
''' <summary>
''' Returns the date and time that the batch entry
''' was submitted.
''' </summary>
Public ReadOnly Property Submitted() As Date
Get
Return mSubmitted
End Get
End Property
''' <summary>
''' Returns the Windows user id of the user that
''' was logged into the workstation when the job
''' was submitted.
''' </summary>
Public ReadOnly Property User() As String
Get
Return mUser
End Get
End Property
''' <summary>
''' Returns the name of the workstation from
''' which this job was submitted.
''' </summary>
Public ReadOnly Property Machine() As String
Get
Return mMachine
End Get
End Property
''' <summary>
''' Returns the priority of this batch entry.
''' </summary>
''' <remarks>
''' The priority values flow from System.Messaging and
''' the priority is used by MSMQ to order the entries
''' in the queue.
''' </remarks>
Public Property Priority() As Messaging.MessagePriority
Get
Return mPriority
End Get
Set(ByVal Value As Messaging.MessagePriority)
mPriority = Value
End Set
End Property
''' <summary>
''' Returns the MSMQ message ID of the batch entry.
''' </summary>
''' <remarks>
''' This value is only valid after the batch entry
''' has been submitted to the queue.
''' </remarks>
Public ReadOnly Property MessageID() As String
Get
Return mMsgID
End Get
End Property
Friend Sub SetMessageID(ByVal ID As String)
mMsgID = ID
End Sub
''' <summary>
''' Returns the date and time until which the
''' batch entry will be held before it can be run.
''' </summary>
''' <remarks>
''' This value is optional. If it was provided, the batch
''' entry will be held until this date/time. At this date/time,
''' the entry will switch from Holding status to Pending
''' status and will be queued based on its priority along
''' with all other Pending entries.
''' </remarks>
Public Property HoldUntil() As Date
Get
Return mHoldUntil
End Get
Set(ByVal Value As Date)
mHoldUntil = Value
End Set
End Property
''' <summary>
''' Returns the status of the batch entry.
''' </summary>
''' <remarks>
''' <para>
''' If the job is Holding, it means that the job
''' won't run until the data/time specified by
''' <see cref="P:CSLA.BatchQueue.BatchEntryInfo.HoldUntil" />.
''' </para><para>
''' If the job is Pending, it means that the job
''' will run as soon as possible, but that the queue
''' is busy. Pending entries are run in priority order based
''' on <see cref="P:CSLA.BatchQueue.BatchEntryInfo.Priority" />.
''' </para><para>
''' If the job is Active, it means that the job is
''' currently being executed on the server.
''' </para>
''' </remarks>
Public ReadOnly Property Status() As BatchEntryStatus
Get
If mHoldUntil > Now AndAlso mStatus = BatchEntryStatus.Pending Then
Return BatchEntryStatus.Holding
Else
Return mStatus
End If
End Get
End Property
Friend Sub SetStatus(ByVal Status As BatchEntryStatus)
mStatus = Status
End Sub
#Region " System.Object overrides "
Public Overrides Function ToString() As String
Return mUser & "@" & mMachine & ":" & mID.ToString
End Function
Public Overloads Function Equals(ByVal Info As BatchEntryInfo) As Boolean
Return mID.Equals(Info.ID)
End Function
Public Overrides Function GetHashCode() As Integer
Return mID.GetHashCode
End Function
#End Region
End Class

View File

@@ -0,0 +1,97 @@
''' <summary>
''' A helper object used to execute a specified class
''' from a specified DLL on the server.
''' </summary>
''' <remarks>
''' <para>
''' A worker object can be provided directly by the client
''' workstation. In that case, the worker object is passed
''' by value to the server where it is executed. The drawback
''' to such an approach is that the worker assembly must be
''' installed on both client and server.
''' </para><para>
''' BatchJobRequest is a worker object that specifies the
''' type and assembly name of a class on the server. When
''' this job is run, it dynamically creates an instance of
''' the specified class and executes it on the server. This
''' means that the actual worker assembly needs to be installed
''' only on the server, not on the client.
''' </para>
''' </remarks>
<Serializable()> _
Public Class BatchJobRequest
Implements IBatchEntry
Private mAssembly As String = ""
Private mType As String = ""
''' <summary>
''' Creates a new object, specifying the type and assembly
''' of the actual worker object.
''' </summary>
''' <param name="Type">The class name of the actual worker object.</param>
''' <param name="Assembly">The name of the assembly containing the actual worker class.</param>
Public Sub New(ByVal Type As String, ByVal [Assembly] As String)
mAssembly = [Assembly]
mType = Type
End Sub
''' <summary>
''' The class name of the worker object.
''' </summary>
Public Property Type() As String
Get
Return mType
End Get
Set(ByVal Value As String)
mType = Value
End Set
End Property
''' <summary>
''' The name of the assembly containing the actual worker class.
''' </summary>
Public Property [Assembly]() As String
Get
Return mAssembly
End Get
Set(ByVal Value As String)
mAssembly = Value
End Set
End Property
''' <summary>
''' Executes the batch job on the server.
''' </summary>
''' <remarks>
''' This method runs on the server - it is called
''' by <see cref="T:CSLA.BatchQueue.Server.BatchEntry" />,
''' which is called by
''' <see cref="T:CSLA.BatchQueue.Server.BatchQueueService" />.
''' </remarks>
''' <param name="State"></param>
Public Sub Execute(ByVal State As Object) _
Implements IBatchEntry.Execute
' create an instance of the specified object
Dim job As IBatchEntry
job = CType(AppDomain.CurrentDomain.CreateInstanceAndUnwrap(mAssembly, mType), IBatchEntry)
' execute the job
job.Execute(State)
End Sub
#Region " System.Object overrides "
Public Overrides Function ToString() As String
Return "BatchJobRequest: " & mType & "," & mAssembly
End Function
#End Region
End Class

View File

@@ -0,0 +1,257 @@
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Tcp
Imports System.Configuration
''' <summary>
''' Provides easy access to the batch queue functionality.
''' </summary>
''' <remarks>
''' Client code should create an instance of this class to
''' interact with a batch queue. A BatchQueue object can
''' be used to interact with either the default batch queue
''' server or with a specific batch queue server.
''' </remarks>
Public Class BatchQueue
Private mQueueURL As String
Private mServer As Server.BatchQueue
#Region " Constructors and Initialization "
''' <summary>
''' Creates an instance of the object that allows interaction
''' with the default batch queue server as specified in the
''' application configuration file.
''' </summary>
Public Sub New()
mQueueURL = ConfigurationSettings.AppSettings("DefaultBatchQueueServer")
End Sub
''' <summary>
''' Creates an instance of the object that allows interaction
''' with a specific batch queue server as specified by
''' the URL passed as a parameter.
''' </summary>
''' <param name="QueueServerURL">A URL referencing the batch queue server.</param>
Public Sub New(ByVal QueueServerURL As String)
mQueueURL = QueueServerURL
End Sub
Private Function QueueServer() As Server.BatchQueue
If mServer Is Nothing Then
mServer = _
CType(Activator.GetObject(GetType(Server.BatchQueue), _
mQueueURL), _
Server.BatchQueue)
End If
Return mServer
End Function
#End Region
#Region " Submitting jobs "
''' <summary>
''' Submits an entry to the batch queue.
''' </summary>
''' <param name="Entry">A reference to your worker object.</param>
Public Sub Submit(ByVal Entry As IBatchEntry)
QueueServer.Submit(New Server.BatchEntry(Entry))
End Sub
''' <summary>
''' Submits an entry to the batch queue with extra state data.
''' </summary>
''' <param name="Entry">A reference to your worker object.</param>
''' <param name="State">A reference to a serializable object containing state data.</param>
Public Sub Submit(ByVal Entry As IBatchEntry, ByVal State As Object)
QueueServer.Submit(New Server.BatchEntry(Entry, State))
End Sub
''' <summary>
''' Submits an entry to the batch queue with a specific priority.
''' </summary>
''' <param name="Entry">A reference to your worker object.</param>
''' <param name="Priority">The priority of the entry.</param>
Public Sub Submit(ByVal Entry As IBatchEntry, _
ByVal Priority As Messaging.MessagePriority)
Dim job As Server.BatchEntry = New Server.BatchEntry(Entry)
job.Info.Priority = Priority
QueueServer.Submit(job)
End Sub
''' <summary>
''' Submits an entry to the batch queue with extra state data and
''' a specific priority.
''' </summary>
''' <param name="Entry">A reference to your worker object.</param>
''' <param name="State">A reference to a serializable object containing state data.</param>
''' <param name="Priority">The priority of the entry.</param>
Public Sub Submit(ByVal Entry As IBatchEntry, _
ByVal State As Object, _
ByVal Priority As Messaging.MessagePriority)
Dim job As Server.BatchEntry = New Server.BatchEntry(Entry, State)
job.Info.Priority = Priority
QueueServer.Submit(job)
End Sub
''' <summary>
''' Submits an entry to the batch queue to be held until a specific date/time.
''' </summary>
''' <param name="Entry">A reference to your worker object.</param>
''' <param name="HoldUntil">The date/time until which the entry should be held.</param>
Public Sub Submit(ByVal Entry As IBatchEntry, ByVal HoldUntil As Date)
Dim job As Server.BatchEntry = New Server.BatchEntry(Entry)
job.Info.HoldUntil = HoldUntil
QueueServer.Submit(job)
End Sub
''' <summary>
''' Submits an entry to the batch queue with extra state data
''' and to be held until a specific date/time.
''' </summary>
''' <param name="Entry">A reference to your worker object.</param>
''' <param name="State">A reference to a serializable object containing state data.</param>
''' <param name="HoldUntil">The date/time until which the entry should be held.</param>
Public Sub Submit(ByVal Entry As IBatchEntry, _
ByVal State As Object, _
ByVal HoldUntil As Date)
Dim job As Server.BatchEntry = New Server.BatchEntry(Entry, State)
job.Info.HoldUntil = HoldUntil
QueueServer.Submit(job)
End Sub
''' <summary>
''' Submits an entry to the batch queue to be held until
''' a specific date/time and at a specific priority.
''' </summary>
''' <param name="Entry">A reference to your worker object.</param>
''' <param name="HoldUntil">The date/time until which the entry should be held.</param>
''' <param name="Priority">The priority of the entry.</param>
Public Sub Submit(ByVal Entry As IBatchEntry, _
ByVal HoldUntil As Date, ByVal Priority As Messaging.MessagePriority)
Dim job As Server.BatchEntry = New Server.BatchEntry(Entry)
job.Info.HoldUntil = HoldUntil
job.Info.Priority = Priority
QueueServer.Submit(job)
End Sub
''' <summary>
''' Submits an entry to the batch queue specifying all details.
''' </summary>
''' <param name="Entry">A reference to your worker object.</param>
''' <param name="State">A reference to a serializable object containing state data.</param>
''' <param name="HoldUntil">The date/time until which the entry should be held.</param>
''' <param name="Priority">The priority of the entry.</param>
Public Sub Submit(ByVal Entry As IBatchEntry, _
ByVal State As Object, _
ByVal HoldUntil As Date, _
ByVal Priority As Messaging.MessagePriority)
Dim job As Server.BatchEntry = New Server.BatchEntry(Entry, State)
job.Info.HoldUntil = HoldUntil
job.Info.Priority = Priority
QueueServer.Submit(job)
End Sub
#End Region
#Region " Public methods "
''' <summary>
''' Removes a holding or pending entry from the
''' batch queue.
''' </summary>
''' <param name="Entry">A reference to the entry to be removed.</param>
Public Sub Remove(ByVal Entry As BatchEntryInfo)
QueueServer.Remove(Entry)
End Sub
''' <summary>
''' Returns the URL which identifies the batch
''' queue server to which this object is attached.
''' </summary>
Public ReadOnly Property BatchQueueURL() As String
Get
Return mQueueURL
End Get
End Property
''' <summary>
''' Returns a collection of the batch entries currently
''' in the batch queue.
''' </summary>
Public ReadOnly Property Entries() As BatchEntries
Get
Return QueueServer.GetEntries(GetPrincipal)
End Get
End Property
#End Region
#Region " System.Object overrides "
Public Overrides Function ToString() As String
Return mQueueURL
End Function
Public Overloads Function Equals(ByVal Queue As BatchQueue) As Boolean
Return mQueueURL = Queue.BatchQueueURL
End Function
Public Overrides Function GetHashCode() As Integer
Return mQueueURL.GetHashCode
End Function
#End Region
#Region " Security "
Private Function AUTHENTICATION() As String
Return ConfigurationSettings.AppSettings("Authentication")
End Function
Private Function GetPrincipal() As System.Security.Principal.IPrincipal
If AUTHENTICATION() = "Windows" Then
' Windows integrated security
Return Nothing
Else
' we assume using the CSLA framework security
Return System.Threading.Thread.CurrentPrincipal
End If
End Function
#End Region
End Class

View File

@@ -0,0 +1,103 @@
Imports System.Security.Principal
Imports System.Configuration
Imports CSLA.Security
''' <summary>
'''
''' </summary>
Namespace Server
''' <summary>
''' This is the entry point for all queue requests from
''' the client via Remoting.
''' </summary>
Public Class BatchQueue
Inherits MarshalByRefObject
#Region " Public methods "
''' <summary>
''' Submits a batch entry to the queue.
''' </summary>
''' <param name="Entry">A reference to the batch entry.</param>
Public Sub Submit(ByVal Entry As BatchEntry)
BatchQueueService.Enqueue(Entry)
End Sub
''' <summary>
''' Removes a holding or pending entry from the queue.
''' </summary>
''' <param name="Entry">A reference to the info object for the batch entry.</param>
Public Sub Remove(ByVal Entry As BatchEntryInfo)
BatchQueueService.Dequeue(Entry)
End Sub
''' <summary>
''' Gets a list of the entries currently in the
''' batch queue.
''' </summary>
''' <param name="Principal">The requesting user's security credentials.</param>
Public Function GetEntries(ByVal Principal As IPrincipal) As BatchEntries
SetPrincipal(Principal)
Dim entries As New BatchEntries()
BatchQueueService.LoadEntries(entries)
Return entries
End Function
#End Region
#Region " Security "
Private Function AUTHENTICATION() As String
Return ConfigurationSettings.AppSettings("Authentication")
End Function
Private Sub SetPrincipal(ByVal Principal As Object)
If AUTHENTICATION() = "Windows" Then
' when using integrated security, Principal must be Nothing
' and we need to set our policy to use the Windows principal
If Principal Is Nothing Then
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal)
Exit Sub
Else
Throw New System.Security.SecurityException("No principal object should be passed to DataPortal when using Windows integrated security")
End If
End If
' we expect Principal to be of type BusinessPrincipal, but
' we can't enforce that since it causes a circular reference
' with the business library so instead we must use type Object
' for the parameter, so here we do a check on the type of the
' parameter
If Principal.ToString = "CSLA.Security.BusinessPrincipal" Then
' see if our current principal is
' different from the caller's principal
If Not ReferenceEquals(Principal, System.Threading.Thread.CurrentPrincipal) Then
' the caller had a different principal, so change ours to
' match the caller's so all our objects use the caller's
' security
System.Threading.Thread.CurrentPrincipal = CType(Principal, IPrincipal)
End If
Else
Throw New System.Security.SecurityException("Principal must be of type BusinessPrincipal, not " & Principal.ToString)
End If
End Sub
#End Region
End Class
End Namespace

View File

@@ -0,0 +1,337 @@
Imports System.Configuration
Imports System.Messaging
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Tcp
Imports System.IO
Imports System.Runtime.Serialization.Formatters.Binary
''' <summary>
'''
''' </summary>
Namespace Server
''' <summary>
''' Implements the batch queue service.
''' </summary>
''' <remarks>
''' This class implements the server-side batch queue functionality.
''' It must be hosted within some process, typically a Windows Service.
''' It may also be hosted within a console application, which is
''' useful for testing and debugging.
''' </remarks>
Public Class BatchQueueService
Private Shared mChannel As TcpServerChannel
Private Shared mQueue As MessageQueue
Private Shared mMonitor As Threading.Thread
Private Shared WithEvents mTimer As New System.Timers.Timer
Private Shared mRunning As Boolean
Private Shared mActiveEntries As Hashtable = Hashtable.Synchronized(New Hashtable)
Private Shared mSync As New Threading.AutoResetEvent(False)
Private Shared mWaitToEnd As New Threading.ManualResetEvent(False)
''' <summary>
''' Returns the name of the batch queue server.
''' </summary>
Public Shared ReadOnly Property Name() As String
Get
Return LISTENER_NAME()
End Get
End Property
#Region " Start/Stop "
''' <summary>
''' Starts the batch queue.
''' </summary>
''' <remarks>
''' Call this as your Windows Service starts up to
''' start the batch queue itself. This will cause
''' the queue to start listening for user requests
''' via remoting and to the MSMQ for queued jobs.
''' </remarks>
Public Shared Sub Start()
mTimer.AutoReset = False
' open and/or create queue
If MessageQueue.Exists(QUEUE_NAME) Then
mQueue = New MessageQueue(QUEUE_NAME)
Else
mQueue = MessageQueue.Create(QUEUE_NAME)
End If
mQueue.MessageReadPropertyFilter.Extension = True
' start reading from queue
mRunning = True
mMonitor = New Threading.Thread(AddressOf MonitorQueue)
mMonitor.Name = "MonitorQueue"
mMonitor.Start()
' start remoting for Server.BatchQueue
If mChannel Is Nothing Then
' set application name (virtual root name)
RemotingConfiguration.ApplicationName = LISTENER_NAME()
' set up channel
Dim properties As New Hashtable()
properties("name") = "TcpBinary"
properties("priority") = "2"
properties("port") = CStr(PORT())
Dim svFormatter As New BinaryServerFormatterSinkProvider()
'TODO: uncomment the following line for .NET 1.1
'svFormatter.TypeFilterLevel = Runtime.Serialization.Formatters.TypeFilterLevel.Full
mChannel = New TcpServerChannel(properties, svFormatter)
Channels.ChannelServices.RegisterChannel(mChannel)
' register our class
RemotingConfiguration.RegisterWellKnownServiceType( _
GetType(Server.BatchQueue), _
"BatchQueue.rem", _
WellKnownObjectMode.SingleCall)
Else
mChannel.StartListening(Nothing)
End If
Dim sb As New Text.StringBuilder()
sb.Append("Batch queue processor started")
sb.Append(vbCrLf)
sb.AppendFormat("Name: {0}", Name)
sb.Append(vbCrLf)
sb.AppendFormat("Port: {0}", PORT)
sb.Append(vbCrLf)
sb.AppendFormat("Queue: {0}", QUEUE_NAME)
sb.Append(vbCrLf)
sb.AppendFormat("Max jobs: {0}", MAX_ENTRIES)
sb.Append(vbCrLf)
System.Diagnostics.EventLog.WriteEntry( _
Name, sb.ToString, EventLogEntryType.Information)
End Sub
''' <summary>
''' Stops the batch queue.
''' </summary>
''' <remarks>
''' <para>
''' Call this as your Windows Service is stopping. It
''' stops the batch queue, causing it to stop listening
''' for user requests via remoting and to stop looking for
''' jobs in the MSMQ queue.
''' </para><para>
''' NOTE that this method will not return until any
''' currently active (executing) batch jobs have completed.
''' </para>
''' </remarks>
Public Shared Sub [Stop]()
' stop remoting for Server.BatchQueue
mChannel.StopListening(Nothing)
' signal to stop working
mRunning = False
mSync.Set()
mMonitor.Join()
' close the queue
mQueue.Close()
If mActiveEntries.Count > 0 Then
' wait for work to end
mWaitToEnd.WaitOne()
End If
End Sub
#End Region
#Region " Process messages "
' this will be running on a background thread
Private Shared Sub MonitorQueue()
While mRunning
ScanQueue()
mSync.WaitOne()
End While
End Sub
' this runs on a threadpool thread
Private Shared Sub mTimer_Elapsed(ByVal sender As Object, _
ByVal e As System.Timers.ElapsedEventArgs) Handles mTimer.Elapsed
mTimer.Stop()
mSync.Set()
End Sub
' this is called by MonitorQueue
Private Shared Sub ScanQueue()
Dim msg As Message
Dim holdUntil As Date
Dim nextWake As Date = Date.MaxValue
Dim en As MessageEnumerator = mQueue.GetMessageEnumerator
While en.MoveNext()
msg = en.Current
holdUntil = CDate(Text.Encoding.ASCII.GetString(msg.Extension))
If holdUntil <= Now Then
If mActiveEntries.Count < MAX_ENTRIES() Then
ProcessEntry(mQueue.ReceiveById(msg.Id))
Else
' the queue is busy, go to sleep
Exit Sub
End If
ElseIf holdUntil < nextWake Then
' find the minimum holduntil value
nextWake = holdUntil
End If
End While
If nextWake < Date.MaxValue AndAlso nextWake > Now Then
' we have at least one entry holding, so set the
' timer to wake us when it should be run
mTimer.Interval = nextWake.Subtract(Now).TotalMilliseconds
mTimer.Start()
End If
End Sub
Private Shared Sub ProcessEntry(ByVal msg As Message)
' get entry from queue
Dim entry As BatchEntry
Dim formatter As New BinaryFormatter()
entry = CType(formatter.Deserialize(msg.BodyStream), BatchEntry)
' make active
entry.Info.SetStatus(BatchEntryStatus.Active)
mActiveEntries.Add(entry.Info.ID, entry.Info)
' start processing entry on background thread
Threading.ThreadPool.QueueUserWorkItem(AddressOf entry.Execute)
End Sub
' called by BatchEntry when it is done processing so
' we know that it is complete and we can start another
' job if needed
Friend Shared Sub Deactivate(ByVal entry As BatchEntry)
mActiveEntries.Remove(entry.Info.ID)
mSync.Set()
If Not mRunning AndAlso mActiveEntries.Count = 0 Then
' indicate that there are no active workers
mWaitToEnd.Set()
End If
End Sub
#End Region
#Region " Enqueue/Dequeue/LoadEntries "
Friend Shared Sub Enqueue(ByVal Entry As BatchEntry)
Dim msg As New Message()
Dim f As New BinaryFormatter()
With msg
.Label = Entry.ToString
.Priority = Entry.Info.Priority
.Extension = Text.Encoding.ASCII.GetBytes(CStr(Entry.Info.HoldUntil))
Entry.Info.SetMessageID(.Id)
f.Serialize(.BodyStream, Entry)
End With
mQueue.Send(msg)
mSync.Set()
End Sub
Friend Shared Sub Dequeue(ByVal Entry As BatchEntryInfo)
Dim label As String
Dim msg As Message
Dim msgID As String
label = Entry.ToString
Dim en As MessageEnumerator = mQueue.GetMessageEnumerator
mQueue.MessageReadPropertyFilter.Label = True
While en.MoveNext()
If en.Current.Label = label Then
' we found a match
msgID = en.Current.Id
Exit While
End If
End While
If Len(msgID) > 0 Then
mQueue.ReceiveById(msgID)
End If
End Sub
Friend Shared Sub LoadEntries(ByVal List As BatchEntries)
' load our list of BatchEntry objects
Dim msgs() As Message
Dim msg As Message
Dim formatter As New BinaryFormatter()
Dim de As DictionaryEntry
Dim entry As Server.BatchEntry
' get all active entries
SyncLock mActiveEntries.SyncRoot
For Each de In Server.BatchQueueService.mActiveEntries
List.Add(CType(de.Value, BatchEntryInfo))
Next
End SyncLock
' get all queued entries
msgs = mQueue.GetAllMessages
For Each msg In msgs
entry = CType(formatter.Deserialize(msg.BodyStream), Server.BatchEntry)
entry.Info.SetMessageID(msg.Id)
List.Add(entry.Info)
Next
End Sub
#End Region
#Region " Utility functions "
Private Shared Function QUEUE_NAME() As String
Return ".\private$\" & ConfigurationSettings.AppSettings("QueueName")
End Function
Private Shared Function LISTENER_NAME() As String
Return ConfigurationSettings.AppSettings("ListenerName")
End Function
Private Shared Function PORT() As Integer
Return CInt(ConfigurationSettings.AppSettings("ListenerPort"))
End Function
Private Shared Function MAX_ENTRIES() As Integer
Return CInt(ConfigurationSettings.AppSettings("MaxActiveEntries"))
End Function
#End Region
End Class
End Namespace

View File

@@ -0,0 +1,137 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectType>Local</ProjectType>
<ProductVersion>8.0.50727</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{F8E2709C-E0DE-4253-9A1A-59F4F59B0EBD}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ApplicationIcon>
</ApplicationIcon>
<AssemblyKeyContainerName>
</AssemblyKeyContainerName>
<AssemblyName>CSLA.BatchQueue</AssemblyName>
<AssemblyOriginatorKeyFile>
</AssemblyOriginatorKeyFile>
<AssemblyOriginatorKeyMode>None</AssemblyOriginatorKeyMode>
<DefaultClientScript>JScript</DefaultClientScript>
<DefaultHTMLPageLayout>Grid</DefaultHTMLPageLayout>
<DefaultTargetSchema>IE50</DefaultTargetSchema>
<DelaySign>false</DelaySign>
<OutputType>Library</OutputType>
<OptionCompare>Binary</OptionCompare>
<OptionExplicit>On</OptionExplicit>
<OptionStrict>Off</OptionStrict>
<RootNamespace>CSLA.BatchQueue</RootNamespace>
<StartupObject>
</StartupObject>
<FileUpgradeFlags>
</FileUpgradeFlags>
<MyType>Windows</MyType>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutputPath>bin\</OutputPath>
<DocumentationFile>CSLA.BatchQueue.xml</DocumentationFile>
<BaseAddress>285212672</BaseAddress>
<ConfigurationOverrideFile>
</ConfigurationOverrideFile>
<DefineConstants>
</DefineConstants>
<DefineDebug>true</DefineDebug>
<DefineTrace>true</DefineTrace>
<DebugSymbols>true</DebugSymbols>
<Optimize>false</Optimize>
<RegisterForComInterop>false</RegisterForComInterop>
<RemoveIntegerChecks>false</RemoveIntegerChecks>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningLevel>1</WarningLevel>
<NoWarn>42016,42017,42018,42019,42032</NoWarn>
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<OutputPath>bin\</OutputPath>
<DocumentationFile>CSLA.BatchQueue.xml</DocumentationFile>
<BaseAddress>285212672</BaseAddress>
<ConfigurationOverrideFile>
</ConfigurationOverrideFile>
<DefineConstants>
</DefineConstants>
<DefineDebug>false</DefineDebug>
<DefineTrace>true</DefineTrace>
<DebugSymbols>false</DebugSymbols>
<Optimize>true</Optimize>
<RegisterForComInterop>false</RegisterForComInterop>
<RemoveIntegerChecks>false</RemoveIntegerChecks>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningLevel>1</WarningLevel>
<NoWarn>42016,42017,42018,42019,42032</NoWarn>
<DebugType>none</DebugType>
</PropertyGroup>
<ItemGroup>
<Reference Include="System">
<Name>System</Name>
</Reference>
<Reference Include="System.Data">
<Name>System.Data</Name>
</Reference>
<Reference Include="System.Messaging">
<Name>System.Messaging</Name>
</Reference>
<Reference Include="System.Runtime.Remoting">
<Name>System.Runtime.Remoting</Name>
</Reference>
<Reference Include="System.Xml">
<Name>System.XML</Name>
</Reference>
<ProjectReference Include="..\CSLA\CSLA.vbproj">
<Name>CSLA</Name>
<Project>{1B9A38BB-461A-47A4-AD72-099C694138A0}</Project>
<Package>{F184B08F-C81C-45F6-A57F-5ABD9991F28F}</Package>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Import Include="Microsoft.VisualBasic" />
<Import Include="System" />
<Import Include="System.Collections" />
<Import Include="System.Data" />
<Import Include="System.Diagnostics" />
</ItemGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.vb">
<SubType>Code</SubType>
</Compile>
<Compile Include="BatchEntries.vb">
<SubType>Code</SubType>
</Compile>
<Compile Include="BatchEntry.vb">
<SubType>Code</SubType>
</Compile>
<Compile Include="BatchEntryInfo.vb">
<SubType>Code</SubType>
</Compile>
<Compile Include="BatchJobRequest.vb">
<SubType>Code</SubType>
</Compile>
<Compile Include="BatchQueue.vb">
<SubType>Code</SubType>
</Compile>
<Compile Include="BatchQueueServer.vb">
<SubType>Code</SubType>
</Compile>
<Compile Include="BatchQueueService.vb">
<SubType>Code</SubType>
</Compile>
<Compile Include="IBatchEntry.vb">
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.VisualBasic.targets" />
<PropertyGroup>
<PreBuildEvent>
</PreBuildEvent>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,58 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<LastOpenVersion>7.10.3077</LastOpenVersion>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ReferencePath>
</ReferencePath>
<CopyProjectDestinationFolder>
</CopyProjectDestinationFolder>
<CopyProjectUncPath>
</CopyProjectUncPath>
<CopyProjectOption>0</CopyProjectOption>
<ProjectView>ProjectFiles</ProjectView>
<ProjectTrust>0</ProjectTrust>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<EnableASPDebugging>false</EnableASPDebugging>
<EnableASPXDebugging>false</EnableASPXDebugging>
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
<EnableSQLServerDebugging>false</EnableSQLServerDebugging>
<RemoteDebugEnabled>false</RemoteDebugEnabled>
<RemoteDebugMachine>
</RemoteDebugMachine>
<StartAction>Project</StartAction>
<StartArguments>
</StartArguments>
<StartPage>
</StartPage>
<StartProgram>
</StartProgram>
<StartURL>
</StartURL>
<StartWorkingDirectory>
</StartWorkingDirectory>
<StartWithIE>false</StartWithIE>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<EnableASPDebugging>false</EnableASPDebugging>
<EnableASPXDebugging>false</EnableASPXDebugging>
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
<EnableSQLServerDebugging>false</EnableSQLServerDebugging>
<RemoteDebugEnabled>false</RemoteDebugEnabled>
<RemoteDebugMachine>
</RemoteDebugMachine>
<StartAction>Project</StartAction>
<StartArguments>
</StartArguments>
<StartPage>
</StartPage>
<StartProgram>
</StartProgram>
<StartURL>
</StartURL>
<StartWorkingDirectory>
</StartWorkingDirectory>
<StartWithIE>false</StartWithIE>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,18 @@
''' <summary>
''' Defines the interface that must be implemented by
''' all worker classes.
''' </summary>
''' <remarks>
''' To create a worker that can be executed within the
''' batch queue, implement this interface. The interface
''' will be invoked by the batch queue processor on the
''' server.
''' </remarks>
Public Interface IBatchEntry
''' <summary>
''' This method should contain your worker code that
''' is to be run in the batch queue.
''' </summary>
''' <param name="State">An optional object containing extra state data from the client.</param>
Sub Execute(ByVal State As Object)
End Interface

View File

@@ -0,0 +1 @@
obj\Release\ResolveAssemblyReference.cache