Inherited Message Distributing

Description

  Given a week off of school I found the time to start designing a system that had been thought up in a school class. The idea was simple, a webapp that lets a user reach the widest possible audience by distributing messages across many mediums. One message could be delivered to each recipient as an email, text message, instant message, or phone call to name a few possibilities. With this system, an already overworked teacher could take 30 seconds to send one message that was delivered to each student and parent instantly across multiple mediums. A sports coach could finally reach everyone on the team, without trying to post news on a site that was rarely checked and poorly maintained.
http://www.chalk2me.com/
Difficulty: Intermediate
Time Required: 3-6 hours
Cost: Free
Software: Visual Basic or Visual C# Express Editions
Hardware:
Download: Download
 
Disclaimer: Currently this article is C# only.  If you want it in Visual Basic.Net, please comment and tell us so we can transcode it to VB.

Starting Out:

We thought we had come up with a cool concept, and I knew it wouldn't be that difficult to build. After calling our dev-team into the office I started on it, by myself in my apartment. My goal was to have a single method to send any type of message that would then be picked up by an appropriate thread to deliver the message. As the whole idea here was to create and deliver messages, a logical place to start was a struct to hold our message fields. It's always good to keep other services in mind while you're building your own. I started off with the first 4 fields in any email. This covered message content and we only needed a bit more info to deliver the message. The medium type, along with an identifier field for your address (or screen name etc.), would cover this. Just in case a service we end up using needs a second identifier, I added one more. Check it out:

public struct ctMessage
{
    public ctMessage(string sendToUser, string fromUser, string subj, string msg, string

    mediumType, string mediumArg1, string mediumArg)
    {
        this.toUser = sendToUser;
        this.fromUser = fromUser;
        this.subject = subj;
        this.message = msg;
        this.mediumType = mediumType;
        this.mediumArg1 = mediumArg1;
        this.mediumArg2 = mediumArg2;
    }

    public string toUser; //name of recipient
    public string fromUser; //name of sender
    public string subject;
    public string message;
    public string mediumType; //which node should deliver this
    public string mediumArg1; //varies, to identify the recipient
    public string mediumArg2; //varies, to identify the recipient
}

Once we had a message designed I, of course, wanted to be able to send one! Eventually I wanted a bunch of different types of messages being sent, so a solid base class would save a ton of time later. Although all of the messaging services, or ‘nodes' would undoubtedly send the messages completely different, they would all need to run in a similar thread that sent messages coming from a single location. By doing a good job writing an abstract base class, we could add new services by only overriding the 'send' method later.

This abstract class would have a field identifying the type of messages it will handle, and call an external send method whenever messages of that type existed. A continuous loop to poll for messages, given a modest polling frequency would do the trick. Notice the protected methods and fields that we'll want to override.

public abstract class ctNode
{
    public ctNode()
    {
        this.maxQueueSize = 100; //Set the size according to how long it will take to reach the Nth message
        this.msgQueue = new Queue(maxQueueSize);
        this.msPauseAfterRun = 10000; //If using a DB, polling it with no pause b/w queries is an unnecessary load
        this.runnerThread = new Thread(new ThreadStart(this.run));
    }

    private Thread runnerThread;
    protected int msPauseAfterRun;
    protected Queue msgQueue;
    public int maxQueueSize;

    void run()
    {
        while (true)
        {
            while (this.msgQueue.Count > 0)
            {
                //pop a message and send it
                ctMessage ctm = (ctMessage)msgQueue.Dequeue();

                try
                {
                    sendSingleMessage(ctm);
                }
                catch (Exception e)
                {
                    //Write a more detailed error log here
                    Console.writeLine("the message could not be sent!" + e.toString());
                }
            }

            //get new messages
            ctNode ctn = this;
            ctMessage[] newMessages = messageSender.getMessages(ref ctn, getRoomInQueue());

            //Wait some time before checking for more messages!
            if (newMessages.Length == 0)
                Thread.Sleep(msPauseAfterRun);
            else
                this.EnqueueMessages(newMessages);
        }
    }

    public virtual void EnqueueMessages(ctMessage[] messagesToSend)
    {
        int roomInQueue = maxQueueSize - msgQueue.Count;

        if (messagesToSend.Length > roomInQueue)
            throw new Exception("Too many Messages have been inserted");

        for (int i = 0; i < messagesToSend.Length; i++)
        {
            msgQueue.Enqueue(messagesToSend[i]);
        }
    }

    public virtual void EnqueueMessages(ctMessage messageToSend)
    {
        ctMessage[] ctm = new ctMessage[1];
        ctm[0] = messageToSend;
        this.EnqueueMessages(ctm);
    }

    protected virtual void sendSingleMessage(ctMessage ctm)
    {
        //code to send an individual message
    }

    public void startThread()
    {
        runnerThread.Name = this.nodeType;
        runnerThread.Start();
    }

    protected string nodeType;

    public string getNodeType()
    {
        return this.nodeType;
    }
}

You might have noticed the big thing missing from these classes, a place to store, and retrieve messages! I initially put our message storage into an external static class. This made them centralized, easily accessible from any other objects that might need to access them, simple to ensure thread safety, and gave us room to move to a smarter mechanism later (or database. Notice the simplicity:

public static class messageSender
{
    private static List<ctMessage> messagesToSend = new List<ctMessage>();

    public static ctMessage[] getMessages(ref ctNode nodeRef, int numberOfMessages)
    {
        //We'll do this in a database later, but for now this works similarly
        List<ctMessage> returnMessages = new List<ctMessage>();

        lock (messagesToSend)
            for (int i = 0; i < messagesToSend.Count; i++)
                if (messagesToSend[i].mediumArgs == nodeRef.getNodeType())
                {
                    returnMessages.Add(messagesToSend[i]);
                    messagesToSend.RemoveAt(i);
                    i--;
                }

        return returnMessages.ToArray();
    }

    public static void sendMessage(ctMessage message)
    {
        lock (messagesToSend)
            messagesToSend.Add(message);
    }
}

Now that the groundwork was done, I grabbed some eye drops and an energy drink to refresh for the best part, function! I started simple with an email node, and if you've played with email before in .net, you've definitely seen and loved the SmtpClient class in the System.Net.Mail Namespace. If you don't already have a local SMTP client set up, you can easily use a Gmail account as an SMTP server. Check out the following implementation, notice we only had to set the node type, initialize the SmtpClient and override the send method. Send a message through our message sender, and watch the node pick up the message and deliver it. Inheritance rocks!

ctMessage ctm = new ctMessge("Friend", "Developer", "testing", "My first message", "email", "yourEmail@yourDomain.com", "");
messageSender.sendMessage(ctm);

public class ctNodeEmail : ctNode
{
    public ctNodeEmail() : base()
    {
        this.nodeType = "email";

        //
        //SET UP SMTP CLIENT
        //

        //GMAIL SERVER
        this.mSmtpClient = new SmtpClient("gmail.com/mail", 25);
        this.mSmtpClient.Host = "smtp.gmail.com";
        this.mSmtpClient.Port = 25;
        this.mSmtpClient.EnableSsl = true;
        this.mSmtpClient.Credentials = new System.Net.NetworkCredential("YOURADDRESS@gmail.com", "YOUR_PASSWORD");

        //LOCAL SERVER
        //this.mSmtpClient = new SmtpClient("localhost", 25);
    }

    SmtpClient mSmtpClient;

    protected override void sendSingleMessage(ctMessage ctm)
    {
        MailMessage msgMail = new MailMessage(new MailAddress(ctm.fromUser + "@ChalkTalkNow.com"), new MailAddress(ctm.mediumArgs));
        msgMail.Subject = ctm.subject;
        string msg = ctm.message;
        msgMail.Body = msg;

        // Send the mail message
        mSmtpClient.Send(msgMail);
    }
}

After writing this so quickly I wanted something a bit more than email, and knew text messages were only a step away. It's no secret that all major cell companies give your phone an email address, you just need to append the proper domain to your number. Building off of our email node, we used the first mediumArg to hold a phone number, and put the carrier into the second mediumArg.

public class ctNodeTextMessage : ctNode
{
    public ctNodeTextMessage() : base()
    {
        this.nodeType = "textmessage";

        //
        //SET UP SMTP CLIENT
        //
        this.mSmtpClient = new SmtpClient("gmail.com/mail", 25);
        this.mSmtpClient.Host = "smtp.gmail.com";
        this.mSmtpClient.Port = 25;
        this.mSmtpClient.EnableSsl = true;
        this.mSmtpClient.Credentials = new System.Net.NetworkCredential("GMAIL@gmail.com", "PASSWORD");

    }
    SmtpClient mSmtpClient;

    private string getEmailAddress(string phoneNumber, string provider)
    {
        switch (provider)
        {
            case "t-mobile":
                return phoneNumber + "@tmomail.net";
            case "virginmobile":
                return phoneNumber + "@vmobl.com";
            case "cingular":
                return phoneNumber + "@cingularme.com";
            case "att":
                return phoneNumber + "@cingularme.com";
            case "sprint":
                return phoneNumber + "@messaging.sprintpcs.com";
            case "verizon":
                return phoneNumber + "@vtext.com";
            case "nextel":
                return phoneNumber + "@messaging.nextel.com";
            default:
                //assume cingular/att is the most popular
                return phoneNumber + "@cingularme.com";
        }
    }

    protected override void sendSingleMessage(ctMessage ctm)
    {
        //send a single message
        string emailAddress = getEmailAddress(ctm.mediumArg1, ctm.mediumArg2);

        MailMessage msgMail = new MailMessage(new MailAddress(ctm.fromUser + "@chalktalk.net", ctm.fromUser), new MailAddress(emailAddress));
        msgMail.Subject = ctm.subject;
        msgMail.Body = ctm.message;
        sendEmail(msgMail);
    }

    private void sendEmail(MailMessage msgMail)
    {
        // Send the mail message
        this.mSmtpClient.Send(msgMail);
    }
}

Now to test this out, just like we sent the email we'll send a text. Cool!

ctMessage ctm = new ctMessge("Friend", "Developer", "testing", "hello world via text", "textmessage", "#########", "verizon");
messageSender.sendMessage(ctm);

Conclusion:

When you're working on a project with a small team, it's especially important to build a good foundation like this so you can focus on easily added functionality without getting bogged down in complexity. This portion of the project was written over the course of a week and later on allowed us to focus almost exclusively on adding new services like Instant Messengers and interactive phone calling. Having functionality so quickly was crucial, along with a UI built by my brother, in getting the attention and support of others to build a business around this.

Next Steps:

  • Build a UI to manage content and run your messaging!
  • Rewrite your messageSender class to store messages in a database. This will make your app much more extensible, and you can save a record of the messages sent in an ‘outbox'.
  • Strongly type all textual identifiers in enumerations to avoid any frustrating mistakes.
  • Find an SDK for your favorite messenger, and build a node for it
  • Join our team and continue to build with us!

The Discussion

  • User profile image
    Bob

    Yes a VB.net version would be nice.

  • User profile image
    Clint

    @Bob Thanks, I will get to this then.

  • User profile image
    jalal

    cool can you trancode it to vb language

  • User profile image
    GC

    Hi Robert,

    An alternative would be to have a base abstract "Message" class and a sub-type for each message type, e.g. TextMessage, EmailMessage etc. To create a message you just call new TextMessage(args) and you have your TextMessage object.

    The Message base class defines a method "send()". Each message sub-class should know how to send itself.

    To send your messages all you have to do is go through your queue of Message objects calling "send()" on each.

    I'm curious as to why you chose to sub-class the sender rather than the message, as "send()" seems like something a message should be able to do?

    Cheers,

    GC

  • User profile image
    Rob

    GC,

    You bring up a couple of interesting points, that are not well addressed in this piece.  For the simple examples shown, you could certainly encapsulate sending functionality within the message struct, with perhaps a statically maintained connection.

    When you're designing a web app with a lot done behind the scenes it's often best to decouple the interface and the processing.  For the larger project here this meant an interface needed a message struct solely to hold fields.  A second server, running the nodes might be actually doing the sending.  This design allows you to not expose the node overhead to the UI developers that won't be it anyways.

    The second point to consider is that a node may require a persistent singleton connection that must talk to another server.  In this case it is easier to maintain sending in a separate class/thread that always runs.

    -Rob

  • User profile image
    Oron

    Yes Please give a VB version!!!

  • User profile image
    mgetta

    hey, the idea is cool but im not farmiliar with C# PLS CAN U TRASCODE IN VB

  • User profile image
    Clint

    @rmehta looking into this

  • User profile image
    rmehta

    I could not find the downloable code on codeplex.

  • User profile image
    Clint

    @Oron:  Working on it, just have other things on my plate.

    http://converter.telerik.com/ can do most of the heavy lifting.

  • User profile image
    Ritesh Mehta

    I could not find the downloable code on codeplex.

  • User profile image
    Ritesh Mehta

    Just wanted to followup if you manged to get the code. I could not find the code at the downloadable link

  • User profile image
    Clint

    @rmehta working on getting it back up.

  • User profile image
    Oron

    Hey thanks, I know how it gets!!!

Comments closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to send us feedback you can Contact Us.