Click here to Skip to main content
15,881,173 members
Articles / Programming Languages / XML

DotNetMQ: A Complete Message Queue System for .NET

Rate me:
Please Sign up or sign in to vote.
4.94/5 (190 votes)
23 May 2011LGPL331 min read 1.1M   53.8K   490  
A new and independent Open Source Message Queue system that is entirely built in C# and .NET framework 3.5.
/*
DotNetMQ - A Complete Message Broker For .NET
Copyright (C) 2011 Halil ibrahim KALKAN

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

using System;
using System.Reflection;
using System.Text;

namespace MDS.Client.MDSServices
{
    /// <summary>
    /// Any MDSService class must be inherited from this class.
    /// </summary>
    public abstract class MDSService
    {
        #region Public properties

        /// <summary>
        /// When a method of a MDSService application is invoked, this field stores address of source application in MDS.
        /// </summary>
        public MDSRemoteAppEndPoint RemoteApplication { get; internal set; }

        /// <summary>
        /// When a method of a MDSService application is invoked, this field stores the original message that is sent by MDS server.
        /// </summary>
        public IIncomingMessage IncomingMessage { get; internal set; }

        #endregion

        #region Predefined Public Service Methods

        /// <summary>
        /// This method generates client proxy class to use this service.
        /// It is also a MDSServiceMethod, so, clients can update it's proxy classes via calling this method remotely.
        /// </summary>
        /// <param name="namespaceName">Namespace of generating proxy class</param>
        /// <returns>Proxy class code to use this service</returns>
        [MDSServiceMethod(Description = "This method generates client proxy class to use this service. Clients can update it's proxy classes via calling this method remotely.")]
        [return: MDSServiceMethodParameter("Proxy class code to use this service")]
        public string GenerateProxyClass([MDSServiceMethodParameter("Namespace of generating proxy class")] string namespaceName)
        {
            //Check parameters
            if (string.IsNullOrEmpty(namespaceName))
            {
                namespaceName = "MDSServiceProxies";
            }

            //Get this Type, Methods and Attributes
            var serviceType = GetType();
            var methods = serviceType.GetMethods();
            var attributes = serviceType.GetCustomAttributes(typeof(MDSServiceAttribute), true);

            //Check for MDSService attribute
            if (attributes.Length <= 0)
            {
                return "This class has not MDSService attribute. So, it is not a MDSService.";
            }

            //Get MDSService attribute
            var mdsServiceAttribute = (MDSServiceAttribute)attributes[0];

            //Generate class name
            var proxyClassName = serviceType.Name + "Proxy";

            //Start generating code
            var classBuilder = new StringBuilder();

            //Generate header of file.
            classBuilder.AppendLine("/* This code file is generated by MDSService Proxy Generator tool.");
            classBuilder.AppendLine(" * ");
            classBuilder.AppendLine(" * Service Name    : " + serviceType.Name);
            classBuilder.AppendLine(" * Service version : " + mdsServiceAttribute.Version);
            classBuilder.AppendLine(" * Generating date : " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
            classBuilder.AppendLine(" */");
            classBuilder.AppendLine();

            //Namespaces
            classBuilder.AppendLine("using System;");
            classBuilder.AppendLine("using MDS.Client;");
            classBuilder.AppendLine("using MDS.Client.MDSServices;");
            classBuilder.AppendLine();

            //Class code
            classBuilder.AppendLine("namespace " + namespaceName);
            classBuilder.AppendLine("{");
            classBuilder.AppendLine("    /// <summary>");
            classBuilder.AppendLine("    /// This class is a proxy class to use " + serviceType.Name + " service.");
            if (!string.IsNullOrEmpty(mdsServiceAttribute.Description))
            {
                classBuilder.AppendLine("    /// Service Description: " + mdsServiceAttribute.Description);
            }

            classBuilder.AppendLine("    /// </summary>");
            classBuilder.AppendLine("    public partial class " + proxyClassName + " : MDSServiceProxyBase");
            classBuilder.AppendLine("    {");

            //Constructor
            classBuilder.AppendLine("        #region Constructor");
            classBuilder.AppendLine("        ");
            classBuilder.AppendLine("        /// <summary>");
            classBuilder.AppendLine("        /// Creates a new instance of " + proxyClassName + ".");
            classBuilder.AppendLine("        /// </summary>");
            classBuilder.AppendLine("        /// <param name=\"serviceConsumer\">Reference to a MDSServiceConsumer object to send/receive MDS messages</param>");
            classBuilder.AppendLine("        /// <param name=\"remoteEndPoint\">Remote application end point to send requests</param>");
            classBuilder.AppendLine("        public " + proxyClassName + "(MDSServiceConsumer serviceConsumer, MDSRemoteAppEndPoint remoteEndPoint)");
            classBuilder.AppendLine("            : base(serviceConsumer, remoteEndPoint, \"" + serviceType.Name + "\")");
            classBuilder.AppendLine("        {");
            classBuilder.AppendLine("            ");
            classBuilder.AppendLine("        }");
            classBuilder.AppendLine("        ");
            classBuilder.AppendLine("        #endregion");
            classBuilder.AppendLine("        ");

            //Methods
            classBuilder.AppendLine("        #region " + serviceType.Name + " methods");
            classBuilder.AppendLine("        ");
            foreach (var method in methods)
            {
                if (IsPredefinedMethod(method.Name))
                {
                    continue;
                }

                WriteMethod(classBuilder, method);
            }

            classBuilder.AppendLine("        #endregion");
            classBuilder.AppendLine("        ");
            classBuilder.AppendLine("        #region Default (predefined) service methods");
            classBuilder.AppendLine("        ");
            foreach (var method in methods)
            {
                if (!IsPredefinedMethod(method.Name))
                {
                    continue;
                }

                WriteMethod(classBuilder, method);
            }

            classBuilder.AppendLine("        #endregion");

            //Close class
            classBuilder.AppendLine("    }");

            //Close namespace
            classBuilder.AppendLine("}");

            return classBuilder.ToString();
        }

        /// <summary>
        /// This method can be used to check if service is available.
        /// </summary>
        /// <param name="message">A string message</param>
        /// <returns>Reply to message as formatted: "RE:message".</returns>
        [MDSServiceMethod(Description = "This method can be used to check if service is available.")]
        [return: MDSServiceMethodParameter("Reply to message as formatted: 'RE: message'")]
        public string CheckServiceIsAvailable([MDSServiceMethodParameter("A message to reply")] string message)
        {
            return ("RE: " + message);
        }

        #endregion

        #region Private methods

        private static void WriteMethod(StringBuilder classBuilder, MethodInfo method)
        {
            //Check for MDSServiceMethod attribute
            var methodAttributes = method.GetCustomAttributes(typeof(MDSServiceMethodAttribute), true);
            if (methodAttributes.Length <= 0)
            {
                return;
            }

            //Get MDSServiceMethod attribute
            var serviceMethodAttribute = (MDSServiceMethodAttribute)methodAttributes[0];

            //Get return type
            var returnType = NormalizeType(method.ReturnType.Name);

            //Get parameters
            var parameters = method.GetParameters();

            //Generate proxy method arguments and invoke method parameters
            var methodArgumentsString = new StringBuilder();
            var invokeParameters = new StringBuilder();
            foreach (var parameter in parameters)
            {
                var paramType = NormalizeType(parameter.ParameterType.Name);
                if (methodArgumentsString.Length > 0)
                {
                    methodArgumentsString.Append(", ");
                }

                methodArgumentsString.Append(paramType + " " + parameter.Name);
                invokeParameters.Append(", " + parameter.Name);
            }

            //Generate method summary
            classBuilder.AppendLine("        /// <summary>");
            classBuilder.AppendLine("        /// " + (serviceMethodAttribute.Description ?? "No method summary available."));
            classBuilder.AppendLine("        /// </summary>");

            //Generate XML-Comments for parameters
            foreach (var parameter in parameters)
            {
                var paramAttributes = parameter.GetCustomAttributes(typeof(MDSServiceMethodParameterAttribute), true);
                if (paramAttributes.Length <= 0)
                {
                    continue;
                }

                classBuilder.AppendLine("        /// <param name=\"" + parameter.Name + "\">" + ((MDSServiceMethodParameterAttribute)paramAttributes[0]).Description + "</param>");
            }

            //Generate XML-Comments for return value
            if (returnType != "void")
            {
                var returnAttributes = method.ReturnParameter.GetCustomAttributes(typeof(MDSServiceMethodParameterAttribute), true);
                if (returnAttributes.Length > 0)
                {
                    classBuilder.AppendLine("        /// <returns>" + ((MDSServiceMethodParameterAttribute)returnAttributes[0]).Description + "</returns>");
                }
            }

            //Generate method signature and opening bracket
            classBuilder.AppendLine("        public " + returnType + " " + method.Name + "(" + methodArgumentsString + ")");
            classBuilder.AppendLine("        {");

            //Generate method body according to return value
            if (returnType == "void")
            {
                classBuilder.AppendLine("            InvokeRemoteMethod(\"" + method.Name + "\"" + invokeParameters + ");");
            }
            else
            {
                classBuilder.AppendLine("            return (" + returnType + ") InvokeRemoteMethodAndGetResult(\"" + method.Name + "\"" + invokeParameters + ");");
            }

            //Method closing bracket
            classBuilder.AppendLine("        }");
            classBuilder.AppendLine("        ");
        }

        /// <summary>
        /// Normalizes some known primitive types.
        /// </summary>
        /// <param name="typeName">Type name</param>
        /// <returns>Normalized type name</returns>
        private static string NormalizeType(string typeName)
        {
            switch (typeName)
            {
                case "Void":
                    return "void";
                case "Boolean":
                    return "bool";
                case "Byte":
                    return "byte";
                case "Byte[]":
                    return "byte[]";
                case "Int16":
                    return "short";
                case "Int16[]":
                    return "short[]";
                case "Int32":
                    return "int";
                case "Int32[]":
                    return "int[]";
                case "Int64":
                    return "long";
                case "Int64[]":
                    return "long[]";
                case "String":
                    return "string";
                case "String[]":
                    return "string[]";
                case "Single":
                    return "float";
                case "Single[]":
                    return "float[]";
                case "Double":
                    return "double";
                case "Double[]":
                    return "double[]";
            }

            //Not known type
            return typeName;
        }

        /// <summary>
        /// Checks if a method is predefined method (MDSService methods in MDSService class).
        /// </summary>
        /// <param name="methodName">Method name to check</param>
        /// <returns>True: Yes, it is..</returns>
        private static bool IsPredefinedMethod(string methodName)
        {
            return (methodName == "GenerateProxyClass" || methodName == "CheckServiceIsAvailable");
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Founder Volosoft
Turkey Turkey
I have started programming at 14 years old using Pascal as hobby. Then I interested in web development (HTML, JavaScript, ASP...) before university.

I graduated from Sakarya University Computer Engineering. At university, I learned C++, Visual Basic.NET, C#, ASP.NET and Java. I partly implemented ARP, IP and TCP protocols in Java as my final term project.

Now, I am working on Windows and web based software development mostly using Microsoft technologies in my own company.

My open source projects:

* ABP Framework: https://abp.io
* jTable: http://jtable.org
* Others: https://github.com/hikalkan

My personal web site:

https://halilibrahimkalkan.com

Comments and Discussions