Xafari Server. Message Handler Implementation

Implement Message Handler in separate individual module.

Invoke DevExpress Solution Wizard and add platform-independent module to the solution, name it FileSearcher, set ORM property to XPO. The example uses XPO, but with the same success you can use Domain Comonents. Declare two business classes to store the original Message data and the results.

  • c#
  • VB

[NavigationItem("Xafari Server Demo")]
public class SearchFiles : XPObject
{
  public const string Tag = "SearchFiles";
  public SearchFiles()
  {
  }
  public SearchFiles(Session session)
    : base(session)
  {
  }
  public string Path
  {
    get
    {
      return GetPropertyValue<string>("Path");
    }
    set
    {
      SetDelayedPropertyValue("Path", value);
    }
  }
  public string SearchPattern
  {
    get
    {
      return GetPropertyValue<string>("SearchPattern");
    }
    set
    {
      SetDelayedPropertyValue("SearchPattern", value);
    }
  }
  public long TotalSize
  {
    get
    {
      return GetPropertyValue<long>("TotalSize");
    }
    set
    {
      SetDelayedPropertyValue("TotalSize", value);
    }
  }
  [Association, Aggregated]
  public XPCollection<FileInfo> Files
  {
    get
    {
      return GetCollection<FileInfo>("Files");
    }
  }
}
public class FileInfo : XPObject
{
  public FileInfo(Session session)
    : base(session)
  {
  }
  [Association]
  public SearchFiles Owner
  {
    get
    {
      return GetPropertyValue<SearchFiles>("Owner");
    }
    set
    {
      SetDelayedPropertyValue("Owner", value);
    }
  }
  public string FileName
  {
    get
    {
      return GetPropertyValue<string>("FileName");
    }
    set
    {
      SetDelayedPropertyValue("FileName", value);
    }
  }
  public long Size
  {
    get
    {
      return GetPropertyValue<long>("Size");
    }
    set
    {
      SetDelayedPropertyValue("Size", value);
    }
  }
}

<NavigationItem("Xafari Server Demo")> _
Public Class SearchFiles
  Inherits XPObject
  Public Const Tag As String = "SearchFiles"
  Public Sub New()
  End Sub
  Public Sub New(ByVal session As Session)
    MyBase.New(session)
  End Sub
  Public Property Path As String
    Get
      Return GetPropertyValue(Of String)("Path")
    End Get
    Set
      SetDelayedPropertyValue("Path", value)
    End Set
  End Property
  Public Property SearchPattern As String
    Get
      Return GetPropertyValue(Of String)("SearchPattern")
    End Get
    Set
      SetDelayedPropertyValue("SearchPattern", value)
    End Set
  End Property
  Public Property TotalSize As long
    Get
      Return GetPropertyValue(Of long)("TotalSize")
    End Get
    Set
      SetDelayedPropertyValue("TotalSize", value)
    End Set
  End Property
  <Association, Aggregated> _
Public ReadOnly Property Files As XPCollection(Of FileInfo)
    Get
      Return GetCollection(Of FileInfo)("Files")
    End Get
  End Property
End Class
Public Class FileInfo
  Inherits XPObject
  Public Sub New(ByVal session As Session)
    MyBase.New(session)
  End Sub
  <Association> _
Public Property Owner As SearchFiles
    Get
      Return GetPropertyValue(Of SearchFiles)("Owner")
    End Get
    Set
      SetDelayedPropertyValue("Owner", value)
    End Set
  End Property
  Public Property FileName As String
    Get
      Return GetPropertyValue(Of String)("FileName")
    End Get
    Set
      SetDelayedPropertyValue("FileName", value)
    End Set
  End Property
  Public Property Size As long
    Get
      Return GetPropertyValue(Of long)("Size")
    End Get
    Set
      SetDelayedPropertyValue("Size", value)
    End Set
  End Property
End Class

SearchFiles class stores additional Message data and the results of processing the Message. SearchFiles.Tag constant is the ID of the Messages.

The FileInfo class will store a list of files found.

The next step is to implement Message Handler. Inherit from the abstract base Xafari.Server.MessageHanler class. If you implement the handler in this way it is enough to override an abstract ProcessMessageCore method:

  • c#
  • VB

public class SearcFilesHandler : MessageHandler
{
  public SearcFilesHandler(XafApplication application, object messageKey, string username)
    : base(application, messageKey, username)
  {
  }
  protected override void ProcessMessageCore(IObjectSpace messageObjectSpace, MQMessage message, CancellationToken token)
  {
    using (var objectSpace = Application.CreateObjectSpace())
    {
      var searchFiles = message.MessageData.SafeGetTargetObject<SearchFiles>(objectSpace);
      var directoryInfo = new DirectoryInfo(searchFiles.Path);
      long totalSize = 0;
      foreach (var file in directoryInfo.EnumerateFiles(searchFiles.SearchPattern))
      {
        token.ThrowIfCancellationRequested();
        var fileInfo = objectSpace.CreateObject<DemoApp.Module.BusinessObjects.FileInfo>();
        fileInfo.FileName = file.Name;
        fileInfo.Size = file.Length;
        searchFiles.Files.Add(fileInfo);
        totalSize += file.Length;
      }
      searchFiles.TotalSize = totalSize;
      objectSpace.CommitChanges();
    }
  }
}

Public Class SearcFilesHandler
  Inherits MessageHandler
  Public Sub New(ByVal application As XafApplication, ByVal messageKey As Object, ByVal username As String)
    MyBase.New(application, messageKey, username)
  End Sub
  Protected Overrides Sub ProcessMessageCore(ByVal messageObjectSpace As IObjectSpace, ByVal message As MQMessage, ByVal token As CancellationToken)
    Using objectSpace = Application.CreateObjectSpace()
      Dim searchFiles = message.MessageData.SafeGetTargetObject(Of SearchFiles)(objectSpace)
      Dim directoryInfo = New DirectoryInfo(searchFiles.Path)
      Dim totalSize As long = 0
      For Each file In directoryInfo.EnumerateFiles(searchFiles.SearchPattern)
        token.ThrowIfCancellationRequested()
        Dim fileInfo = objectSpace.CreateObject(Of DemoApp.Module.BusinessObjects.FileInfo)()
        fileInfo.FileName = file.Name
        fileInfo.Size = file.Length
        searchFiles.Files.Add(fileInfo)
        totalSize += file.Length
      Next
      searchFiles.TotalSize = totalSize
      objectSpace.CommitChanges()
    End Using
  End Sub
End Class

Xafari Server choose the raw Message from the Queue and pass to the handler. The handler receives the Message as a parameter of the ProcessMessageCore method. Additional Message data (for considerated Messages it is an SearchFiles instance) are stored in the MQMessage.MessageData. Because it is a field of the XPWeakReferenceStruct type,  then to receive the SearchFiles object, use the Xafari.MQ.SafeGetTargetObject<T> extension method. SearchFiles objects are stored in a database, so specify the Object Space of the application upon receipt of the object from XPWeakReferenceStruct. The Message itself (MQMessage) may or may not be stored in the database. Therefore, the Object Space of the Message is used to work with the Message as parameter of the ProcessMessageCore method.

CancellationToken instance is used to abort Message processing the on the server. It is passed to the ProcessMessageCore method as a parameter. If the Message is cancelled, then the exception will be generated. This exception will be caught in the MessageHandler base class, then the Message will be moved to the "Error" status.

Next, register the Message Handler in the XafApplication.SetupComplete event handler of the FileSearcherModule module class.

  • c#
  • VB

public sealed partial class FileSearcherModule : ModuleBase
{
  public override void Setup(XafApplication application)
  {
    base.Setup(application);
    Application.SetupComplete += Application_SetupComplete;
  }
  void Application_SetupComplete(object sender, EventArgs e)
  {
    XafariServerModule.DataObserver.ServerHandlersStrategy.RegisterHandler(SearchFiles.Tag, (application, messageKey, userName) => new SearcFilesHandler(application, messageKey, userName));
  }
}

Public Partial NotInheritable Class FileSearcherModule
  Inherits ModuleBase
  Public Overrides Sub Setup(ByVal application As XafApplication)
    MyBase.Setup(application)
    Application.SetupComplete += Application_SetupComplete
  End Sub
  Private Sub Application_SetupComplete(ByVal sender As Object, ByVal e As EventArgs)
    XafariServerModule.DataObserver.ServerHandlersStrategy.RegisterHandler(SearchFiles.Tag, Function(ByVal application, ByVal messageKey, ByVal userName)
      New SearcFilesHandler(application, messageKey, userName)
    End Function)
  End Sub
End Class

Specify SearchFiles.Tag as Message ID. The second RegisterHandler is a delegate, that is a factory of the Message Handlers instances.