Creating custom messaging channel
This article describes two ways of creating a custom messaging channel:
- Creating separate text files on the server;
- Writing messages in the database.
In this example, all system messages (messages, notifications on overdue tasks and new assignments, etc.) will be sent through your own message channel. Using the extension point you can send messages to Twitter, ICQ, Jabber, if necessary.
Example of the Data Display
Fig.1 Creating separate text files on the server
Fig. 2 Writing messages to a database
Extension methods (interface)
An extension point (interface) IMessageChannel has the following methods:
/// <summary>
/// Unique Channel Identifier
/// </summary>
Guid Uid { get; }
/// <summary>
/// Channel Name
/// </summary>
string Name { get; }
/// <summary>
/// Display Name
/// </summary>
string DisplayName { get; }
/// <summary>
/// Use a Default Name
/// </summary>
bool Default { get; }
/// <summary>
/// Send a Message
/// </summary>
/// <param name="message">Message</param>
void Send(IMessage message);
Example of the extension point class
Creating separate text files on the server.
[Component]
public class MessageChannel : IMessageChannel
{
private readonly Guid _uid = new Guid("{B2D745F9-9624-40c4-9C07-ABF44281F066}");
public Guid Uid
{
get { return _uid; }
}
public string Name
{
get { return "TextFileChannel"; }
}
public string DisplayName
{
get { return "The Channel writes messages into text files"; }
}
public bool Default
{
get { return true; }
}
private static readonly string Filepath = Locator.GetServiceNotNull<IRuntimeApplication>().Configuration.Config.FilePath;
private static readonly string Fullpath = Path.GetDirectoryName(Filepath);
private static readonly string HeadDir = Path.Combine(Fullpath, "Messages");
public void Send(IMessage message)
{
//Check the message
if (message == null) throw new ArgumentNullException("message");
//Check the recipient
var recipient = message.Recipient as IUser;
if (recipient == null)
{
return;
}
string recipientDir = Path.Combine(headDir, recipient.ToString());
if (!Directory.Exists(headDir))
Directory.CreateDirectory(headDir);
if (!Directory.Exists(recipientDir))
Directory.CreateDirectory(recipientDir);
string path = Path.Combine(recipientDir, string.Format("{0}_{1}-{2}-{3}.{4}.txt",
DateTime.Now.ToShortDateString(),
DateTime.Now.Hour,
DateTime.Now.Minute,
DateTime.Now.Second,
DateTime.Now.Millisecond));
if (!File.Exists(path))
{
using (StreamWriter file1 =
new StreamWriter(path, true))
{
file1.WriteLine("Subject: {0}\r\n Message text: {1}", message.Subject, message.FullMessageText);
}
}
}
}
Writing messages to the database.
[Component]
public class MessageChannelDB : IMessageChannel
{
private readonly Guid _uid = new Guid("{FA1B0A61-B3F6-4f16-A57F-9D6253710D50}");
public Guid Uid
{
get { return _uid; }
}
public string Name
{
get { return "DBChannelMessage"; }
}
public string DisplayName
{
get { return "Database Message Channel"; }
}
public bool Default
{
get { return true; }
}
public void Send(IMessage message)
{
// Check the message
if (message == null) throw new ArgumentNullException("message");
// Check the recipient
var recipient = message.Recipient as IUser;
if (recipient == null)
{
return;
}
var author = message.Author as IUser;
if (author == null)
{
return;
}
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("SUBJECT", message.Subject);
parameters.Add("RECIPIENT", recipient.Id);
parameters.Add("TEXT", message.FullMessageText);
parameters.Add("AUTHOR", author.Id);
FireBirdConnection.SqlQuery("insert into MESSAGES (ID, SUBJECT, RECIPIENT, TEXT, \"DATE\", AUTHOR) values (gen_id(GEN_MESSAGES_ID, 1), @SUBJECT, @RECIPIENT, @TEXT, current_timestamp, @AUTHOR)",
parameters);
}
}
In this example, a Firebird database query is generated. The query adds a new entry to the MESSAGES table. To implement a database connection with the Firebird database and create a database query, the FireBirdConnection.cs class was created.
Code of the FireBirdConnection.cs Class:
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using EleWise.ELMA.Logging;
using EleWise.ELMA.Runtime;
using EleWise.ELMA.Services;
using FirebirdSql.Data.FirebirdClient;
namespace MessageChannel.Connection
{
public static class FireBirdConnection
{
private static readonly string Filepath = Locator.GetServiceNotNull<IRuntimeApplication>().Configuration.Config.FilePath; // The path to the configuration file
private static readonly string HeadDir = Path.GetDirectoryName(Filepath); // Configuration File Directory
private const string DbName = "BASEMESSAGES.FDB"; //Database name
//Generate a connection string
private static readonly string Fbconnection = new FbConnectionStringBuilder
{
DataSource = "127.0.0.1",
UserID = "sysdba",
Password = "masterkey",
Port = 3056,
Dialect = 3,
ServerType = 0,
Database = Path.Combine(HeadDir, DbName),
Charset = "UNICODE_FSS"
}.ToString();
private readonly static FbConnection Fb = new FbConnection(Fbconnection);
public static void SqlQuery(string query, Dictionary<string, object> parameters = null)
{
if (Fb.State == ConnectionState.Closed) // if the connection is closed - open it
Fb.Open();
//Generate a query
using (var fbCommand = new FbCommand(query, Fb))
{
var fbt = Fb.BeginTransaction();
if (parameters != null)
{
foreach (var items in parameters)
{
fbCommand.Parameters.AddWithValue(items.Key, items.Value);
}
}
fbCommand.Transaction = fbt;
try
{
fbCommand.ExecuteNonQuery(); // it is necessary to call this method for queries that do not return a set of data (insert, update, delete)
fbt.Commit(); // if an entry added successfully - commit the transaction
}
catch (Exception exception)
{
Logger.Log.Error(exception.Message);
fbt.Rollback();
}
}
}
}
}
In the example, Firebird database was used, to which the new MESSAGES table was added with the following fields: ID, SUBJECT, RECIPIENT, TEXT, DATE, and AUTHOR. The ID field must have Primary Key and NotNull attributes. You also need to create a generator (in this example the generator has GEN_MESSAGES_ID name).
A script for creating tables in the Firebird database:
CREATE TABLE MESSAGES (
ID BIGINT NOT NULL,
SUBJECT VARCHAR(255),
RECIPIENT INTEGER,
TEXT VARCHAR(255),
"DATE" TIMESTAMP,
AUTHOR INTEGER
);
ALTER TABLE MESSAGES ADD PRIMARY KEY (ID);
The following example shows how to create a message channel for multiple recipients. The messages will be sent to a database in a single transaction.
In this example, all system messages (messages, notifications on overdue tasks and new assignments, etc.) will be sent through your own message channel. If necessary, using the extension point you can send messages to Twitter, ICQ, Jabber.
In this example, the extension point IGroupingMessageChannel was implemented, which has the public void method Send(IMessage message, IEnumerable <EleWise.ELMA.Security.IUser> recipients). This allows you to send messages to all recipients at once and saves your time.
Example of Data Display
Fig.1 Writing messages in a database
Extension Method (interface)
An extension point (interface) IGrouping MessageChannel has the following methods:
/// <summary>
/// The unique channel identifier
/// </summary>
Guid Uid { get; } – You need to define the Uid for your messaging channel
/// <summary>
/// Chanel name
/// </summary>
string Name { get; }
/// <summary>
/// Display name
/// </summary>
string DisplayName { get; }
/// <summary>
/// Use the default name
/// </summary>
bool Default { get; }
/// <summary>
/// Send a message
/// </summary>
/// <param name="message">Message</param>
void Send(IMessage message);
/// <summary>
/// Send a message to multiple recipients
/// </summary>
/// <param name="message"> Message</param>
/// <param name="recipients">List of recipients </param>
void Send(IMessage message, IEnumerable<IUser> recipients);
Example of the extension point class
Messages are written in the database.
[Component]
public class GroupingMessageChannel : IGroupingMessageChannel
{
private readonly Guid _uid = new Guid("{F2E1B073-DADA-4b13-805C-FE2CE3FA9375}");
public Guid Uid
{
get { return _uid; }
}
public string Name
{
get { return "DBGropingMessageChannel"; }
}
public string DisplayName
{
get { return "A channel to send group messages to a DB"; }
}
public bool Default
{
get { return true; }
}
public void Send(IMessage message)
{
var recipient = message.Recipient as IUser;
if (recipient == null)
{
return;
}
Send(message, new[] {recipient});
}
public void Send(IMessage message, IEnumerable<EleWise.ELMA.Security.IUser> recipients)
{
//Check message
if (message == null) throw new ArgumentNullException("message");
var recUsers = recipients as IUser[] ?? recipients.ToArray();
if (recUsers.Length == 0) return;
var author = message.Author as IUser;
if (author == null)
{
return;
}
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("SUBJECT", message.Subject);
parameters.Add("TEXT", message.FullMessageText);
parameters.Add("AUTHOR", author.Id);
string query = string.Empty;
foreach (IUser recipient in recUsers)
{
query +=
string.Format(
"insert into MESSAGES (SUBJECT, RECIPIENT, TEXT, DATE, AUTHOR) values (@SUBJECT, {0}, @TEXT, current_timestamp, @AUTHOR) ",
recipient.Id);
}
MSSQLConnection.SqlQuery(query, parameters);
}
}
Writing messages in the database
In this example, the MSSQL database query is generated. The query adds multiple entries to the MESSAGES table in a single transaction. To implement MSSQL database connection and create a database query the MSSQLConnection.cs class was created.
Code of the MSSQLConnection.cs Class:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using EleWise.ELMA.Logging;
namespace MessageChannel.Connection
{
public static class MssqlConnection
{
//Generate a connection string
private static readonly string MssqlconnectionString = new SqlConnectionStringBuilder
{
DataSource = "(local)",
UserID = "sa",
Password = "p@ssworD",
InitialCatalog = "BASEMESSAGES"
}.ToString();
private static readonly SqlConnection SqlConnection = new SqlConnection(MssqlconnectionString);
public static void SqlQuery(string query, Dictionary<string, object> parameters = null)
{
if (SqlConnection.State == ConnectionState.Closed) // if the connection is closed - open it
SqlConnection.Open();
//Generate a query
using (var sqlCommand = new SqlCommand(query, SqlConnection))
{
var transaction = SqlConnection.BeginTransaction();
if (parameters != null)
{
foreach (var items in parameters)
{
sqlCommand.Parameters.AddWithValue(items.Key, items.Value);
}
}
sqlCommand.Transaction = transaction;
try
{
sqlCommand.ExecuteNonQuery(); // it is necessary to call this method for the queries that do not return a set of data (insert, update, delete)
transaction.Commit(); // if an entry added successfully - commit the transaction
}
catch (Exception exception)
{
Logger.Log.Error(exception.Message);
transaction.Rollback();
}
}
}
}
}
In the example, an MSSQL database was used, which needs to be created. The following is the script for creating a table in the database BASEMESSAGES:
CREATE TABLE [BASEMESSAGES].[dbo].[MESSAGES](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[SUBJECT] [nvarchar](255) NULL,
[RECIPIENT] [bigint] NULL,
[TEXT] [nvarchar](255) NULL,
[AUTHOR] [bigint] NULL,
[DATE] [datetime] NULL,
CONSTRAINT [PK_MESSAGES] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]